aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Gouesse <[email protected]>2022-10-23 23:29:32 +0200
committerJulien Gouesse <[email protected]>2022-10-23 23:29:32 +0200
commit889b24bfc7a60efcd0779b8c2606cbc58f9d5b67 (patch)
tree31e79c94a693870440c6a4be0ccddcca95ceddff
parent37240573f4d95be2c79477fd86e5f6186e2a4dab (diff)
Adds an audio system to Ardor3D, no backend yet
-rw-r--r--ardor3d-audio/build.gradle6
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/Channel.java279
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/CommandObject.java612
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/CommandThread.java185
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/FilenameURL.java162
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/ICodec.java128
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/IStreamListener.java20
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/Library.java1563
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/ListenerData.java291
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/SimpleThread.java209
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/SoundBuffer.java100
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystem.java2893
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemConfig.java1083
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemException.java103
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemLogger.java182
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/Source.java1347
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/StreamThread.java306
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/Vector3D.java219
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/codecs/CodecJOrbis.java823
-rw-r--r--ardor3d-audio/src/main/java/com/ardor3d/audio/sampled/AudioFormat.java623
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jogg/Buffer.java294
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jogg/Packet.java47
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jogg/Page.java135
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jogg/StreamState.java526
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jogg/SyncState.java275
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Block.java128
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/ChainingExample.java69
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/CodeBook.java478
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Comment.java243
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/DecodeExample.java324
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Drft.java1327
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/DspState.java376
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Floor0.java335
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Floor1.java611
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncFloor.java52
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncMapping.java45
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncResidue.java46
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncTime.java45
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Info.java472
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/InfoMode.java34
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/JOrbisException.java40
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Lookup.java152
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Lpc.java188
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Lsp.java107
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Mapping0.java375
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Mdct.java250
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/PsyInfo.java74
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/PsyLook.java42
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue0.java330
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue1.java45
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue2.java41
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/StaticCodeBook.java443
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Time0.java52
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/Util.java30
-rw-r--r--ardor3d-audio/src/main/java/com/jcraft/jorbis/VorbisFile.java1397
-rw-r--r--settings.gradle4
56 files changed, 20565 insertions, 1 deletions
diff --git a/ardor3d-audio/build.gradle b/ardor3d-audio/build.gradle
new file mode 100644
index 0000000..08d57d6
--- /dev/null
+++ b/ardor3d-audio/build.gradle
@@ -0,0 +1,6 @@
+
+description = 'Ardor 3D Sound System'
+dependencies {
+ implementation project(':ardor3d-core')
+ implementation project(':ardor3d-math')
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/Channel.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/Channel.java
new file mode 100644
index 0000000..4906809
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/Channel.java
@@ -0,0 +1,279 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+import java.util.LinkedList;
+import com.ardor3d.audio.sampled.AudioFormat;
+
+/**
+ * The Channel class is the base class which can be extended for
+ * library-specific channels. It is also used in the "no-sound" library.
+ * A channel is a reserved sound-card voice through which sources are played
+ * back. Channels can be either streaming channels or normal (non-streaming)
+ * ones. For consistant naming conventions, each sub-class should have the
+ * name prefix "Channel".
+ *<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 Channel
+{
+/**
+ * The library class associated with this type of channel.
+ */
+ protected Class libraryType = Library.class;
+
+/**
+ * Global identifier for the type of channel (normal or streaming). Possible
+ * values for this varriable can be found in the
+ * {@link com.ardor3d.audio.SoundSystemConfig SoundSystemConfig} class.
+ */
+ public int channelType;
+
+/**
+ * Processes status messages, warnings, and error messages.
+ */
+ private SoundSystemLogger logger;
+
+/**
+ * Whatever source is attached to this channel.
+ */
+ public Source attachedSource = null;
+
+/**
+ * Cumulative counter of the buffers played then unqued.
+ */
+ public int buffersUnqueued = 0;
+
+/**
+ * Constructor: Takes channelType identifier as a paramater. Possible values
+ * for channel type can be found in the
+ * {@link com.ardor3d.audio.SoundSystemConfig SoundSystemConfig} class.
+ * @param type Type of channel (normal or streaming).
+ */
+ public Channel( int type )
+ {
+ // grab a handle to the message logger:
+ logger = SoundSystemConfig.getLogger();
+
+ channelType = type;
+ }
+
+/**
+ * Shuts the channel down and removes references to all instantiated objects.
+ */
+ public void cleanup()
+ {
+ logger = null;
+ }
+
+/**
+ * Queues up the initial byte[] buffers of data to be streamed.
+ * @param bufferList List of the first buffers to be played for a streaming source.
+ * @return False if an error occurred or if end of stream was reached.
+ */
+ public boolean preLoadBuffers( LinkedList<byte[]> bufferList )
+ {
+ return true;
+ }
+
+/**
+ * Queues up a byte[] buffer of data to be streamed.
+ * @param buffer The next buffer to be played for a streaming source.
+ * @return False if an error occurred or if the channel is shutting down.
+ */
+ public boolean queueBuffer( byte[] buffer )
+ {
+ return false;
+ }
+
+/**
+ * Feeds raw data to the stream.
+ * @param buffer Buffer containing raw audio data to stream.
+ * @return Number of prior buffers that have been processed.
+ */
+ public int feedRawAudioData( byte[] buffer )
+ {
+ return 1;
+ }
+
+/**
+ * Returns the number of queued byte[] buffers that have finished playing.
+ * @return Number of buffers processed.
+ */
+ public int buffersProcessed()
+ {
+ return 0;
+ }
+
+/**
+ * Calculates the number of milliseconds since the channel began playing.
+ * @return Milliseconds, or -1 if unable to calculate.
+ */
+ public float millisecondsPlayed()
+ {
+ return -1;
+ }
+/**
+ * Plays the next queued byte[] buffer. This method is run from the seperate
+ * {@link com.ardor3d.audio.StreamThread StreamThread}.
+ * @return False when no more buffers are left to process.
+ */
+ public boolean processBuffer()
+ {
+ return false;
+ }
+
+/**
+ * Sets the channel up to receive the specified audio format.
+ */
+ public void setAudioFormat( AudioFormat audioFormat )
+ {}
+
+/**
+ * Dequeues all previously queued data.
+ */
+ public void flush()
+ {}
+
+/**
+ * Stops the channel, dequeues any queued data, and closes the channel.
+ */
+ public void close()
+ {}
+
+/**
+ * Plays the currently attached normal source, opens this channel up for
+ * streaming, or resumes playback if this channel was paused.
+ */
+ public void play()
+ {}
+
+/**
+ * Temporarily stops playback for this channel.
+ */
+ public void pause()
+ {}
+
+/**
+ * Stops playback for this channel and rewinds the attached source to the
+ * beginning.
+ */
+ public void stop()
+ {}
+
+/**
+ * Rewinds the attached source to the beginning. Stops the source if it was
+ * paused.
+ */
+ public void rewind()
+ {}
+
+/**
+ * Used to determine if a channel is actively playing a source. This method
+ * will return false if the channel is paused or stopped and when no data is
+ * queued to be streamed.
+ * @return True if this channel is playing a source.
+ */
+ public boolean playing()
+ {
+ return false;
+ }
+
+/**
+ * Returns the name of the class.
+ * @return "Channel" + library title.
+ */
+ public String getClassName()
+ {
+ String libTitle = SoundSystemConfig.getLibraryTitle( libraryType );
+
+ if( libTitle.equals( "No Sound" ) )
+ return "Channel";
+ else
+ return "Channel" + libTitle;
+ }
+
+/**
+ * Prints a message.
+ * @param message Message to print.
+ */
+ protected void message( String message )
+ {
+ logger.message( message, 0 );
+ }
+
+/**
+ * Prints an important message.
+ * @param message Message to print.
+ */
+ protected void importantMessage( String message )
+ {
+ logger.importantMessage( message, 0 );
+ }
+
+/**
+ * Prints the specified message if error is true.
+ * @param error True or False.
+ * @param message Message to print if error is true.
+ * @return True if error is true.
+ */
+ protected boolean errorCheck( boolean error, String message )
+ {
+ return logger.errorCheck( error, getClassName(), message, 0 );
+ }
+
+/**
+ * Prints an error message.
+ * @param message Message to print.
+ */
+ protected void errorMessage( String message )
+ {
+ logger.errorMessage( getClassName(), message, 0 );
+ }
+
+/**
+ * Prints an exception's error message followed by the stack trace.
+ * @param e Exception containing the information to print.
+ */
+ protected void printStackTrace( Exception e )
+ {
+ logger.printStackTrace( e, 1 );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/CommandObject.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/CommandObject.java
new file mode 100644
index 0000000..0b3dbed
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/CommandObject.java
@@ -0,0 +1,612 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+/**
+ * The CommandObject class is used to store arguments in the SoundSystem's
+ * Command Queue. Queued CommandObjects are then processed by the
+ * {@link com.ardor3d.audio.CommandThread CommandThread}. Commands are queued
+ * and executed in the background, so it is unlikely that the user will ever
+ * need to use this class.
+ *<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 CommandObject
+{
+/**
+ * Global identifier for the command to initialize the current sound library.
+ */
+ public static final int INITIALIZE = 1;
+/**
+ * Global identifier for the command to pre-load a sound file.
+ */
+ public static final int LOAD_SOUND = 2;
+/**
+ * Global identifier for the command to pre-load a sound file.
+ */
+ public static final int LOAD_DATA = 3;
+/**
+ * Global identifier for the command to remove a sound file from memory.
+ */
+ public static final int UNLOAD_SOUND = 4;
+/**
+ * Global identifier for the command to queue a sound file.
+ */
+ public static final int QUEUE_SOUND = 5;
+/**
+ * Global identifier for the command to dequeue a sound file.
+ */
+ public static final int DEQUEUE_SOUND = 6;
+/**
+ * Global identifier for the command to fade-out transition a source.
+ */
+ public static final int FADE_OUT = 7;
+/**
+ * Global identifier for the command to fade-out/in transition a source.
+ */
+ public static final int FADE_OUT_IN = 8;
+/**
+ * Global identifier for the command to check volume levels of fading sources.
+ */
+ public static final int CHECK_FADE_VOLUMES = 9;
+/**
+ * Global identifier for the command to create a new source.
+ */
+ public static final int NEW_SOURCE = 10;
+/**
+ * Global identifier for the command to create a new raw data stream.
+ */
+ public static final int RAW_DATA_STREAM = 11;
+/**
+ * Global identifier for the command to create a source and immediately play it.
+ */
+ public static final int QUICK_PLAY = 12;
+/**
+ * Global identifier for the command to set a source's position in 3D space.
+ */
+ public static final int SET_POSITION = 13;
+/**
+ * Global identifier for the command to change a source's volume.
+ */
+ public static final int SET_VOLUME = 14;
+/**
+ * Global identifier for the command to change a source's pitch.
+ */
+ public static final int SET_PITCH = 15;
+/**
+ * Global identifier for the command to change a source's priority.
+ */
+ public static final int SET_PRIORITY = 16;
+/**
+ * Global identifier for the command to tell a source whether or not to loop.
+ */
+ public static final int SET_LOOPING = 17;
+/**
+ * Global identifier for the command to set a source's attenuation model.
+ */
+ public static final int SET_ATTENUATION = 18;
+/**
+ * Global identifier for the command to set a source's fade distance or rolloff
+ * factor.
+ */
+ public static final int SET_DIST_OR_ROLL = 19;
+/**
+ * Global identifier for the command to change the Doppler factor.
+ */
+ public static final int CHANGE_DOPPLER_FACTOR = 20;
+/**
+ * Global identifier for the command to change the Doppler velocity.
+ */
+ public static final int CHANGE_DOPPLER_VELOCITY = 21;
+/**
+ * Global identifier for the command to set a source's velocity.
+ */
+ public static final int SET_VELOCITY = 22;
+/**
+ * Global identifier for the command to set a source's velocity.
+ */
+ public static final int SET_LISTENER_VELOCITY = 23;
+/**
+ * Global identifier for the command to play a source.
+ */
+ public static final int PLAY = 24;
+/**
+ * Global identifier for the command to play a source.
+ */
+ public static final int FEED_RAW_AUDIO_DATA = 25;
+/**
+ * Global identifier for the command to pause a source.
+ */
+ public static final int PAUSE = 26;
+/**
+ * Global identifier for the command to stop a source.
+ */
+ public static final int STOP = 27;
+/**
+ * Global identifier for the command to rewind a source.
+ */
+ public static final int REWIND = 28;
+/**
+ * Global identifier for the command to flush all queued data.
+ */
+ public static final int FLUSH = 29;
+/**
+ * Global identifier for the command to cull a source.
+ */
+ public static final int CULL = 30;
+/**
+ * Global identifier for the command to activate a source.
+ */
+ public static final int ACTIVATE = 31;
+/**
+ * Global identifier for the command to set a source as permanant or temporary.
+ */
+ public static final int SET_TEMPORARY = 32;
+/**
+ * Global identifier for the command to delete a source.
+ */
+ public static final int REMOVE_SOURCE = 33;
+/**
+ * Global identifier for the command to move the listner.
+ */
+ public static final int MOVE_LISTENER = 34;
+/**
+ * Global identifier for the command to set the listener's position.
+ */
+ public static final int SET_LISTENER_POSITION = 35;
+/**
+ * Global identifier for the command to turn the listener.
+ */
+ public static final int TURN_LISTENER = 36;
+/**
+ * Global identifier for the command to set the listener's turn angle.
+ */
+ public static final int SET_LISTENER_ANGLE = 37;
+/**
+ * Global identifier for the command to change the listener's orientation.
+ */
+ public static final int SET_LISTENER_ORIENTATION = 38;
+/**
+ * Global identifier for the command to change the master volume.
+ */
+ public static final int SET_MASTER_VOLUME = 39;
+/**
+ * Global identifier for the command to create a new library.
+ */
+ public static final int NEW_LIBRARY = 40;
+
+/**
+ * Any buffer required for a command.
+ */
+ public byte[] buffer;
+/**
+ * Any int arguments required for a command.
+ */
+ public int[] intArgs;
+/**
+ * Any float arguments required for a command.
+ */
+ public float[] floatArgs;
+/**
+ * Any long arguments required for a command.
+ */
+ public long[] longArgs;
+/**
+ * Any boolean arguments required for a command.
+ */
+ public boolean[] boolArgs;
+/**
+ * Any String arguments required for a command.
+ */
+ public String[] stringArgs;
+
+/**
+ * Any Class arguments required for a command.
+ */
+ public Class[] classArgs;
+
+/**
+ * Any Object arguments required for a command.
+ */
+ public Object[] objectArgs;
+
+/**
+ * Which command to execute.
+ */
+ public int Command;
+
+/**
+ * Constructor used to create a command which doesn't require any arguments.
+ * @param cmd Which command to execute.
+ */
+ public CommandObject( int cmd )
+ {
+ Command = cmd;
+ }
+/**
+ * Constructor used to create a command which requires one integer argument.
+ * @param cmd Which command to execute.
+ * @param i The integer argument needed to execute this command.
+ */
+ public CommandObject( int cmd, int i )
+ {
+ Command = cmd;
+ intArgs = new int[1];
+ intArgs[0] = i;
+ }
+/**
+ * Constructor used to create a command which requires one Library Class
+ * argument.
+ * @param cmd Which command to execute.
+ * @param c The Library Class argument needed to execute this command.
+ */
+ public CommandObject( int cmd, Class c )
+ {
+ Command = cmd;
+ classArgs = new Class[1];
+ classArgs[0] = c;
+ }
+/**
+ * Constructor used to create a command which requires one float argument.
+ * @param cmd Which command to execute.
+ * @param f The float argument needed to execute this command.
+ */
+ public CommandObject( int cmd, float f )
+ {
+ Command = cmd;
+ floatArgs = new float[1];
+ floatArgs[0] = f;
+ }
+/**
+ * Constructor used to create a command which requires one String argument.
+ * @param cmd Which command to execute.
+ * @param s The String argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s )
+ {
+ Command = cmd;
+ stringArgs = new String[1];
+ stringArgs[0] = s;
+ }
+/**
+ * Constructor used to create a command which requires one Object argument.
+ * @param cmd Which command to execute.
+ * @param o The Object argument needed to execute this command.
+ */
+ public CommandObject( int cmd, Object o )
+ {
+ Command = cmd;
+ objectArgs = new Object[1];
+ objectArgs[0] = o;
+ }
+/**
+ * Constructor used to create a command which requires one String argument and
+ * one Object argument.
+ * @param cmd Which command to execute.
+ * @param s The String argument needed to execute this command.
+ * @param o The Object argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s, Object o )
+ {
+ Command = cmd;
+ stringArgs = new String[1];
+ stringArgs[0] = s;
+ objectArgs = new Object[1];
+ objectArgs[0] = o;
+ }
+/**
+ * Constructor used to create a command which requires one String argument and
+ * one byte buffer argument.
+ * @param cmd Which command to execute.
+ * @param s The String argument needed to execute this command.
+ * @param buff The byte buffer argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s, byte[] buff )
+ {
+ Command = cmd;
+ stringArgs = new String[1];
+ stringArgs[0] = s;
+ buffer = buff;
+ }
+/**
+ * Constructor used to create a command which requires one String argument, one
+ * Object argument, and one long argument.
+ * @param cmd Which command to execute.
+ * @param s The String argument needed to execute this command.
+ * @param o The Object argument needed to execute this command.
+ * @param l The long argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s, Object o, long l )
+ {
+ Command = cmd;
+ stringArgs = new String[1];
+ stringArgs[0] = s;
+ objectArgs = new Object[1];
+ objectArgs[0] = o;
+ longArgs = new long[1];
+ longArgs[0] = l;
+ }
+/**
+ * Constructor used to create a command which requires one String argument, one
+ * Object argument, and two long arguments.
+ * @param cmd Which command to execute.
+ * @param s The String argument needed to execute this command.
+ * @param o The Object argument needed to execute this command.
+ * @param l1 The first long argument needed to execute this command.
+ * @param l2 The second long argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s, Object o, long l1, long l2 )
+ {
+ Command = cmd;
+ stringArgs = new String[1];
+ stringArgs[0] = s;
+ objectArgs = new Object[1];
+ objectArgs[0] = o;
+ longArgs = new long[2];
+ longArgs[0] = l1;
+ longArgs[1] = l2;
+ }
+/**
+ * Constructor used to create a command which requires two String arguments.
+ * @param cmd Which command to execute.
+ * @param s1 The first String argument needed to execute this command.
+ * @param s2 The second String argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s1, String s2 )
+ {
+ Command = cmd;
+ stringArgs = new String[2];
+ stringArgs[0] = s1;
+ stringArgs[1] = s2;
+ }
+/**
+ * Constructor used to create a command which requires a String and an int as
+ * arguments.
+ * @param cmd Which command to execute.
+ * @param s The String argument needed to execute this command.
+ * @param i The integer argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s, int i )
+ {
+ Command = cmd;
+ intArgs = new int[1];
+ stringArgs = new String[1];
+ intArgs[0] = i;
+ stringArgs[0] = s;
+ }
+/**
+ * Constructor used to create a command which requires a String and a float as
+ * arguments.
+ * @param cmd Which command to execute.
+ * @param s The String argument needed to execute this command.
+ * @param f The float argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s, float f )
+ {
+ Command = cmd;
+ floatArgs = new float[1];
+ stringArgs = new String[1];
+ floatArgs[0] = f;
+ stringArgs[0] = s;
+ }
+/**
+ * Constructor used to create a command which requires a String and a boolean
+ * as arguments.
+ * @param cmd Which command to execute.
+ * @param s The String argument needed to execute this command.
+ * @param b The boolean argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s, boolean b )
+ {
+ Command = cmd;
+ boolArgs = new boolean[1];
+ stringArgs = new String[1];
+ boolArgs[0] = b;
+ stringArgs[0] = s;
+ }
+/**
+ * Constructor used to create a command which requires three float arguments.
+ * @param cmd Which command to execute.
+ * @param f1 The first float argument needed to execute this command.
+ * @param f2 The second float argument needed to execute this command.
+ * @param f3 The third float argument needed to execute this command.
+ */
+ public CommandObject( int cmd, float f1, float f2, float f3 )
+ {
+ Command = cmd;
+ floatArgs = new float[3];
+ floatArgs[0] = f1;
+ floatArgs[1] = f2;
+ floatArgs[2] = f3;
+ }
+/**
+ * Constructor used to create a command which a String and three float
+ * arguments.
+ * @param cmd Which command to execute.
+ * @param s The String argument needed to execute this command.
+ * @param f1 The first float argument needed to execute this command.
+ * @param f2 The second float argument needed to execute this command.
+ * @param f3 The third float argument needed to execute this command.
+ */
+ public CommandObject( int cmd, String s, float f1, float f2, float f3 )
+ {
+ Command = cmd;
+ floatArgs = new float[3];
+ stringArgs = new String[1];
+ floatArgs[0] = f1;
+ floatArgs[1] = f2;
+ floatArgs[2] = f3;
+ stringArgs[0] = s;
+ }
+/**
+ * Constructor used to create a command which requires six float arguments.
+ * @param cmd Which command to execute.
+ * @param f1 The first float argument needed to execute this command.
+ * @param f2 The second float argument needed to execute this command.
+ * @param f3 The third float argument needed to execute this command.
+ * @param f4 The fourth float argument needed to execute this command.
+ * @param f5 The fifth float argument needed to execute this command.
+ * @param f6 The sixth float argument needed to execute this command.
+ */
+ public CommandObject( int cmd, float f1, float f2, float f3, float f4,
+ float f5, float f6 )
+ {
+ Command = cmd;
+ floatArgs = new float[6];
+ floatArgs[0] = f1;
+ floatArgs[1] = f2;
+ floatArgs[2] = f3;
+ floatArgs[3] = f4;
+ floatArgs[4] = f5;
+ floatArgs[5] = f6;
+ }
+/**
+ * Constructor used to create a command which requires several arguments.
+ * @param cmd Which command to execute.
+ * @param b1 The first boolean argument needed to execute this command.
+ * @param b2 The second boolean argument needed to execute this command.
+ * @param b3 The third boolean argument needed to execute this command.
+ * @param s The String argument needed to execute this command.
+ * @param o The Object argument needed to execute this command.
+ * @param f1 The first float argument needed to execute this command.
+ * @param f2 The second float argument needed to execute this command.
+ * @param f3 The third float argument needed to execute this command.
+ * @param i The integer argument needed to execute this command.
+ * @param f4 The fourth float argument needed to execute this command.
+ */
+ public CommandObject( int cmd,
+ boolean b1, boolean b2, boolean b3,
+ String s, Object o,
+ float f1, float f2, float f3,
+ int i, float f4 )
+ {
+ Command = cmd;
+ intArgs = new int[1];
+ floatArgs = new float[4];
+ boolArgs = new boolean[3];
+ stringArgs = new String[1];
+ objectArgs = new Object[1];
+ intArgs[0] = i;
+ floatArgs[0] = f1;
+ floatArgs[1] = f2;
+ floatArgs[2] = f3;
+ floatArgs[3] = f4;
+ boolArgs[0] = b1;
+ boolArgs[1] = b2;
+ boolArgs[2] = b3;
+ stringArgs[0] = s;
+ objectArgs[0] = o;
+ }
+/**
+ * Constructor used to create a command which requires several arguments.
+ * @param cmd Which command to execute.
+ * @param b1 The first boolean argument needed to execute this command.
+ * @param b2 The second boolean argument needed to execute this command.
+ * @param b3 The third boolean argument needed to execute this command.
+ * @param s The String argument needed to execute this command.
+ * @param o The Object argument needed to execute this command.
+ * @param f1 The first float argument needed to execute this command.
+ * @param f2 The second float argument needed to execute this command.
+ * @param f3 The third float argument needed to execute this command.
+ * @param i The integer argument needed to execute this command.
+ * @param f4 The fourth float argument needed to execute this command.
+ * @param b4 The fourth boolean argument needed to execute this command.
+ */
+ public CommandObject( int cmd,
+ boolean b1, boolean b2, boolean b3,
+ String s,
+ Object o,
+ float f1, float f2, float f3,
+ int i, float f4, boolean b4 )
+ {
+ Command = cmd;
+ intArgs = new int[1];
+ floatArgs = new float[4];
+ boolArgs = new boolean[4];
+ stringArgs = new String[1];
+ objectArgs = new Object[1];
+ intArgs[0] = i;
+ floatArgs[0] = f1;
+ floatArgs[1] = f2;
+ floatArgs[2] = f3;
+ floatArgs[3] = f4;
+ boolArgs[0] = b1;
+ boolArgs[1] = b2;
+ boolArgs[2] = b3;
+ boolArgs[3] = b4;
+ stringArgs[0] = s;
+ objectArgs[0] = o;
+ }
+/**
+ * Constructor used to create a command which requires several arguments.
+ * @param cmd Which command to execute.
+ * @param o The Object argument needed to execute this command.
+ * @param b The first boolean argument needed to execute this command.
+ * @param s The String argument needed to execute this command.
+ * @param f1 The first float argument needed to execute this command.
+ * @param f2 The second float argument needed to execute this command.
+ * @param f3 The third float argument needed to execute this command.
+ * @param i The integer argument needed to execute this command.
+ * @param f4 The fourth float argument needed to execute this command.
+ */
+ public CommandObject( int cmd,
+ Object o,
+ boolean b,
+ String s,
+ float f1, float f2, float f3,
+ int i,
+ float f4 )
+ {
+ Command = cmd;
+ intArgs = new int[1];
+ floatArgs = new float[4];
+ boolArgs = new boolean[1];
+ stringArgs = new String[1];
+ objectArgs = new Object[1];
+ intArgs[0] = i;
+ floatArgs[0] = f1;
+ floatArgs[1] = f2;
+ floatArgs[2] = f3;
+ floatArgs[3] = f4;
+ boolArgs[0] = b;
+ stringArgs[0] = s;
+ objectArgs[0] = o;
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/CommandThread.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/CommandThread.java
new file mode 100644
index 0000000..11ae506
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/CommandThread.java
@@ -0,0 +1,185 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+/**
+ * The CommandThread class is designed to move all command processing into a
+ * single thread to be run in the background and avoid conflicts between
+ * threads. Commands are processed in the order that they were queued. The
+ * arguements for each command are stored in a
+ * {@link com.ardor3d.audio.CommandObject CommandObject}. The Command Queue is
+ * located in the {@link com.ardor3d.audio.SoundSystem SoundSystem} class.
+ * Calling kill() stops the thread, and this should be immediatly followed
+ * by a call to interrupt() to wake up the thread so it may end. This class
+ * also checks for temporary sources that are finished playing, and removes
+ * them.
+ *
+ * NOTE: The command thread is created automatically by the sound system, so it
+ * is unlikely that the user would ever need to use this class.
+ *<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 CommandThread extends SimpleThread
+{
+/**
+ * Processes status messages, warnings, and error messages.
+ */
+ protected SoundSystemLogger logger;
+
+/**
+ * Handle to the Sound System. This is where the Command Queue is located.
+ */
+ private SoundSystem soundSystem;
+
+/**
+ * Name of this class.
+ */
+ protected String className = "CommandThread";
+
+/**
+ * Constructor: Takes a handle to the SoundSystem object as a parameter.
+ * @param s Handle to the SoundSystem.
+*/
+ public CommandThread( SoundSystem s )
+ {
+ // grab a handle to the message logger:
+ logger = SoundSystemConfig.getLogger();
+
+ soundSystem = s;
+ }
+
+/**
+ * Shuts the thread down and removes references to all instantiated objects.
+ * NOTE: Method alive() will return false when cleanup() has finished.
+ */
+ @Override
+ protected void cleanup()
+ {
+ kill();
+
+ logger = null;
+ soundSystem = null;
+
+ super.cleanup(); // Important!
+ }
+
+/**
+ * The main loop for processing commands. The Command Thread starts out
+ * asleep, and it sleeps again after it finishes processing commands, so it
+ * must be interrupted when commands are queued for processing.
+ */
+ @Override
+ public void run()
+ {
+ long previousTime = System.currentTimeMillis();
+ long currentTime = previousTime;
+
+ if( soundSystem == null )
+ {
+ errorMessage( "SoundSystem was null in method run().", 0 );
+ cleanup();
+ return;
+ }
+
+ // Start out asleep:
+ snooze( 3600000 );
+
+ while( !dying() )
+ {
+ // Perform user-specific source management:
+ soundSystem.ManageSources();
+
+ // Process all queued commands:
+ soundSystem.CommandQueue( null );
+
+ // Remove temporary sources every ten seconds:
+ currentTime = System.currentTimeMillis();
+ if( (!dying()) && ((currentTime - previousTime) > 10000) )
+ {
+ previousTime = currentTime;
+ soundSystem.removeTemporarySources();
+ }
+
+ // Wait for more commands:
+ if( !dying() )
+ snooze( 3600000 );
+ }
+
+ cleanup(); // Important!
+ }
+
+/**
+ * Prints a message.
+ * @param message Message to print.
+ */
+ protected void message( String message, int indent )
+ {
+ logger.message( message, indent );
+ }
+
+/**
+ * Prints an important message.
+ * @param message Message to print.
+ */
+ 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.
+ * @return True if error is true.
+ */
+ protected boolean errorCheck( boolean error, String message )
+ {
+ return logger.errorCheck( error, className, message, 0 );
+ }
+
+/**
+ * Prints an error message.
+ * @param message Message to print.
+ */
+ protected void errorMessage( String message, int indent )
+ {
+ logger.errorMessage( className, message, indent );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/FilenameURL.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/FilenameURL.java
new file mode 100644
index 0000000..eab3749
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/FilenameURL.java
@@ -0,0 +1,162 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+import java.net.URL;
+
+/**
+ * The FilenameURL class is designed to associate a String filename/identifier
+ * with a URL. Handles either case where user supplies a String path or user
+ * supplies a URL instance.
+ *<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 FilenameURL
+{
+/**
+ * Processes status messages, warnings, and error messages.
+ */
+ private SoundSystemLogger logger;
+
+/**
+ * Filename or identifier for the file.
+ */
+ private String filename = null;
+
+/**
+ * URL interface to the file.
+ */
+ private URL url = null;
+
+/**
+ * Constructor: Saves handles to the url and identifier. The identifier should
+ * look like a filename, and it must have the correct extension so SoundSystem
+ * knows what format to use for the file referenced by the URL instance.
+ * @param url URL interface to a file.
+ * @param identifier Identifier (filename) for the file.
+ */
+ public FilenameURL( URL url, String identifier )
+ {
+ // grab a handle to the message logger:
+ logger = SoundSystemConfig.getLogger();
+
+ filename = identifier;
+ this.url = url;
+ }
+
+/**
+ * Constructor: Saves a handle to the filename (used later to generate a URL
+ * instance). 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 names.
+ * @param filename Name of the file.
+ */
+ public FilenameURL( String filename )
+ {
+ // grab a handle to the message logger:
+ logger = SoundSystemConfig.getLogger();
+
+ this.filename = filename;
+ url = null;
+ }
+
+/**
+ * Returns the filename/identifier.
+ * @return Filename or identifier for the file.
+ */
+ public String getFilename()
+ {
+ return filename;
+ }
+
+/**
+ * Returns the URL interface to the file. If a URL was not originally specified
+ * in the constructor, then the first time this method is called it creates a
+ * URL instance using the previously specified filename.
+ * @return URL interface to the file.
+ */
+ public URL getURL()
+ {
+ if( url == null )
+ {
+ // Check if the file is online or inside the JAR:
+ if( filename.matches( SoundSystemConfig.PREFIX_URL ) )
+ {
+ // Online
+ try
+ {
+ url = new URL( filename );
+ }
+ catch( Exception e )
+ {
+ errorMessage( "Unable to access online URL in " +
+ "method 'getURL'" );
+ printStackTrace( e );
+ return null;
+ }
+ }
+ else
+ {
+ // Inside the JAR
+ url = getClass().getClassLoader().getResource(
+ SoundSystemConfig.getSoundFilesPackage() + filename );
+ }
+ }
+ return url;
+ }
+
+/**
+ * Prints an error message.
+ * @param message Message to print.
+ */
+ private void errorMessage( String message )
+ {
+ logger.errorMessage( "MidiChannel", message, 0 );
+ }
+
+/**
+ * Prints an exception's error message followed by the stack trace.
+ * @param e Exception containing the information to print.
+ */
+ private void printStackTrace( Exception e )
+ {
+ logger.printStackTrace( e, 1 );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/ICodec.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/ICodec.java
new file mode 100644
index 0000000..bddd958
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/ICodec.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+import java.net.URL;
+import com.ardor3d.audio.sampled.AudioFormat;
+
+/**
+ * The ICodec interface provides a common interface for SoundSystem to use
+ * for accessing external codec libraries.
+ *<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 interface ICodec
+{
+/**
+ * Should tell derived classes when they may need to reverse the byte order of
+ * the data before returning it in the read() and readAll() methods. The
+ * reason for the reversBytOrder method is because some external codec
+ * libraries produce audio data in a format that some external audio libraries
+ * require to be reversed. Derivatives of the Library and Source classes for
+ * audio libraries which require this type of data to be reversed should call
+ * the reverseByteOrder() method for all instances of ICodec that they use.
+ * Derivatives of the ICodec interface for codec libraries which which produce
+ * this type of data should use the reverseByteOrder() method to know when the
+ * data needs to be reversed before returning it in the read() and readAll()
+ * methods. If a particular codec library does not produce this type of data,
+ * its derived ICodec class may disregard any calls to the reverseByteOrder()
+ * method.
+ * @param b True if the calling audio library requires byte-reversal by some codec libraries.
+ */
+ public void reverseByteOrder( boolean b );
+
+/**
+ * Should make any preperations required before reading from the audio stream.
+ * If another stream is already opened, it should be closed and a new audio
+ * stream opened in its place. This method is used internally by SoundSystem
+ * not only to initialize a stream, but also to rewind streams and to switch
+ * stream sources on the fly.
+ * @return False if an error occurred or if end of stream was reached.
+ */
+ public boolean initialize( URL url );
+
+/**
+ * Should return false if the stream is busy initializing. To prevent bad
+ * data from being returned by this method, derived classes should internally
+ * synchronize with any elements used by both the initialized() and initialize()
+ * methods.
+ * @return True if steam is initialized.
+ */
+ public boolean initialized();
+
+/**
+ * Should read in one stream buffer worth of audio data. See
+ * {@link com.ardor3d.audio.SoundSystemConfig SoundSystemConfig} for more
+ * information about accessing and changing default settings.
+ * @return The audio data wrapped into a SoundBuffer context.
+ */
+ public SoundBuffer read();
+
+/**
+ * Should read in all the audio data from the stream (up to the default
+ * "maximum file size". See
+ * {@link com.ardor3d.audio.SoundSystemConfig SoundSystemConfig} for more
+ * information about accessing and changing default settings.
+ * @return the audio data wrapped into a SoundBuffer context.
+ */
+ public SoundBuffer readAll();
+
+/**
+ * Should return false if there is still more data available to be read in. To
+ * prevent bad data from being returned by this method, derived classes should
+ * internally synchronize with any elements used in both the endOfStream() and
+ * the read() or readAll() methods.
+ * @return True if end of stream was reached.
+ */
+ public boolean endOfStream();
+
+/**
+ * Should close any open streams and remove references to all instantiated
+ * objects.
+ */
+ public void cleanup();
+
+/**
+ * Should return the audio format of the data being returned by the read() and
+ * readAll() methods.
+ * @return Information wrapped into an AudioFormat context.
+ */
+ public AudioFormat getAudioFormat();
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/IStreamListener.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/IStreamListener.java
new file mode 100644
index 0000000..418fe59
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/IStreamListener.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+public interface IStreamListener
+{
+ /**
+ * Notifies implementation that an End Of Stream was reached.
+ * @param sourcename String identifier of the source which reached the EOS.
+ * @param queueSize Number of items left the the stream's play queue, or zero if none.
+ */
+ public void endOfStream( String sourcename, int queueSize );
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/Library.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/Library.java
new file mode 100644
index 0000000..8abd6b6
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/Library.java
@@ -0,0 +1,1563 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import com.ardor3d.audio.sampled.AudioFormat;
+
+/**
+ * The Library class is the class from which all library types are extended.
+ * It provides generic methods for interfacing with the audio libraries
+ * supported by the SoundSystem. Specific libraries should extend this class
+ * and override the necessary methods. For consistant naming conventions, each
+ * sub-class should have the name prefix "Library".
+ *
+ * This class may also be used as the "No Sound Library" (i.e. silent mode) if
+ * no other audio libraries are supported by the host machine, or to mute all
+ * sound.
+ *<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>
+ *
+ * Julien Gouesse: I removed Midi support in order to stop depending on javax.sound more easily
+ */
+public class Library
+{
+/**
+ * Processes status messages, warnings, and error messages.
+ */
+ private SoundSystemLogger logger;
+
+/**
+ * Position and orientation of the listener.
+ */
+ protected ListenerData listener;
+
+/**
+ * Map containing sound file data for easy lookup by filename / identifier.
+ */
+ protected HashMap<String, SoundBuffer> bufferMap = null;
+
+/**
+ * Map containing all created sources for easy look-up by name.
+ */
+ protected HashMap<String, Source> sourceMap; // (name, source data) pairs
+
+/**
+ * Array containing maximum number of non-streaming audio channels.
+ */
+ protected List<Channel> streamingChannels;
+
+/**
+ * Array containing maximum number of non-streaming audio channels.
+ */
+ protected List<Channel> normalChannels;
+
+/**
+ * Source name last played on each streaming channel.
+ */
+ private String[] streamingChannelSourceNames;
+
+/**
+ * Source name last played on each non-streaming channel.
+ */
+ private String[] normalChannelSourceNames;
+
+/**
+ * Increments through the steaming channel list as new sources are played.
+ */
+ private int nextStreamingChannel = 0;
+
+/**
+ * Increments through the non-steaming channel list as new sources are played.
+ */
+ private int nextNormalChannel = 0;
+
+/**
+ * Handles processing for all streaming sources.
+ */
+ protected StreamThread streamThread;
+
+/**
+ * Whether or not the library requires reversal of audio data byte order.
+ */
+ protected boolean reverseByteOrder = false;
+
+/**
+ * Constructor: Instantiates the source map and listener information. NOTES:
+ * The 'super()' method should be at the top of constructors for all extended
+ * classes. The varriable 'libraryType' should be given a new value in the
+ * constructors for all extended classes.
+ */
+ public Library() throws SoundSystemException
+ {
+ // grab a handle to the message logger:
+ logger = SoundSystemConfig.getLogger();
+
+ // instantiate the buffer map:
+ bufferMap = new HashMap<String, SoundBuffer>();
+
+ // instantiate the source map:
+ sourceMap = new HashMap<String, Source>();
+
+ listener = new ListenerData( 0.0f, 0.0f, 0.0f, // position
+ 0.0f, 0.0f, -1.0f, // look-at direction
+ 0.0f, 1.0f, 0.0f, // up direction
+ 0.0f ); // angle
+
+ streamingChannels = new LinkedList<Channel>();
+ normalChannels = new LinkedList<Channel>();
+ streamingChannelSourceNames = new String[
+ SoundSystemConfig.getNumberStreamingChannels() ];
+ normalChannelSourceNames = new String[
+ SoundSystemConfig.getNumberNormalChannels() ];
+
+ streamThread = new StreamThread();
+ streamThread.start();
+ }
+
+
+/* ########################################################################## */
+/* BEGIN OVERRIDE METHODS */
+/* */
+/* The following methods should be overrided as required */
+/* ########################################################################## */
+
+/**
+ * Stops all sources, shuts down sound library, and removes references to all
+ * instantiated objects.
+ */
+ public void cleanup()
+ {
+ streamThread.kill();
+ streamThread.interrupt();
+
+ // wait up to 5 seconds for stream thread to end:
+ for( int i = 0; i < 50; i++ )
+ {
+ if( !streamThread.alive() )
+ break;
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch(Exception e)
+ {}
+ }
+
+ if( streamThread.alive() )
+ {
+ errorMessage( "Stream thread did not die!" );
+ message( "Ignoring errors... continuing clean-up." );
+ }
+
+
+
+ Channel channel = null;
+ if( streamingChannels != null )
+ {
+ while( !streamingChannels.isEmpty() )
+ {
+ channel = streamingChannels.remove(0);
+ channel.close();
+ channel.cleanup();
+ channel = null;
+ }
+ streamingChannels.clear();
+ streamingChannels = null;
+ }
+ if( normalChannels != null )
+ {
+ while( !normalChannels.isEmpty() )
+ {
+ channel = normalChannels.remove(0);
+ channel.close();
+ channel.cleanup();
+ channel = null;
+ }
+ normalChannels.clear();
+ normalChannels = null;
+ }
+
+ Set<String> keys = sourceMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ String sourcename;
+ Source source;
+
+ // loop through and cleanup all the sources:
+ while( iter.hasNext() )
+ {
+ sourcename = iter.next();
+ source = sourceMap.get( sourcename );
+ if( source != null )
+ source.cleanup();
+ }
+ sourceMap.clear();
+ sourceMap = null;
+
+ listener = null;
+ streamThread = null;
+ }
+
+/**
+ * Initializes the sound library.
+ */
+ public void init() throws SoundSystemException
+ {
+ Channel channel = null;
+
+ // create the streaming channels:
+ for( int x = 0; x < SoundSystemConfig.getNumberStreamingChannels(); x++ )
+ {
+ channel = createChannel( SoundSystemConfig.TYPE_STREAMING );
+ if( channel == null )
+ break;
+ streamingChannels.add( channel );
+ }
+ // create the non-streaming channels:
+ for( int x = 0; x < SoundSystemConfig.getNumberNormalChannels(); x++ )
+ {
+ channel = createChannel( SoundSystemConfig.TYPE_NORMAL );
+ if( channel == null )
+ break;
+ normalChannels.add( channel );
+ }
+ }
+
+/**
+ * Checks if the no-sound library type is compatible.
+ * @return True or false.
+ */
+ public static boolean libraryCompatible()
+ {
+ return true; // the no-sound library is always compatible.
+ }
+
+/**
+ * Creates a new channel of the specified type (normal or streaming). Possible
+ * values for channel type can be found in the
+ * {@link com.ardor3d.audio.SoundSystemConfig SoundSystemConfig} class.
+ * @param type Type of channel.
+ * @return The new channel.
+ */
+ protected Channel createChannel( int type )
+ {
+ return new Channel( type );
+ }
+
+/**
+ * Pre-loads a sound into memory.
+ * @param filenameURL Filename/URL of the sound file to load.
+ * @return True if the sound loaded properly.
+ */
+ public boolean loadSound( FilenameURL filenameURL )
+ {
+ return true;
+ }
+
+/**
+ * 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.
+ * @param buffer the sample data and audio format to save.
+ * @param identifier What to call the sample.
+ * @return True if there weren't any problems.
+ */
+ public boolean loadSound( SoundBuffer buffer, String identifier )
+ {
+ return true;
+ }
+
+/**
+ * Returns the filenames of all previously loaded sounds.
+ * @return LinkedList of String filenames.
+ */
+ public LinkedList<String> getAllLoadedFilenames()
+ {
+ LinkedList<String> filenames = new LinkedList<String>();
+ Set<String> keys = bufferMap.keySet();
+ Iterator<String> iter = keys.iterator();
+
+ // loop through and update the volume of all sources:
+ while( iter.hasNext() )
+ {
+ filenames.add( iter.next() );
+ }
+
+ return filenames;
+ }
+
+/**
+ * Returns the sourcenames of all sources.
+ * @return LinkedList of String sourcenames.
+ */
+ public LinkedList<String> getAllSourcenames()
+ {
+ LinkedList<String> sourcenames = new LinkedList<String>();
+ Set<String> keys = sourceMap.keySet();
+ Iterator<String> iter = keys.iterator();
+
+ // loop through and update the volume of all sources:
+ while( iter.hasNext() )
+ {
+ sourcenames.add( iter.next() );
+ }
+
+ return sourcenames;
+ }
+
+/**
+ * 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 this method has been called, for as long
+ * as the sound is attached to an existing source.
+ * @param filename Filename/identifier of the sound file to unload.
+ */
+ public void unloadSound( String filename )
+ {
+ bufferMap.remove( filename );
+ }
+
+/**
+ * Opens a direct line for streaming audio data.
+ * @param audioFormat Format that the data will be in.
+ * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.
+ * @param posX X position for this source.
+ * @param posY Y position for this source.
+ * @param posZ 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 posX, float posY,
+ float posZ, int attModel, float distOrRoll )
+ {
+ sourceMap.put( sourcename,
+ new Source( audioFormat, priority, sourcename, posX,
+ posY, posZ, attModel, distOrRoll ) );
+ }
+
+/**
+ * Creates a new source using the specified information.
+ * @param priority Setting this to true will prevent other sounds from overriding this one.
+ * @param toStream Setting this to true will load the sound in pieces rather than all at once.
+ * @param toLoop Should this source loop, or play only once.
+ * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.
+ * @param filenameURL Filename/URL of the sound file to play at this source.
+ * @param posX X position for this source.
+ * @param posY Y position for this source.
+ * @param posZ 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, boolean toStream, boolean toLoop,
+ String sourcename, FilenameURL filenameURL,
+ float posX, float posY, float posZ, int attModel,
+ float distOrRoll )
+ {
+ sourceMap.put( sourcename,
+ new Source( priority, toStream, toLoop, sourcename,
+ filenameURL, null, posX, posY, posZ,
+ attModel, distOrRoll, false ) );
+ }
+
+/**
+ * Creates and immediately plays a new source that will be removed when it
+ * finishes playing.
+ * @param priority Setting this to true will prevent other sounds from overriding this one.
+ * @param toStream Setting this to true will load the sound in pieces rather than all at once.
+ * @param toLoop Should this source loop, or play only once.
+ * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.
+ * @param filenameURL The filename/URL of the sound file to play at this source.
+ * @param posX X position for this source.
+ * @param posY Y position for this source.
+ * @param posZ 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 quickPlay( boolean priority, boolean toStream, boolean toLoop,
+ String sourcename, FilenameURL filenameURL,
+ float posX, float posY, float posZ, int attModel,
+ float distOrRoll, boolean tmp )
+ {
+ sourceMap.put( sourcename,
+ new Source( priority, toStream, toLoop, sourcename,
+ filenameURL, null, posX, posY, posZ,
+ attModel, distOrRoll, tmp ) );
+ }
+
+/**
+ *
+ * Defines whether or not the source should be removed after it finishes
+ * playing.
+ * @param sourcename The source's name.
+ * @param temporary True or False.
+ */
+ public void setTemporary( String sourcename, boolean temporary )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.setTemporary( temporary );
+ }
+
+/**
+ * Changes the specified source's position.
+ * @param sourcename The source's name.
+ * @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 )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.setPosition( x, y, z );
+ }
+
+/**
+ * Sets the specified source's priority factor. A priority source will not be
+ * overriden if there are too many sources playing at once.
+ * @param sourcename The source's name.
+ * @param pri True or False.
+ */
+ public void setPriority( String sourcename, boolean pri )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.setPriority( pri );
+ }
+
+/**
+ * Sets the specified source's looping parameter. If parameter lp is false,
+ * the source will play once and stop.
+ * @param sourcename The source's name.
+ * @param lp True or False.
+ */
+ public void setLooping( String sourcename, boolean lp )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.setLooping( lp );
+ }
+
+/**
+ * Sets the specified source's attenuation model.
+ * @param sourcename The source's name.
+ * @param model Attenuation model to use.
+ */
+ public void setAttenuation( String sourcename, int model )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.setAttenuation( model );
+ }
+
+/**
+ * Sets the specified source's fade distance or rolloff factor.
+ * @param sourcename The source's name.
+ * @param dr Fade distance or rolloff factor.
+ */
+ public void setDistOrRoll( String sourcename, float dr)
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.setDistOrRoll( dr );
+ }
+
+/**
+ * Sets the specified source's velocity, for use in 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 )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.setVelocity( x, y, z );
+ }
+
+/**
+ * Sets the listener's velocity, for use in 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 )
+ {
+ listener.setVelocity( x, y, z );
+ }
+
+/**
+ * Notifies the underlying library that the Doppler parameters have changed.
+ */
+ public void dopplerChanged()
+ {}
+
+/**
+ * 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 )
+ {
+ if( sourcename == null || sourcename.equals( "" ) )
+ {
+ errorMessage( "Sourcename not specified in method " +
+ "'millisecondsPlayed'" );
+ return -1;
+ }
+
+ if( midiSourcename( sourcename ) )
+ {
+ errorMessage( "Unable to calculate milliseconds for MIDI source." );
+ return -1;
+ }
+ else
+ {
+ Source source = sourceMap.get( sourcename );
+ if( source == null )
+ {
+ errorMessage( "Source '" + sourcename + "' not found in " +
+ "method 'millisecondsPlayed'" );
+ }
+ return source.millisecondsPlayed();
+ }
+ }
+/**
+ * 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.
+ * @param sourcename Name of the streaming source to play from.
+ * @param buffer Byte buffer containing raw audio data to stream.
+ * @return Number of prior buffers that have been processed, or -1 if unable to queue the buffer (if the source was culled, for example).
+ */
+ public int feedRawAudioData( String sourcename, byte[] buffer )
+ {
+ if( sourcename == null || sourcename.equals( "" ) )
+ {
+ errorMessage( "Sourcename not specified in method " +
+ "'feedRawAudioData'" );
+ return -1;
+ }
+
+ if( midiSourcename( sourcename ) )
+ {
+ errorMessage( "Raw audio data can not be fed to the " +
+ "MIDI channel." );
+ return -1;
+ }
+ else
+ {
+ Source source = sourceMap.get( sourcename );
+ if( source == null )
+ {
+ errorMessage( "Source '" + sourcename + "' not found in " +
+ "method 'feedRawAudioData'" );
+ }
+ return feedRawAudioData( source, buffer );
+ }
+ }
+
+/**
+ * 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.
+ * @param source Streaming source to play from.
+ * @param buffer Byte buffer containing raw audio data to stream.
+ * @return Number of prior buffers that have been processed, or -1 if unable to queue the buffer (if the source was culled, for example).
+ */
+ public int feedRawAudioData( Source source, byte[] buffer )
+ {
+ if( source == null )
+ {
+ errorMessage( "Source parameter null in method " +
+ "'feedRawAudioData'" );
+ return -1;
+ }
+ if( !source.toStream )
+ {
+ errorMessage( "Only a streaming source may be specified in " +
+ "method 'feedRawAudioData'" );
+ return -1;
+ }
+ if( !source.rawDataStream )
+ {
+ errorMessage( "Streaming source already associated with a " +
+ "file or URL in method'feedRawAudioData'" );
+ return -1;
+ }
+
+ if( !source.playing() || source.channel == null )
+ {
+ Channel channel;
+ if( source.channel != null && ( source.channel.attachedSource ==
+ source ) )
+ channel = source.channel;
+ else
+ channel = getNextChannel( source );
+
+ int processed = source.feedRawAudioData( channel, buffer );
+ channel.attachedSource = source;
+ streamThread.watch( source );
+ streamThread.interrupt();
+ return processed;
+ }
+
+ return( source.feedRawAudioData( source.channel, buffer ) );
+ }
+
+/**
+ * Looks up the specified source and plays it.
+ * @param sourcename Name of the source to play.
+ */
+ public void play( String sourcename )
+ {
+ if( sourcename == null || sourcename.equals( "" ) )
+ {
+ errorMessage( "Sourcename not specified in method 'play'" );
+ return;
+ }
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source source = sourceMap.get( sourcename );
+ if( source == null )
+ {
+ errorMessage( "Source '" + sourcename + "' not found in " +
+ "method 'play'" );
+ }
+ play( source );
+ }
+ }
+
+/**
+ * Plays the specified source.
+ * @param source The source to play.
+ */
+ public void play( Source source )
+ {
+ if( source == null )
+ return;
+
+ // raw data streams will automatically play when data is sent to them,
+ // so no need to do anything here.
+ if( source.rawDataStream )
+ return;
+
+ if( !source.active() )
+ return;
+
+ if( !source.playing() )
+ {
+ Channel channel = getNextChannel( source );
+
+ if( source != null && channel != null )
+ {
+ if( source.channel != null &&
+ source.channel.attachedSource != source )
+ source.channel = null;
+ channel.attachedSource = source;
+ source.play( channel );
+ if( source.toStream )
+ {
+ streamThread.watch( source );
+ streamThread.interrupt();
+ }
+ }
+ }
+ }
+
+/**
+ * Stops the specified source.
+ * @param sourcename The source's name.
+ */
+ public void stop( String sourcename )
+ {
+ if( sourcename == null || sourcename.equals( "" ) )
+ {
+ errorMessage( "Sourcename not specified in method 'stop'" );
+ return;
+ }
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.stop();
+ }
+ }
+
+/**
+ * Pauses the specified source.
+ * @param sourcename The source's name.
+ */
+ public void pause( String sourcename )
+ {
+ if( sourcename == null || sourcename.equals( "" ) )
+ {
+ errorMessage( "Sourcename not specified in method 'pause'" );
+ return;
+ }
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.pause();
+ }
+ }
+
+/**
+ * Rewinds the specified source.
+ * @param sourcename The source's name.
+ */
+ public void rewind( String sourcename )
+ {
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.rewind();
+ }
+ }
+
+/**
+ * Clears all previously queued data from a stream.
+ * @param sourcename The source's name.
+ */
+ public void flush( String sourcename )
+ {
+ if( midiSourcename( sourcename ) )
+ errorMessage( "You can not flush the MIDI channel" );
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.flush();
+ }
+ }
+
+/**
+ * Culls the specified source. A culled source will not play until it has been
+ * activated again.
+ * @param sourcename The source's name.
+ */
+ public void cull( String sourcename )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.cull();
+ }
+
+/**
+ * Activates a previously culled source, so it can be played again.
+ * @param sourcename The source's name.
+ */
+ public void activate( String sourcename )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ {
+ mySource.activate();
+ if( mySource.toPlay )
+ play( mySource );
+ }
+ }
+
+/**
+ * Sets the overall volume to the specified value, affecting all sources.
+ * @param value New volume, float value ( 0.0f - 1.0f ).
+ */
+ public void setMasterVolume( float value )
+ {
+ SoundSystemConfig.setMasterGain( value );
+ }
+
+/**
+ * Manually sets the specified source's volume.
+ * @param sourcename The source's name.
+ * @param value A float value ( 0.0f - 1.0f ).
+ */
+ public void setVolume( String sourcename, float value )
+ {
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ {
+ float newVolume = value;
+ if( newVolume < 0.0f )
+ newVolume = 0.0f;
+ else if( newVolume > 1.0f )
+ newVolume = 1.0f;
+
+ mySource.sourceVolume = newVolume;
+ mySource.positionChanged();
+ }
+ }
+ }
+
+/**
+ * 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 )
+ {
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ return mySource.sourceVolume;
+ 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 )
+ {
+ if( !midiSourcename( sourcename ) )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ {
+ float newPitch = value;
+ if( newPitch < 0.5f )
+ newPitch = 0.5f;
+ else if( newPitch > 2.0f )
+ newPitch = 2.0f;
+
+ mySource.setPitch( newPitch );
+ mySource.positionChanged();
+ }
+ }
+ }
+
+/**
+ * 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( !midiSourcename( sourcename ) )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ return mySource.getPitch();
+ }
+ return 1.0f;
+ }
+
+/**
+ * Moves the listener relative to the current position.
+ * @param x X offset.
+ * @param y Y offset.
+ * @param z Z offset.
+ */
+ public void moveListener( float x, float y, float z )
+ {
+ setListenerPosition( listener.position.x + x, listener.position.y + y,
+ listener.position.z + z );
+ }
+
+/**
+ * Changes the listener's position.
+ * @param x Destination X coordinate.
+ * @param y Destination Y coordinate.
+ * @param z Destination Z coordinate.
+ */
+ public void setListenerPosition( float x, float y, float z )
+ {
+ // update listener's position
+ listener.setPosition( x, y, z );
+
+ Set<String> keys = sourceMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ String sourcename;
+ Source source;
+
+ // loop through and update the volume of all sources:
+ while( iter.hasNext() )
+ {
+ sourcename = iter.next();
+ source = sourceMap.get( sourcename );
+ if( source != null )
+ source.positionChanged();
+ }
+ }
+
+/**
+ * Turn the listener 'angle' radians counterclockwise around the y-Axis,
+ * relative to the current angle.
+ * @param angle Angle in radians.
+ */
+ public void turnListener( float angle )
+ {
+ setListenerAngle( listener.angle + angle );
+
+ Set<String> keys = sourceMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ String sourcename;
+ Source source;
+
+ // loop through and update the volume of all sources:
+ while( iter.hasNext() )
+ {
+ sourcename = iter.next();
+ source = sourceMap.get( sourcename );
+ if( source != null )
+ source.positionChanged();
+ }
+ }
+
+/**
+ * Changes the listeners orientation to the specified 'angle' radians
+ * counterclockwise around the y-Axis.
+ * @param angle Angle in radians.
+ */
+ public void setListenerAngle( float angle )
+ {
+ listener.setAngle( angle );
+
+ Set<String> keys = sourceMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ String sourcename;
+ Source source;
+
+ // loop through and update the volume of all sources:
+ while( iter.hasNext() )
+ {
+ sourcename = iter.next();
+ source = sourceMap.get( sourcename );
+ if( source != null )
+ source.positionChanged();
+ }
+ }
+
+/**
+ * Changes the listeners orientation using the specified coordinates.
+ * @param lookX X element of the look-at direction.
+ * @param lookY Y element of the look-at direction.
+ * @param lookZ Z element of the look-at direction.
+ * @param upX X element of the up direction.
+ * @param upY Y element of the up direction.
+ * @param upZ Z element of the up direction.
+ */
+ public void setListenerOrientation( float lookX, float lookY, float lookZ,
+ float upX, float upY, float upZ )
+ {
+ listener.setOrientation( lookX, lookY, lookZ, upX, upY, upZ );
+
+ Set<String> keys = sourceMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ String sourcename;
+ Source source;
+
+ // loop through and update the volume of all sources:
+ while( iter.hasNext() )
+ {
+ sourcename = iter.next();
+ source = sourceMap.get( sourcename );
+ if( source != null )
+ source.positionChanged();
+ }
+ }
+
+/**
+ * Changes the listeners position and orientation using the specified listener
+ * data.
+ * @param l Listener data to use.
+ */
+ public void setListenerData( ListenerData l )
+ {
+ listener.setData( l );
+ }
+
+/**
+ * Creates sources based on the source map provided.
+ * @param srcMap Sources to copy.
+ */
+ public void copySources( HashMap<String, Source> srcMap )
+ {
+ if( srcMap == null )
+ return;
+ Set<String> keys = srcMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ String sourcename;
+ Source srcData;
+
+ // remove any existing sources before starting:
+ sourceMap.clear();
+
+ // loop through and copy all the sources:
+ while( iter.hasNext() )
+ {
+ sourcename = iter.next();
+ srcData = srcMap.get( sourcename );
+ if( srcData != null )
+ {
+ loadSound( srcData.filenameURL );
+ sourceMap.put( sourcename, new Source( srcData, null ) );
+ }
+ }
+ }
+
+/**
+ * Stops and deletes the specified source.
+ * @param sourcename The source's name.
+ */
+ public void removeSource( String sourcename )
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.cleanup(); // end the source, free memory
+ sourceMap.remove( sourcename );
+ }
+
+/**
+ * Searches for and removes all temporary sources that have finished playing.
+ */
+ public void removeTemporarySources()
+ {
+ Set<String> keys = sourceMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ String sourcename;
+ Source srcData;
+
+ // loop through and cleanup all the sources:
+ while( iter.hasNext() )
+ {
+ sourcename = iter.next();
+ srcData = sourceMap.get( sourcename );
+ if( (srcData != null) && (srcData.temporary)
+ && (!srcData.playing()) )
+ {
+ srcData.cleanup(); // end the source, free memory
+ iter.remove();
+ }
+ }
+ }
+
+/* ########################################################################## */
+/* END OVERRIDE METHODS */
+/* ########################################################################## */
+
+/**
+ * Returns a handle to the next available channel. If the specified
+ * source is a normal source, a normal channel is returned, and if it is a
+ * streaming source, then a streaming channel is returned. If all channels of
+ * the required type are currently playing, then the next channel playing a
+ * non-priority source is returned. If no channels are available (i.e. they
+ * are all playing priority sources) then getNextChannel returns null.
+ * @param source Source to find a channel for.
+ * @return The next available channel, or null.
+ */
+ private Channel getNextChannel( Source source )
+ {
+ if( source == null )
+ return null;
+
+ String sourcename = source.sourcename;
+ if( sourcename == null )
+ return null;
+
+ int x;
+ int channels;
+ int nextChannel;
+ List<Channel> channelList;
+ String[] sourceNames;
+ String name;
+
+ if( source.toStream )
+ {
+ nextChannel = nextStreamingChannel;
+ channelList = streamingChannels;
+ sourceNames = streamingChannelSourceNames;
+ }
+ else
+ {
+ nextChannel = nextNormalChannel;
+ channelList = normalChannels;
+ sourceNames = normalChannelSourceNames;
+ }
+
+ channels = channelList.size();
+
+ // Check if this source is already on a channel:
+ for( x = 0; x < channels; x++ )
+ {
+ if( sourcename.equals( sourceNames[x] ) )
+ return channelList.get( x );
+ }
+
+ int n = nextChannel;
+ Source src;
+ // Play on the next new or non-playing channel:
+ for( x = 0; x < channels; x++ )
+ {
+ name = sourceNames[n];
+ if( name == null )
+ src = null;
+ else
+ src = sourceMap.get( name );
+
+ if( src == null || !src.playing() )
+ {
+ if( source.toStream )
+ {
+ nextStreamingChannel = n + 1;
+ if( nextStreamingChannel >= channels )
+ nextStreamingChannel = 0;
+ }
+ else
+ {
+ nextNormalChannel = n + 1;
+ if( nextNormalChannel >= channels )
+ nextNormalChannel = 0;
+ }
+ sourceNames[n] = sourcename;
+ return channelList.get( n );
+ }
+ n++;
+ if( n >= channels )
+ n = 0;
+ }
+
+ n = nextChannel;
+ // Play on the next non-priority channel:
+ for( x = 0; x < channels; x++ )
+ {
+ name = sourceNames[n];
+ if( name == null )
+ src = null;
+ else
+ src = sourceMap.get( name );
+
+ if( src == null || !src.playing() || !src.priority )
+ {
+ if( source.toStream )
+ {
+ nextStreamingChannel = n + 1;
+ if( nextStreamingChannel >= channels )
+ nextStreamingChannel = 0;
+ }
+ else
+ {
+ nextNormalChannel = n + 1;
+ if( nextNormalChannel >= channels )
+ nextNormalChannel = 0;
+ }
+ sourceNames[n] = sourcename;
+ return channelList.get( n );
+ }
+ n++;
+ if( n >= channels )
+ n = 0;
+ }
+
+ return null;
+ }
+
+/**
+ * Plays all sources whose 'toPlay' varriable is true but are not currently
+ * playing (such as sources which were culled while looping and then
+ * reactivated).
+ */
+ public void replaySources()
+ {
+ Set<String> keys = sourceMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ String sourcename;
+ Source source;
+
+ // loop through and cleanup all the sources:
+ while( iter.hasNext() )
+ {
+ sourcename = iter.next();
+ source = sourceMap.get( sourcename );
+ if( source != null )
+ {
+ if( source.toPlay && !source.playing() )
+ {
+ play( sourcename );
+ source.toPlay = false;
+ }
+ }
+ }
+ }
+
+/**
+ * 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.
+ * @param sourcename Source identifier.
+ * @param filenameURL Filename/URL of the sound file to play next.
+ */
+ public void queueSound( String sourcename, FilenameURL filenameURL )
+ {
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.queueSound( filenameURL );
+ }
+ }
+
+/**
+ * Removes the first occurrence of the specified filename 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 remove from the queue.
+ */
+ public void dequeueSound( String sourcename, String filename )
+ {
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.dequeueSound( filename );
+ }
+ }
+
+/**
+ * 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.
+ * @param sourcename Name of the source to fade out.
+ * @param filenameURL Filename/URL of the sound file to play next, or null for none.
+ * @param milis Number of miliseconds the fadeout should take.
+ */
+ public void fadeOut( String sourcename, FilenameURL filenameURL,
+ long milis )
+ {
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.fadeOut( filenameURL, milis );
+ }
+ }
+
+/**
+ * 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.
+ * @param sourcename Name of the source to fade out/in.
+ * @param filenameURL Filename/URL of the 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, FilenameURL filenameURL,
+ long milisOut, long milisIn )
+ {
+ if( midiSourcename( sourcename ) )
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+ else
+ {
+ Source mySource = sourceMap.get( sourcename );
+ if( mySource != null )
+ mySource.fadeOutIn( filenameURL, milisOut, milisIn );
+ }
+ }
+
+/**
+ * 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()
+ {
+ Channel c;
+ Source s;
+ for( int x = 0; x < streamingChannels.size(); x++ )
+ {
+ c = streamingChannels.get( x );
+ if( c != null )
+ {
+ s = c.attachedSource;
+ if( s != null )
+ s.checkFadeOut();
+ }
+ }
+ c = null;
+ s = null;
+ }
+
+/**
+ * Loads the specified MIDI file, and saves the source information about it.
+ * @param toLoop Midi file should loop or play once.
+ * @param sourcename Source identifier.
+ * @param filenameURL Filename/URL of the MIDI file to load.
+ */
+ public void loadMidi( boolean toLoop, String sourcename,
+ FilenameURL filenameURL )
+ {
+ if( filenameURL == null )
+ {
+ errorMessage( "Filename/URL not specified in method 'loadMidi'." );
+ return;
+ }
+
+ if( !filenameURL.getFilename().matches(
+ SoundSystemConfig.EXTENSION_MIDI ) )
+ {
+ errorMessage( "Filename/identifier doesn't end in '.mid' or" +
+ "'.midi' in method loadMidi." );
+ return;
+ }
+
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+
+/**
+ * Unloads the current Midi file.
+ */
+ public void unloadMidi()
+ {
+ throw new UnsupportedOperationException("MIDI format not supported without a codec");
+ }
+
+/**
+ * Checks if the sourcename matches the midi source.
+ * @param sourcename Source identifier.
+ * @return True if sourcename and midi sourcename match.
+ */
+ public boolean midiSourcename( String sourcename )
+ {
+ return false;
+ }
+
+/**
+ *
+ * Returns the Source object identified by the specified name.
+ * @param sourcename The source's name.
+ * @return The source, or null if not found.
+ */
+ public Source getSource( String sourcename )
+ {
+ return sourceMap.get( sourcename );
+ }
+
+/**
+ * Tells all the sources that the listener has moved.
+ */
+ public void listenerMoved()
+ {
+ Set<String> keys = sourceMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ String sourcename;
+ Source srcData;
+
+ // loop through and copy all the sources:
+ while( iter.hasNext() )
+ {
+ sourcename = iter.next();
+ srcData = sourceMap.get( sourcename );
+ if( srcData != null )
+ {
+ srcData.listenerMoved();
+ }
+ }
+ }
+
+/**
+ * Returns the sources map.
+ * @return Map of all sources.
+ */
+ public HashMap<String, Source> getSources()
+ {
+ return sourceMap;
+ }
+
+/**
+ * Returns information about the listener.
+ * @return A ListenerData object.
+ */
+ public ListenerData getListenerData()
+ {
+ return listener;
+ }
+
+/**
+ * Indicates whether or not this library requires some codecs to reverse-order
+ * the audio data they generate.
+ * @return True if audio data should be reverse-ordered.
+ */
+ public boolean reverseByteOrder()
+ {
+ return reverseByteOrder;
+ }
+/**
+ * Returns the short title of this library type.
+ * @return A short title.
+ */
+ public static String getTitle()
+ {
+ return "No Sound";
+ }
+
+/**
+ * Returns a longer description of this library type.
+ * @return A longer description.
+ */
+ public static String getDescription()
+ {
+ return "Silent Mode";
+ }
+
+/**
+ * Returns the name of the class.
+ * @return "Library" + library title.
+ */
+ public String getClassName()
+ {
+ return "Library";
+ }
+
+/**
+ * Prints a message.
+ * @param message Message to print.
+ */
+ protected void message( String message )
+ {
+ logger.message( message, 0 );
+ }
+
+/**
+ * Prints an important message.
+ * @param message Message to print.
+ */
+ protected void importantMessage( String message )
+ {
+ logger.importantMessage( message, 0 );
+ }
+
+/**
+ * Prints the specified message if error is true.
+ * @param error True or False.
+ * @param message Message to print if error is true.
+ * @return True if error is true.
+ */
+ protected boolean errorCheck( boolean error, String message )
+ {
+ return logger.errorCheck( error, getClassName(), message, 0 );
+ }
+
+/**
+ * Prints an error message.
+ * @param message Message to print.
+ */
+ protected void errorMessage( String message )
+ {
+ logger.errorMessage( getClassName(), message, 0 );
+ }
+
+/**
+ * Prints an exception's error message followed by the stack trace.
+ * @param e Exception containing the information to print.
+ */
+ protected void printStackTrace( Exception e )
+ {
+ logger.printStackTrace( e, 1 );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/ListenerData.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/ListenerData.java
new file mode 100644
index 0000000..94e2ac6
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/ListenerData.java
@@ -0,0 +1,291 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+/**
+ * The listenerData class is used to store information about the
+ * listener's position and orientation. A ListenerData object can be obtained
+ * using SoundSystem's getListenerData() method. See
+ * {@link com.ardor3d.audio.Vector3D Vector3D} for more information about 3D
+ * coordinates and vectors.
+ *<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 ListenerData
+{
+/**
+ * Listener's position in 3D space
+ */
+ public Vector3D position;
+/**
+ * A normalized vector indicating the direction the listener is facing
+ */
+ public Vector3D lookAt;
+/**
+ * A normalized vector indicating the up direction
+ */
+ public Vector3D up;
+/**
+ * Listener's velocity in world-space
+ */
+ public Vector3D velocity;
+
+/**
+ * Used for easy rotation along the x/z plane (for use in a first-person
+ * shooter type of application).
+ */
+ public float angle = 0.0f;
+
+/**
+ * Constructor: Set this listener data to the origin facing along the z-axis
+ */
+ public ListenerData()
+ {
+ position = new Vector3D( 0.0f, 0.0f, 0.0f );
+ lookAt = new Vector3D( 0.0f, 0.0f, -1.0f );
+ up = new Vector3D( 0.0f, 1.0f, 0.0f );
+ velocity = new Vector3D( 0.0f, 0.0f, 0.0f );
+ angle = 0.0f;
+ }
+
+/**
+ * Constructor: Set this listener data to the specified values for position and
+ * orientation.
+ * @param pX Listener's X coordinate.
+ * @param pY Listener's Y coordinate.
+ * @param pZ Listener's Z coordinate.
+ * @param lX X element of the look-at direction.
+ * @param lY Y element of the look-at direction.
+ * @param lZ Z element of the look-at direction.
+ * @param uX X element of the up direction.
+ * @param uY Y element of the up direction.
+ * @param uZ Z element of the up direction.
+ * @param a Angle in radians that the listener is turned counterclockwise around the y-axis.
+ */
+ public ListenerData( float pX, float pY, float pZ, float lX, float lY,
+ float lZ, float uX, float uY, float uZ, float a )
+ {
+ position = new Vector3D( pX, pY, pZ );
+ lookAt = new Vector3D( lX, lY, lZ );
+ up = new Vector3D( uX, uY, uZ );
+ velocity = new Vector3D( 0.0f, 0.0f, 0.0f );
+ angle = a;
+ }
+
+/**
+ * Constructor: Set this listener data to the specified values for position and
+ * orientation.
+ * @param p Position of the listener in 3D space.
+ * @param l Normalized vector indicating the direction which the listener is facing.
+ * @param u Normalized vector indicating the up direction.
+ * @param a Angle in radians that the listener is turned counterclockwise around the y-axis.
+ */
+ public ListenerData( Vector3D p, Vector3D l, Vector3D u, float a )
+ {
+ position = p.clone();
+ lookAt = l.clone();
+ up = u.clone();
+ velocity = new Vector3D( 0.0f, 0.0f, 0.0f );
+ angle = a;
+ }
+
+/**
+ * Change this listener data using the specified coordinates for position and
+ * orientation.
+ * @param pX Listener's X coordinate.
+ * @param pY Listener's Y coordinate.
+ * @param pZ Listener's Z coordinate.
+ * @param lX X element of the look-at direction.
+ * @param lY Y element of the look-at direction.
+ * @param lZ Z element of the look-at direction.
+ * @param uX X element of the up direction.
+ * @param uY Y element of the up direction.
+ * @param uZ Z element of the up direction.
+ * @param a Angle in radians that the listener is turned counterclockwise around the y-axis.
+ */
+ public void setData( float pX, float pY, float pZ, float lX, float lY,
+ float lZ, float uX, float uY, float uZ, float a )
+ {
+ position.x = pX;
+ position.y = pY;
+ position.z = pZ;
+ lookAt.x = lX;
+ lookAt.y = lY;
+ lookAt.z = lZ;
+ up.x = uX;
+ up.y = uY;
+ up.z = uZ;
+ angle = a;
+ }
+
+/**
+ * Change this listener data using the specified 3D vectors for position and
+ * orientation.
+ * @param p Position of the listener in 3D space.
+ * @param l Normalized vector indicating the direction which the listener is facing.
+ * @param u Normalized vector indicating the up direction.
+ * @param a Angle in radians that the listener is turned counterclockwise around the y-axis.
+ */
+ public void setData( Vector3D p, Vector3D l, Vector3D u, float a )
+ {
+ position.x = p.x;
+ position.y = p.y;
+ position.z = p.z;
+ lookAt.x = l.x;
+ lookAt.y = l.y;
+ lookAt.z = l.z;
+ up.x = u.x;
+ up.y = u.y;
+ up.z = u.z;
+ angle = a;
+ }
+
+/**
+ * Change this listener data to match the specified listener data.
+ * @param l Listener data to use.
+ */
+ public void setData( ListenerData l )
+ {
+ position.x = l.position.x;
+ position.y = l.position.y;
+ position.z = l.position.z;
+ lookAt.x = l.lookAt.x;
+ lookAt.y = l.lookAt.y;
+ lookAt.z = l.lookAt.z;
+ up.x = l.up.x;
+ up.y = l.up.y;
+ up.z = l.up.z;
+ angle = l.angle;
+ }
+
+/**
+ * Change this listener's position using the specified coordinates.
+ * @param x Listener's X coordinate.
+ * @param y Listener's Y coordinate.
+ * @param z Listener's Z coordinate.
+ */
+ public void setPosition( float x, float y, float z )
+ {
+ position.x = x;
+ position.y = y;
+ position.z = z;
+ }
+
+/**
+ * Change this listener's position using the specified vector.
+ * @param p New position.
+ */
+ public void setPosition( Vector3D p )
+ {
+ position.x = p.x;
+ position.y = p.y;
+ position.z = p.z;
+ }
+
+/**
+ * Changes the listeners orientation using the specified coordinates.
+ * @param lX X element of the look-at direction.
+ * @param lY Y element of the look-at direction.
+ * @param lZ Z element of the look-at direction.
+ * @param uX X element of the up direction.
+ * @param uY Y element of the up direction.
+ * @param uZ Z element of the up direction.
+ */
+ public void setOrientation( float lX, float lY, float lZ,
+ float uX, float uY, float uZ )
+ {
+ lookAt.x = lX;
+ lookAt.y = lY;
+ lookAt.z = lZ;
+ up.x = uX;
+ up.y = uY;
+ up.z = uZ;
+ }
+
+/**
+ * Changes the listeners orientation using the specified vectors.
+ * @param l Normalized vector representing the look-at direction.
+ * @param u Normalized vector representing the up direction.
+ */
+ public void setOrientation( Vector3D l, Vector3D u )
+ {
+ lookAt.x = l.x;
+ lookAt.y = l.y;
+ lookAt.z = l.z;
+ up.x = u.x;
+ up.y = u.y;
+ up.z = u.z;
+ }
+
+/**
+ * Change this listener's velocity in world-space.
+ * @param v New velocity.
+ */
+ public void setVelocity( Vector3D v )
+ {
+ velocity.x = v.x;
+ velocity.y = v.y;
+ velocity.z = v.z;
+ }
+
+/**
+ * Change this listener's velocity in world-space.
+ * @param x New velocity along world x-axis.
+ * @param y New velocity along world y-axis.
+ * @param z New velocity along world z-axis.
+ */
+ public void setVelocity( float x, float y, float z )
+ {
+ velocity.x = x;
+ velocity.y = y;
+ velocity.z = z;
+ }
+
+/**
+ * Sets the listener's angle counterclockwise around the y-axis.
+ * @param a Angle in radians.
+ */
+ public void setAngle( float a )
+ {
+ angle = a;
+ lookAt.x = -1.0f * (float) Math.sin( angle );
+ lookAt.z = -1.0f * (float) Math.cos( angle );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/SimpleThread.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/SimpleThread.java
new file mode 100644
index 0000000..72dbb38
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/SimpleThread.java
@@ -0,0 +1,209 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+
+/**
+ * The SimpleThread class is the template used to create all thread classes
+ * used by in the SoundSystem library. It provides methods for common actions
+ * like sleeping, killing, and checking liveness. NOTE: super.cleanup() must
+ * be called at the bottom of overriden cleanup() methods, and cleanup()
+ * must be called at the bottom of the run() method for all extended classes.
+ *<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 SimpleThread extends Thread
+{
+/**
+ * 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;
+
+/**
+ * True when thread is running.
+ */
+ private boolean alive = true;
+
+/**
+ * True when thread should end.
+ */
+ private boolean kill = false;
+
+/**
+ * Removes all references to instantiated objects, and changes the thread's
+ * state to "not alive". Method alive() returns false when this method has
+ * completed. NOTE: super.cleanup() must be called at the bottom of overriden
+ * cleanup() methods, and cleanup() must be called at the bottom of the run()
+ * method for all extended classes.
+ */
+ protected void cleanup()
+ {
+ kill( SET, true ); // tread needs to shut down
+ alive( SET, false ); // thread has ended
+ }
+
+/**
+ * Executes the thread's main loop. NOTES: Extended classes should check
+ * method dying() often to know when the user wants the thread to shut down.
+ * Method cleanup() must be called at the bottom of the run() method for all
+ * extended classes.
+ */
+ @Override
+ public void run()
+ {
+ /* How the run() method should be set up: */
+
+
+ // Do your stuff here. Remember to check dying() often to know when
+ // the user wants the thread to shut down.
+
+ // MUST call cleanup() at the bottom of Overridden run() method!!!!!
+ cleanup(); // clears memory and sets status to dead.
+ }
+
+/**
+ * Calls the rerun() method on a seperate thread, which calls run() when the
+ * previous thread finishes.
+ */
+ public void restart()
+ {
+ new Thread()
+ {
+ @Override
+ public void run()
+ {
+ rerun();
+ }
+ }.start();
+ }
+
+/**
+ * Kills the previous thread, waits for it to die, then calls run().
+ */
+ private void rerun()
+ {
+ kill( SET, true );
+ while( alive( GET, XXX ) )
+ {
+ snooze( 100 );
+ }
+ alive( SET, true );
+ kill( SET, false );
+ run();
+ }
+
+/**
+ * Returns false when the cleanup() method has finished. This method should be
+ * used to know when the thread has been safely shut down.
+ * @return True while the thread is alive.
+ */
+ public boolean alive()
+ {
+ return alive( GET, XXX );
+ }
+
+/**
+ * Causes method dying() to return true, letting the thread know it needs to
+ * shut down.
+ */
+ public void kill()
+ {
+ kill( SET, true );
+ }
+
+/**
+ * Returns true when the thread is supposed to shut down.
+ * @return True if the thread should die.
+ */
+ protected boolean dying()
+ {
+ return kill( GET, XXX );
+ }
+
+/**
+ * Sets or returns the value of boolean 'alive'.
+ * @param action GET or SET.
+ * @param value New value if action == SET, or XXX if action == GET.
+ * @return True while the thread is alive.
+ */
+ private synchronized boolean alive( boolean action, boolean value )
+ {
+ if( action == SET )
+ alive = value;
+ return alive;
+ }
+
+/**
+ * Sets or returns the value of boolean 'kill'.
+ * @param action GET or SET.
+ * @param value New value if action == SET, or XXX if action == GET.
+ * @return True if the thread should die.
+ */
+ private synchronized boolean kill( boolean action, boolean value )
+ {
+ if( action == SET )
+ kill = value;
+ return kill;
+ }
+
+/**
+ * Sleeps for the specified number of milliseconds.
+ */
+ protected void snooze( long milliseconds )
+ {
+ try
+ {
+ Thread.sleep( milliseconds );
+ }
+ catch( InterruptedException e ){}
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundBuffer.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundBuffer.java
new file mode 100644
index 0000000..047e926
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundBuffer.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+import com.ardor3d.audio.sampled.AudioFormat;
+
+/**
+ * The SoundBuffer class is used to wrap audio data along with the format in
+ * which the data is stored.
+ *<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 SoundBuffer
+{
+/**
+ * The actual audio data.
+ */
+ public byte[] audioData;
+/**
+ * The audio format in which the data is stored.
+ */
+ public AudioFormat audioFormat;
+
+/**
+ * Constructor: Wraps the specified data with the specified audio format.
+ *
+ * @param audioData The actual audio data.
+ * @param audioFormat The audio format in which the data is stored.
+ */
+ public SoundBuffer( byte[] audioData, AudioFormat audioFormat )
+ {
+ this.audioData = audioData;
+ this.audioFormat = audioFormat;
+ }
+
+/**
+ * Removes handles to all instantiated objects.
+ */
+ public void cleanup()
+ {
+ audioData = null;
+ audioFormat = null;
+ }
+
+/**
+ * Trims down the size of the audio data if it is larger than the specified
+ * maximum length.
+ *
+ * @param maxLength Maximum size this buffer may be.
+ */
+ public void trimData( int maxLength )
+ {
+ if( audioData == null || maxLength == 0 )
+ audioData = null;
+ else if( audioData.length > maxLength )
+ {
+ byte[] trimmedArray = new byte[maxLength];
+ System.arraycopy( audioData, 0, trimmedArray, 0,
+ maxLength );
+ audioData = trimmedArray;
+ }
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystem.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystem.java
new file mode 100644
index 0000000..8f09183
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystem.java
@@ -0,0 +1,2893 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+import java.lang.reflect.InvocationTargetException;
+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 com.ardor3d.audio.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 com.ardor3d.audio.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>
+ *
+ * Julien Gouesse: I removed Midi support in order to stop depending on javax.sound more easily and I replaced a deprecated call
+ */
+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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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;
+
+ if( soundLibrary != null )
+ {
+ currentLibrary( SET, null );
+ sourceMap = copySources( soundLibrary.getSources() );
+ listenerData = soundLibrary.getListenerData();
+
+ soundLibrary.cleanup();
+ soundLibrary = null;
+ }
+ message( "", 0 );
+ message( "Switching to "
+ + SoundSystemConfig.getLibraryTitle( libraryClass ), 0 );
+ message( "(" + SoundSystemConfig.getLibraryDescription( libraryClass )
+ + ")", 1 );
+
+ try
+ {
+ soundLibrary = (Library) libraryClass.getDeclaredConstructor().newInstance();
+ }
+ catch( NoSuchMethodException nsme )
+ {
+ errorMessage( "The specified library did not load properly", 1 );
+ }
+ catch( InvocationTargetException ite )
+ {
+ errorMessage( "The specified library did not load properly", 1 );
+ }
+ 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 );
+ 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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 com.ardor3d.audio.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 );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemConfig.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemConfig.java
new file mode 100644
index 0000000..546d71e
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemConfig.java
@@ -0,0 +1,1083 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Locale;
+import java.util.ListIterator;
+import java.util.LinkedList;
+
+/**
+ * The SoundSystemConfig class is used to access global sound system settings,
+ * and to link with external pluggins. All members of this class are static.
+ * SoundSystemConfig is sort of a "catch all" configuration class, so if you
+ * are not sure where to find something in the SoundSystem library, this is
+ * probably a good place to start.
+ *<br><br>
+ *<b><i> SoundSystem License:</b></i><br><b><br>
+ * You are free to use this library for any purpose, commercial or otherwise.
+ * You may modify this library or source code, and distribute it any way you
+ * like, provided the following conditions are met:
+ *<br>
+ * 1) You may not falsely claim to be the author of this library or any
+ * unmodified portion of it.
+ *<br>
+ * 2) You may not copyright this library or a modified version of it and then
+ * sue me for copyright infringement.
+ *<br>
+ * 3) If you modify the source code, you must clearly document the changes
+ * made before redistributing the modified source code, so other users know
+ * it is not the original code.
+ *<br>
+ * 4) You are not required to give me credit for this library in any derived
+ * work, but if you do, you must also mention my website:
+ * http://www.paulscode.com
+ *<br>
+ * 5) I the author will not be responsible for any damages (physical,
+ * financial, or otherwise) caused by the use if this library or any part
+ * of it.
+ *<br>
+ * 6) I the author do not guarantee, warrant, or make any representations,
+ * either expressed or implied, regarding the use of this library or any
+ * part of it.
+ * <br><br>
+ * Author: Paul Lamb
+ * <br>
+ * http://www.paulscode.com
+ * </b>
+ */
+public class SoundSystemConfig
+{
+// GLOBAL THREAD SYNCHRONIZATION
+/**
+ * Lock object used to synchronize the three threads used by SoundSystem.
+ * Synchronize on this anytime you manually manipulate a Source's properties.
+ */
+ public static final Object THREAD_SYNC = new Object();
+// END GLOBAL THREAD SYNCHRONIZATION
+
+// GLOBAL IDENTIFIERS
+
+/**
+ * A normal (non-streaming) source. Also used to define a Channel type as
+ * normal.
+ */
+ public static final int TYPE_NORMAL = 0;
+/**
+ * A streaming source. Also used to define a Channel type as streaming.
+ */
+ public static final int TYPE_STREAMING = 1;
+
+/**
+ * Global identifier for no attenuation. Attenuation is how a source's volume
+ * fades with distance. When there is no attenuation, a source's volume
+ * remains constaint regardles of distance.
+ */
+ public static final int ATTENUATION_NONE = 0; // no attenuation
+/**
+ * Global identifier for rolloff attenuation. Rolloff attenuation is a
+ * realistic attenuation model, which uses a rolloff factor to determine how
+ * quickly a source fades with distance. A smaller rolloff factor will fade at
+ * a further distance, and a rolloff factor of 0 will never fade. NOTE: In
+ * OpenAL, rolloff attenuation only works for monotone sounds.
+ */
+ public static final int ATTENUATION_ROLLOFF = 1; // logrithmic attenuation
+/**
+ * Global identifier for linear attenuation. Linear attenuation is less
+ * realistic than rolloff attenuation, but it allows the user to specify a
+ * maximum "fade distance" where a source's volume becomes zero.
+ */
+ public static final int ATTENUATION_LINEAR = 2; // linear attenuation
+
+/**
+ * A Regular expression for determining if a file's extension is MIDI.
+ */
+ public static String EXTENSION_MIDI = ".*[mM][iI][dD][iI]?$";
+
+/**
+ * A Regular expression for determining if a path is an online URL.
+ */
+ public static String PREFIX_URL = "^[hH][tT][tT][pP]://.*";
+
+// END GLOBAL IDENTIFIERS
+
+
+// PRIVATE STATIC VARIABLES
+
+/**
+ * Handle to the message logger. The default logger can be changed by
+ * overridding the {@link com.ardor3d.audio.SoundSystemLogger SoundSystemLogger}
+ * class and calling the setLogger() method (must be done BEFORE instantiating
+ * the SoundSystem class!)
+ */
+ private static SoundSystemLogger logger = null;
+
+/**
+ * List of library types in their order of priority.
+ */
+ private static LinkedList<Class> libraries;
+
+/**
+ * List of codecs and the file formats they are associated with.
+ */
+ private static LinkedList<Codec> codecs = null;
+
+/**
+ * List of stream listeners.
+ */
+ private static LinkedList<IStreamListener> streamListeners = null;
+/**
+ * For synchronizing access to the streamListeners list.
+ */
+ private static final Object streamListenersLock = new Object();
+
+/**
+ * Maximum number of normal (non-streaming) channels that can be created.
+ * NOTE: JavaSound may require the total number of channels (non-streaming +
+ * streaming) to be 32.
+ */
+ private static int numberNormalChannels = 28;
+/**
+ * Maximum number of streaming channels that can be created.
+ * NOTE: JavaSound may require the total number of channels (non-streaming +
+ * streaming) to be 32.
+ */
+ private static int numberStreamingChannels = 4;
+/**
+ * Overall volume, affecting all sources. Float value (0.0f - 1.0f).
+ */
+ private static float masterGain = 1.0f;
+/**
+ * Attenuation model to use if not specified. Attenuation is how a source's
+ * volume fades with distance.
+ */
+ private static int defaultAttenuationModel = ATTENUATION_ROLLOFF;
+/**
+ * Default value to use for the rolloff factor if not specified.
+ */
+ private static float defaultRolloffFactor = 0.03f;
+/**
+ * Value to use for the doppler factor, for determining Doppler scale.
+ */
+ private static float dopplerFactor = 0.0f;
+/**
+ * Value to use for the doppler velocity.
+ */
+ private static float dopplerVelocity = 1.0f;
+/**
+ * Default value to use for fade distance if not specified.
+ */
+ private static float defaultFadeDistance = 1000.0f;
+/**
+ * Package where the sound files are located (must be followed by '/').
+ */
+ private static String soundFilesPackage = "Sounds/";
+
+/**
+ * Number of bytes to load at a time when streaming.
+ */
+ private static int streamingBufferSize = 131072;
+/**
+ * Number of buffers used for each streaming sorce. Slow codecs may require
+ * this number to be greater than 2 to prevent audio skipping during playback.
+ */
+ private static int numberStreamingBuffers = 3;
+/**
+ * Enables a transition-speed optimization by assuming all sounds in each
+ * streaming source's queue will have exactly the same format once decoded
+ * (including channels, sample rate, and sample size). This is an advanced
+ * setting which should only be changed by experienced developers.
+ */
+ private static boolean streamQueueFormatsMatch = false;
+/**
+ * The maximum number of bytes to read in for (non-streaming) files.
+ * Increase this value if non-streaming sounds are getting cut off.
+ * Decrease this value if large sound files are causing lag during load time.
+ */
+ private static int maxFileSize = 268435456;
+/**
+ * Size of each chunk to read at a time for loading (non-streaming) files.
+ * Increase if loading sound files is causing significant lag.
+ */
+ private static int fileChunkSize = 1048576;
+
+/**
+ * Indicates whether or not there is a codec for reading from MIDI files. If
+ * there is no codec for MIDI, then SoundSystem uses javax.sound.midi.
+ */
+ private static boolean midiCodec = false;
+
+/**
+ * MIDI device to try using as the Synthesizer. May be the full name or part
+ * of the name. If this String is empty, the default Synthesizer will be used,
+ * or one of the common alternate synthesizers if the default Synthesizer is
+ * unavailable.
+ */
+ private static String overrideMIDISynthesizer = "";
+
+// END PRIVATE STATIC VARIABLES
+
+// THESE TWO METHODS PROVIDE INFORMATION ABOUT THE INDIVIDUAL SOUND LIBRARIES
+
+/**
+ * Adds an entry to the list of library types. This method has no effect if
+ * the specified library type is already in the list of libraries.
+ * NOTE: The parameterless constructor of the SoundSystem class will try to
+ * load libraries in the order that they were entered into the list.
+ * @param libraryClass Derivitive of class 'Library'.
+*/
+ public static void addLibrary( Class libraryClass )
+ throws SoundSystemException
+ {
+ if( libraryClass == null )
+ throw new SoundSystemException(
+ "Parameter null in method 'addLibrary'",
+ SoundSystemException.NULL_PARAMETER );
+ if( !Library.class.isAssignableFrom( libraryClass ) )
+ throw new SoundSystemException( "The specified class does not " +
+ "extend class 'Library' in method 'addLibrary'" );
+
+ if( libraries == null )
+ libraries = new LinkedList<Class>();
+
+ if( !libraries.contains( libraryClass ) )
+ libraries.add( libraryClass );
+ }
+
+/**
+ * Removes the specified library from the list of library types.
+ * @param libraryClass Derivitive of class 'Library'.
+*/
+ public static void removeLibrary( Class libraryClass )
+ throws SoundSystemException
+ {
+ if( libraries == null || libraryClass == null )
+ return;
+
+ libraries.remove( libraryClass );
+ }
+
+/**
+ * Returns the list of library types.
+ * @return LinkedList of classes derived from 'Library', or null if none were specified.
+*/
+ public static LinkedList<Class> getLibraries()
+ {
+ return libraries;
+ }
+
+/**
+ * Checks if the specified library class is compatible on the user's machine.
+ * @param libraryClass Library type to check.
+ * @return True or false.
+*/
+ public static boolean libraryCompatible( Class libraryClass )
+ {
+ if( libraryClass == null )
+ {
+ errorMessage( "Parameter 'libraryClass' null in method" +
+ "'librayCompatible'" );
+ return false;
+ }
+ if( !Library.class.isAssignableFrom( libraryClass ) )
+ {
+ errorMessage( "The specified class does not extend class " +
+ "'Library' in method 'libraryCompatible'" );
+ return false;
+ }
+
+ Object o = runMethod( libraryClass, "libraryCompatible",
+ new Class[0], new Object[0] );
+
+ if( o == null )
+ {
+ errorMessage( "Method 'Library.libraryCompatible' returned " +
+ "'null' in method 'libraryCompatible'" );
+ return false;
+ }
+
+ return( ( (Boolean) o ).booleanValue() );
+ }
+
+/**
+ * Return the short title of the specified library, or null if error.
+ * @param libraryClass Derivitive of class 'Library'.
+ * @return String containing the library title.
+*/
+ public static String getLibraryTitle( Class libraryClass )
+ {
+ if( libraryClass == null )
+ {
+ errorMessage( "Parameter 'libraryClass' null in method" +
+ "'getLibrayTitle'" );
+ return null;
+ }
+ if( !Library.class.isAssignableFrom( libraryClass ) )
+ {
+ errorMessage( "The specified class does not extend class " +
+ "'Library' in method 'getLibraryTitle'" );
+ return null;
+ }
+
+ Object o = runMethod( libraryClass, "getTitle", new Class[0],
+ new Object[0] );
+ if( o == null )
+ {
+ errorMessage( "Method 'Library.getTitle' returned " +
+ "'null' in method 'getLibraryTitle'" );
+ return null;
+ }
+
+ return( (String) o );
+ }
+
+/**
+ * Return the longer description of the specified library, or null if error.
+ * @param libraryClass Derivitive of class 'Library'.
+ * @return String containing the library title.
+*/
+ public static String getLibraryDescription( Class libraryClass )
+ {
+ if( libraryClass == null )
+ {
+ errorMessage( "Parameter 'libraryClass' null in method" +
+ "'getLibrayDescription'" );
+ return null;
+ }
+ if( !Library.class.isAssignableFrom( libraryClass ) )
+ {
+ errorMessage( "The specified class does not extend class " +
+ "'Library' in method 'getLibraryDescription'" );
+ return null;
+ }
+
+ Object o = runMethod( libraryClass, "getDescription",
+ new Class[0], new Object[0] );
+ if( o == null )
+ {
+ errorMessage( "Method 'Library.getDescription' returned " +
+ "'null' in method 'getLibraryDescription'" );
+ return null;
+ }
+
+ return( (String) o );
+ }
+
+/**
+ * Return whether or not requires reversal of audio data byte-order.
+ * @param libraryClass Derivitive of class 'Library'.
+ * @return True if byte-order reversal is required.
+*/
+ public static boolean reverseByteOrder( Class libraryClass )
+ {
+ if( libraryClass == null )
+ {
+ errorMessage( "Parameter 'libraryClass' null in method" +
+ "'reverseByteOrder'" );
+ return false;
+ }
+ if( !Library.class.isAssignableFrom( libraryClass ) )
+ {
+ errorMessage( "The specified class does not extend class " +
+ "'Library' in method 'reverseByteOrder'" );
+ return false;
+ }
+
+ Object o = runMethod( libraryClass, "reversByteOrder",
+ new Class[0], new Object[0] );
+ if( o == null )
+ {
+ errorMessage( "Method 'Library.reverseByteOrder' returned " +
+ "'null' in method 'getLibraryDescription'" );
+ return false;
+ }
+
+ return( ((Boolean) o).booleanValue() );
+ }
+
+// END LIBRARY INFORMATION
+
+// Use the following methods to interface the private variables above:
+
+// STATIC NONSYNCHRONIZED INTERFACE METHODS
+/**
+ * Changes the message logger to use for handling status messages, warnings,
+ * and error messages. This method should only be called BEFORE instantiating
+ * the SoundSystem class! If this method is called after the SoundSystem has
+ * been created, there will be handles floating around to two different
+ * loggers, and the results will be undesirable. This method can be used to
+ * change how messages are handled. First, the
+ * {@link com.ardor3d.audio.SoundSystemLogger SoundSystemLogger} class should be
+ * extended and methods overriden to change how messages are handled. Then,
+ * the overridden class should be instantiated, and a call made to
+ * SoundSystemConfig.setLogger() before creating the SoundSystem object.
+ * If an alternate logger is not set by the user before the SoundSystem is
+ * instantiated, then an instance of the base SoundSystemLogger class will be
+ * used by default.
+ * @param l Handle to a message logger.
+ */
+ public static void setLogger( SoundSystemLogger l )
+ {
+ logger = l;
+ }
+/**
+ * Returns a handle to the message logger.
+ * @return The current message logger.
+ */
+ public static SoundSystemLogger getLogger()
+ {
+ return logger;
+ }
+
+// STATIC SYNCHRONIZED INTERFACE METHODS
+
+/**
+ * Sets the maximum number of normal (non-streaming) channels that can be
+ * created. Streaming channels are created first, so the higher the maximum
+ * number of streaming channels is set, the fewer non-streaming channels will
+ * be available. If unable to create the number of channels specified,
+ * SoundSystem will create as many as possible.
+ * NOTE: Some sound library pluggins may require the total number of channels
+ * (non-streaming + streaming) to be 32.
+ * @param number How many normal audio channels.
+ */
+ public static synchronized void setNumberNormalChannels( int number )
+ {
+ numberNormalChannels = number;
+ }
+
+/**
+ * Returns the maximum number of normal (non-streaming) channels that can be
+ * created.
+ * @return Maximum non-streaming channels.
+ */
+ public static synchronized int getNumberNormalChannels()
+ {
+ return numberNormalChannels;
+ }
+
+/**
+ * Sets the maximum number of streaming channels that can be created.
+ * Streaming channels are created first, so the higher the maximum number of
+ * streaming channels is set, the fewer non-streaming channels will
+ * be available. If unable to create the number of channels specified,
+ * SoundSystem will create as many as possible.
+ * NOTE: Some sound library pluggins may require the total number of channels
+ * (non-streaming + streaming) to be 32.
+ * @param number How many streaming audio channels.
+ */
+ public static synchronized void setNumberStreamingChannels( int number )
+ {
+ numberStreamingChannels = number;
+ }
+
+/**
+ * Returns the maximum number of streaming channels that can be created.
+ * @return Maximum streaming channels.
+ */
+ public static synchronized int getNumberStreamingChannels()
+ {
+ return numberStreamingChannels;
+ }
+
+/**
+ * Sets the varriable used for overall volume, affecting all sources.
+ * @param value Float value (0.0f - 1.0f).
+ */
+ public static synchronized void setMasterGain( float value )
+ {
+ masterGain = value;
+ }
+
+/**
+ * Returns the value for the overall volume.
+ * @return A float value (0.0f - 1.0f).
+ */
+ public static synchronized float getMasterGain()
+ {
+ return masterGain;
+ }
+
+/**
+ * Sets the default attenuation model to use when one is not specified.
+ * Attenuation is how a source's volume fades with distance.
+ * @param model A global attenuation model identifier.
+ */
+ public static synchronized void setDefaultAttenuation( int model )
+ {
+ defaultAttenuationModel = model;
+ }
+/**
+ * Returns the default attenuation model used when one is not specified.
+ * @return A global attenuation model identifier
+ */
+ public static synchronized int getDefaultAttenuation()
+ {
+ return defaultAttenuationModel;
+ }
+/**
+ * Sets the default rolloff factor to use when one is not specified.
+ * @param rolloff Rolloff factor.
+ */
+ public static synchronized void setDefaultRolloff( float rolloff )
+ {
+ defaultRolloffFactor = rolloff;
+ }
+/**
+ * Returns the doppler factor, for determining Doppler Effect scale.
+ * @return Doppler factor
+ */
+ public static synchronized float getDopplerFactor()
+ {
+ return dopplerFactor;
+ }
+/**
+ * Sets the doppler factor, for determining Doppler Effect scale. Use this
+ * method BEFORE instantiating the SoundSystem. To change the Doppler factor
+ * after the SoundSystem is instantiated, use the
+ * SoundSystem.changeDopplerFactor method instead.
+ * @param factor Doppler factor.
+ */
+ public static synchronized void setDopplerFactor( float factor )
+ {
+ dopplerFactor = factor;
+ }
+/**
+ * Returns the Doppler Velocity, for use in Doppler Effect.
+ * @return Doppler velocity.
+ */
+ public static synchronized float getDopplerVelocity()
+ {
+ return dopplerVelocity;
+ }
+/**
+ * Sets the Doppler velocity, for use in Doppler Effect. Use this method
+ * BEFORE instantiating the SoundSystem. To change the Doppler velocity after
+ * the SoundSystem is instantiated, use the SoundSystem.changeDopplerVelocity
+ * method instead.
+ * @param velocity Doppler velocity.
+ */
+ public static synchronized void setDopplerVelocity( float velocity )
+ {
+ dopplerVelocity = velocity;
+ }
+/**
+ * Returns the default rolloff factor used when one is not specified.
+ * @return Default rolloff factor
+ */
+ public static synchronized float getDefaultRolloff()
+ {
+ return defaultRolloffFactor;
+ }
+/**
+ * Sets the default fade distance to use when one is not specified.
+ * @param distance Fade Distance.
+ */
+ public static synchronized void setDefaultFadeDistance( float distance )
+ {
+ defaultFadeDistance = distance;
+ }
+/**
+ * Returns the default fade distance used when one is not specified.
+ * @return Default fade distance
+ */
+ public static synchronized float getDefaultFadeDistance()
+ {
+ return defaultFadeDistance;
+ }
+/**
+ * Sets the package where sound files are located.
+ * @param location Path to the sound files location (must be followed by '/').
+ */
+ public static synchronized void setSoundFilesPackage( String location )
+ {
+ soundFilesPackage = location;
+ }
+/**
+ * Returns the package where sound files are located.
+ * @return Path to the sound files location
+ */
+ public static synchronized String getSoundFilesPackage()
+ {
+ return soundFilesPackage;
+ }
+/**
+ * Sets the number of bytes to load at a time when streaming.
+ * @param size Size in bytes.
+ */
+ public static synchronized void setStreamingBufferSize( int size )
+ {
+ streamingBufferSize = size;
+ }
+/**
+ * Returns the number of bytes to load at a time when streaming.
+ * @return Size in bytes.
+ */
+ public static synchronized int getStreamingBufferSize()
+ {
+ return streamingBufferSize;
+ }
+/**
+ * Sets the number of buffers used for each streaming sorce.
+ * Slow codecs may require this number to be greater than 2 to prevent audio
+ * skipping during playback.
+ * @param num How many buffers.
+ */
+ public static synchronized void setNumberStreamingBuffers( int num )
+ {
+ numberStreamingBuffers = num;
+ }
+/**
+ * Returns the number of buffers used for each streaming sorce.
+ * @return How many buffers.
+ */
+ public static synchronized int getNumberStreamingBuffers()
+ {
+ return numberStreamingBuffers;
+ }
+
+/**
+ * Enables a transition-speed optimization by assuming all sounds in each
+ * streaming source's queue will have exactly the same format once decoded
+ * (including channels, sample rate, and sample size). This is an advanced
+ * setting which should only be changed by experienced developers.
+ * @param val False by default.
+ */
+ public static synchronized void setStreamQueueFormatsMatch( boolean val )
+ {
+ streamQueueFormatsMatch = val;
+ }
+
+/**
+ * Returns whether or not all sounds in each streaming source's queue will be
+ * handled as if they have exactly the same format once decoded (including
+ * channels, sample rate, and sample size). This is an advanced setting which
+ * should only be changed by experienced developers.
+ * @return Normally false.
+ */
+ public static synchronized boolean getStreamQueueFormatsMatch()
+ {
+ return streamQueueFormatsMatch;
+ }
+
+/**
+ * Sets the maximum number of bytes to read in for (non-streaming) files.
+ * Increase this value if non-streaming sounds are getting cut off.
+ * Decrease this value if large sound files are causing lag during load time.
+ * @param size Size in bytes.
+ */
+ public static synchronized void setMaxFileSize( int size )
+ {
+ maxFileSize = size;
+ }
+/**
+ * Returns the maximum number of bytes to read in for (non-streaming) files.
+ * @return Size in bytes.
+ */
+ public static synchronized int getMaxFileSize()
+ {
+ return maxFileSize;
+ }
+/**
+ * Sets the size of each chunk to read at a time for loading (non-streaming)
+ * files. Increase if loading sound files is causing significant lag.
+ * @param size Size in bytes.
+ */
+ public static synchronized void setFileChunkSize( int size )
+ {
+ fileChunkSize = size;
+ }
+/**
+ * Returns the size of each chunk to read at a time for loading (non-streaming)
+ * files.
+ * @return Size in bytes.
+ */
+ public static synchronized int getFileChunkSize()
+ {
+ return fileChunkSize;
+ }
+/**
+ * Returns the name of the MIDI synthesizer to use instead of the default, or
+ * empty string if none was specified.
+ * @return All or part of a MIDI device name, or empty string for not specified.
+ */
+ public static synchronized String getOverrideMIDISynthesizer()
+ {
+ return overrideMIDISynthesizer;
+ }
+/**
+ * Sets the name of the MIDI synthesizer to use instead of the default. If
+ * 'name' is an empty string, the default Synthesizer will be used, or one of
+ * the common alternate synthesizers if the default Synthesizer is unavailable.
+ * @param name All or part of the MIDI device name.
+ */
+ public static synchronized void setOverrideMIDISynthesizer( String name )
+ {
+ overrideMIDISynthesizer = name;
+ }
+/**
+ * Uses the specified file extension to associate a particular file format
+ * with the codec used to read audio data from it.
+ * @param extension File extension to be associated with the specified codec.
+ * @param iCodecClass Codec type to use for files with the specified extension.
+ */
+ public static synchronized void setCodec( String extension,
+ Class iCodecClass )
+ throws SoundSystemException
+ {
+ if( extension == null )
+ throw new SoundSystemException( "Parameter 'extension' null in " +
+ "method 'setCodec'.",
+ SoundSystemException.NULL_PARAMETER );
+ if( iCodecClass == null )
+ throw new SoundSystemException( "Parameter 'iCodecClass' null in " +
+ "method 'setCodec'.",
+ SoundSystemException.NULL_PARAMETER );
+ if( !ICodec.class.isAssignableFrom( iCodecClass ) )
+ throw new SoundSystemException( "The specified class does " +
+ "not implement interface 'ICodec' in method 'setCodec'",
+ SoundSystemException.CLASS_TYPE_MISMATCH );
+
+ if( codecs == null )
+ codecs = new LinkedList<Codec>();
+
+ ListIterator<Codec> i = codecs.listIterator();
+ Codec codec;
+
+ while( i.hasNext() )
+ {
+ codec = i.next();
+ if( extension.matches( codec.extensionRegX ) )
+ i.remove();
+ }
+ codecs.add( new Codec( extension, iCodecClass ) );
+
+ // Let SoundSystem know if this is a MIDI codec, so it won't use
+ // javax.sound.midi anymore:
+ if( extension.matches( EXTENSION_MIDI ) )
+ midiCodec = true;
+ }
+/**
+ * Returns the codec that can be used to read audio data from the specified
+ * file.
+ * @param filename File to get a codec for.
+ * @return Codec to use for reading audio data.
+ */
+ public static synchronized ICodec getCodec( String filename )
+ {
+ if( codecs == null )
+ return null;
+
+ ListIterator<Codec> i = codecs.listIterator();
+ Codec codec;
+
+ while( i.hasNext() )
+ {
+ codec = i.next();
+ if( filename.matches( codec.extensionRegX ) )
+ return codec.getInstance();
+ }
+
+ return null;
+ }
+
+/**
+ * Indicates whether or not there is a codec for reading from MIDI files. If
+ * there is no codec for MIDI, then SoundSystem uses javax.sound.midi.
+ * @return True if there the user defined a MIDI codec.
+ */
+ public static boolean midiCodec()
+ {
+ return midiCodec;
+ }
+
+/**
+ * Adds an entry to the list of stream listeners. If the instance is already
+ * in the list, the command is ignored.
+ * @param streamListener Implementation of interface 'IStreamListener'.
+*/
+ public static void addStreamListener( IStreamListener streamListener )
+ {
+ synchronized( streamListenersLock )
+ {
+ if( streamListeners == null )
+ streamListeners = new LinkedList<IStreamListener>();
+
+ if( !streamListeners.contains( streamListener ) )
+ streamListeners.add( streamListener );
+ }
+ }
+
+/**
+ * Removes an entry from the list of stream listeners.
+ * @param streamListener Implementation of interface 'IStreamListener'.
+*/
+ public static void removeStreamListener( IStreamListener streamListener )
+ {
+
+ synchronized( streamListenersLock )
+ {
+ if( streamListeners == null )
+ streamListeners = new LinkedList<IStreamListener>();
+
+ if( streamListeners.contains( streamListener ) )
+ streamListeners.remove( streamListener );
+ }
+ }
+
+/**
+ * Notifies all stream listeners that an End Of Stream was reached. If there
+ * are no listeners, the command is ignored.
+ * @param sourcename String identifier of the source which reached the EOS.
+ * @param queueSize Number of items left the the stream's play queue, or zero if none.
+*/
+ public static void notifyEOS( String sourcename, int queueSize )
+ {
+ synchronized( streamListenersLock )
+ {
+ if( streamListeners == null )
+ return;
+ }
+ final String srcName = sourcename;
+ final int qSize = queueSize;
+
+ new Thread()
+ {
+ @Override
+ public void run()
+ {
+ synchronized( streamListenersLock )
+ {
+ if( streamListeners == null )
+ return;
+ ListIterator<IStreamListener> i = streamListeners.listIterator();
+ IStreamListener streamListener;
+ while( i.hasNext() )
+ {
+ streamListener = i.next();
+ if( streamListener == null )
+ i.remove();
+ else
+ streamListener.endOfStream( srcName, qSize );
+ }
+ }
+ }
+ }.start();
+ }
+
+// END STATIC SYNCHRONIZED INTERFACE METHODS
+
+
+// PRIVATE INTERNAL METHODS
+
+/**
+ * Display the specified error message using the current logger.
+ * @param message Error message to display.
+*/
+ private static void errorMessage( String message )
+ {
+ if( logger != null )
+ logger.errorMessage( "SoundSystemConfig", message, 0 );
+ }
+
+ // We don't know what Class parameter 'c' is, so we will ignore the
+ // warning message "unchecked call to getMethod".
+ @SuppressWarnings("unchecked")
+/**
+ * Returns the results of calling the specified method from the specified
+ * class using the specified parameters.
+ * @param c Class to call the method on.
+ * @param method Name of the method.
+ * @param paramTypes Data types of the parameters being passed to the method.
+ * @param params Actual parameters to pass to the method.
+ * @return Specified method's return value, or null if error or void.
+*/
+ private static Object runMethod( Class c, String method, Class[] paramTypes,
+ Object[] params )
+ {
+ Method m = null;
+ try
+ {
+ m = c.getMethod( method, paramTypes ); // <--- generates a warning
+ }
+ catch( NoSuchMethodException nsme )
+ {
+ errorMessage( "NoSuchMethodException thrown when attempting " +
+ "to call method '" + method + "' in " +
+ "method 'runMethod'" );
+ return null;
+ }
+ catch( SecurityException se )
+ {
+ errorMessage( "Access denied when attempting to call method '" +
+ method + "' in method 'runMethod'" );
+ return null;
+ }
+ catch( NullPointerException npe )
+ {
+ errorMessage( "NullPointerException thrown when attempting " +
+ "to call method '" + method + "' in " +
+ "method 'runMethod'" );
+ return null;
+ }
+ if( m == null )
+ {
+ errorMessage( "Method '" + method + "' not found for the class " +
+ "specified in method 'runMethod'" );
+ return null;
+ }
+
+ Object o = null;
+ try
+ {
+ o = m.invoke( null, params );
+ }
+ catch( IllegalAccessException iae )
+ {
+ errorMessage( "IllegalAccessException thrown when attempting " +
+ "to invoke method '" + method + "' in " +
+ "method 'runMethod'" );
+ return null;
+ }
+ catch( IllegalArgumentException iae )
+ {
+ errorMessage( "IllegalArgumentException thrown when attempting " +
+ "to invoke method '" + method + "' in " +
+ "method 'runMethod'" );
+ return null;
+ }
+ catch( InvocationTargetException ite )
+ {
+ errorMessage( "InvocationTargetException thrown while attempting " +
+ "to invoke method 'Library.getTitle' in " +
+ "method 'getLibraryTitle'" );
+ return null;
+ }
+ catch( NullPointerException npe )
+ {
+ errorMessage( "NullPointerException thrown when attempting " +
+ "to invoke method '" + method + "' in " +
+ "method 'runMethod'" );
+ return null;
+ }
+ catch( ExceptionInInitializerError eiie )
+ {
+ errorMessage( "ExceptionInInitializerError thrown when " +
+ "attempting to invoke method '" + method + "' in " +
+ "method 'runMethod'" );
+ return null;
+ }
+
+ return( o );
+ }
+
+// END PRIVATE INTERNAL METHODS
+
+
+// PRIVATE INTERNAL CLASSES
+
+/**
+ * The Codec class is used to associate individual file formats with the
+ * codecs used to load audio data from them.
+ *
+ * Author: Paul Lamb
+ */
+ private static class Codec
+ {
+/**
+ * A regular expression used to match a file's extension. This is used to
+ * determine the file format.
+ */
+ public String extensionRegX;
+/**
+ * Codec used to load audio data from this file format.
+ */
+ public Class iCodecClass;
+/**
+ * Constructor: Converts the specified extension string into a regular
+ * expression, and associates that with the specified codec.
+ * @param extension File extension to be associated with the specified codec.
+ * @param iCodec Codec to use for files with the specified extension.
+ */
+ public Codec( String extension, Class iCodecClass )
+ {
+ extensionRegX = "";
+ // Make sure an extension was specified:
+ if( extension != null && extension.length() > 0 )
+ {
+ // We are only interested in the file extension. The filename
+ // can begin with whatever:
+ extensionRegX = ".*";
+ String c;
+ for( int x = 0; x < extension.length(); x++ )
+ {
+ // Each character could be either upper or lower case:
+ c = extension.substring( x, x + 1 );
+ extensionRegX += "[" + c.toLowerCase( Locale.ENGLISH )
+ + c.toUpperCase( Locale.ENGLISH ) + "]";
+ }
+ // The extension will be at the end of the filename:
+ extensionRegX += "$";
+ }
+ // remember the codec to use for this format:
+ this.iCodecClass = iCodecClass;
+ }
+
+ public ICodec getInstance()
+ {
+ if( iCodecClass == null )
+ return null;
+
+ Object o = null;
+ try
+ {
+ o = iCodecClass.newInstance();
+ }
+ catch( InstantiationException ie )
+ {
+ instantiationErrorMessage();
+ return null;
+ }
+ catch( IllegalAccessException iae )
+ {
+ instantiationErrorMessage();
+ return null;
+ }
+ catch( ExceptionInInitializerError eiie )
+ {
+ instantiationErrorMessage();
+ return null;
+ }
+ catch( SecurityException se )
+ {
+ instantiationErrorMessage();
+ return null;
+ }
+
+
+ if( o == null )
+ {
+ instantiationErrorMessage();
+ return null;
+ }
+
+ return (ICodec) o;
+ }
+
+ private void instantiationErrorMessage()
+ {
+ errorMessage( "Unrecognized ICodec implementation in method " +
+ "'getInstance'. Ensure that the implementing " +
+ "class has one public, parameterless constructor." );
+ }
+ }
+// END PRIVATE INTERNAL CLASSES
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemException.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemException.java
new file mode 100644
index 0000000..9d22c81
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemException.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+/**
+ * The SoundSystemException class is used to provide information about serious
+ * errors.
+ *<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 SoundSystemException extends Exception
+{
+/**
+ * Global identifier for no problem.
+ */
+ public static final int ERROR_NONE = 0;
+/**
+ * Global identifier for a generic exception.
+ */
+ public static final int UNKNOWN_ERROR = 1;
+/**
+ * Global identifier for a null parameter.
+ */
+ public static final int NULL_PARAMETER = 2;
+/**
+ * Global identifier for a class type mismatch.
+ */
+ public static final int CLASS_TYPE_MISMATCH = 3;
+/**
+ * Global identifier for the sound library does not exist.
+ */
+ public static final int LIBRARY_NULL = 4;
+/**
+ * Global identifier for the sound library does not exist.
+ */
+ public static final int LIBRARY_TYPE = 5;
+
+/**
+ * Holds a global identifier indicating the type of exception.
+ */
+ private int myType = UNKNOWN_ERROR;
+
+/**
+ * Constructor: Generic exception. Specify the error message.
+ */
+ public SoundSystemException( String message )
+ {
+ super( message );
+ }
+
+/**
+ * Constructor: Specify the error message and type of exception.
+ * @param message Description of the problem.
+ * @param type Global identifier for type of exception.
+ */
+ public SoundSystemException( String message, int type )
+ {
+ super( message );
+ myType = type;
+ }
+
+ public int getType()
+ {
+ return myType;
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemLogger.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemLogger.java
new file mode 100644
index 0000000..dbb4a90
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/SoundSystemLogger.java
@@ -0,0 +1,182 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+/**
+ * The SoundSystemLogger class handles all status messages, warnings, and error
+ * messages for the SoundSystem library. This class can be extended and
+ * methods overriden to change how messages are handled. To do this, the
+ * overridden class should be instantiated, and a call should be made to method
+ * SoundSystemConfig.setLogger() BEFORE creating the SoundSystem object. If
+ * the setLogger() method is called after the SoundSystem has been created,
+ * there will be handles floating around to two different message loggers, and
+ * the results will be undesirable.
+ * See {@link com.ardor3d.audio.SoundSystemConfig SoundSystemConfig} for more
+ * information about changing default settings. If an alternate logger is not
+ * set by the user, then an instance of this base class will be automatically
+ * created by default when the SoundSystem class is instantiated.
+ *<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 SoundSystemLogger
+{
+/**
+ * Prints a message.
+ * @param message Message to print.
+ * @param indent Number of tabs to indent the message.
+ */
+ public void message( String message, int indent )
+ {
+ String messageText;
+ // Determine how many spaces to indent:
+ String spacer = "";
+ for( int x = 0; x < indent; x++ )
+ {
+ spacer += " ";
+ }
+ // indent the message:
+ messageText = spacer + message;
+
+ // Print the message:
+ System.out.println( messageText );
+ }
+
+/**
+ * Prints an important message.
+ * @param message Message to print.
+ * @param indent Number of tabs to indent the message.
+ */
+ public void importantMessage( String message, int indent )
+ {
+ String messageText;
+ // Determine how many spaces to indent:
+ String spacer = "";
+ for( int x = 0; x < indent; x++ )
+ {
+ spacer += " ";
+ }
+ // indent the message:
+ messageText = spacer + message;
+
+ // Print the message:
+ System.out.println( messageText );
+ }
+
+/**
+ * Prints the specified message if error is true.
+ * @param error True or False.
+ * @param classname Name of the class checking for an error.
+ * @param message Message to print if error is true.
+ * @param indent Number of tabs to indent the message.
+ * @return True if error is true.
+ */
+ public boolean errorCheck( boolean error, String classname, String message,
+ int indent )
+ {
+ if( error )
+ errorMessage( classname, message, indent );
+ return error;
+ }
+
+/**
+ * Prints the classname which generated the error, followed by the error
+ * message.
+ * @param classname Name of the class which generated the error.
+ * @param message The actual error message.
+ * @param indent Number of tabs to indent the message.
+*/
+ public void errorMessage( String classname, String message, int indent )
+ {
+ String headerLine, messageText;
+ // Determine how many spaces to indent:
+ String spacer = "";
+ for( int x = 0; x < indent; x++ )
+ {
+ spacer += " ";
+ }
+ // indent the header:
+ headerLine = spacer + "Error in class '" + classname + "'";
+ // indent the message one more than the header:
+ messageText = " " + spacer + message;
+
+ // Print the error message:
+ System.out.println( headerLine );
+ System.out.println( messageText );
+ }
+
+/**
+ * Prints an exception's error message followed by the stack trace.
+ * @param e Exception containing the information to print.
+ * @param indent Number of tabs to indent the message and stack trace.
+ */
+ public void printStackTrace( Exception e, int indent )
+ {
+ printExceptionMessage( e, indent );
+ importantMessage( "STACK TRACE:", indent );
+ if( e == null )
+ return;
+
+ StackTraceElement[] stack = e.getStackTrace();
+ if( stack == null )
+ return;
+
+ StackTraceElement line;
+ for( int x = 0; x < stack.length; x++ )
+ {
+ line = stack[x];
+ if( line != null )
+ message( line.toString(), indent + 1 );
+ }
+ }
+
+/**
+ * Prints an exception's error message.
+ * @param e Exception containing the message to print.
+ * @param indent Number of tabs to indent the message.
+ */
+ public void printExceptionMessage( Exception e, int indent )
+ {
+ importantMessage( "ERROR MESSAGE:", indent );
+ if( e.getMessage() == null )
+ message( "(none)", indent + 1 );
+ else
+ message( e.getMessage(), indent + 1 );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/Source.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/Source.java
new file mode 100644
index 0000000..09ac50d
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/Source.java
@@ -0,0 +1,1347 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import com.ardor3d.audio.sampled.AudioFormat;
+
+/**
+ * The Source class is used to store information about a source.
+ * Source objects are stored in a map in the Library class. The
+ * information they contain is used to create library-specific sources.
+ * This is the template class which is extended for each specific library.
+ * This class is also used by the "No Sound" library to represent a mute
+ * source.
+ *<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 Source
+{
+/**
+ * The library class associated with this type of channel.
+ */
+ protected Class libraryType = Library.class;
+
+/**
+ * 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.
+ */
+ private SoundSystemLogger logger;
+
+/**
+ * True if this source is being directly fed with raw audio data.
+ */
+ public boolean rawDataStream = false;
+
+/**
+ * Format the raw data will be in if this is a Raw Data Stream source.
+ */
+ public AudioFormat rawDataFormat = null;
+
+/**
+ * Determines whether a source should be removed after it finishes playing.
+ */
+ public boolean temporary = false;
+
+/**
+ * Determines whether or not this is a priority source. Priority sources will
+ * not be overwritten by other sources when there are no available channels.
+ */
+ public boolean priority = false;
+
+/**
+ * Whether or not this source should be streamed.
+ */
+ public boolean toStream = false;
+
+/**
+ * Whether this source should loop or only play once.
+ */
+ public boolean toLoop = false;
+
+/**
+ * Whether this source needs to be played (for example if it was playing and
+ * looping when it got culled).
+ */
+ public boolean toPlay = false;
+
+/**
+ * Unique name for this source. More than one source can not have the same
+ * sourcename.
+ */
+ public String sourcename = "";
+
+/**
+ * The audio file which this source should play.
+ */
+ public FilenameURL filenameURL = null;
+
+/**
+ * This source's position in 3D space.
+ */
+ public Vector3D position;
+
+/**
+ * Attenuation model to use for this source.
+ */
+ public int attModel = 0;
+
+/**
+ * Either fade distance or rolloff factor, depending on the value of attModel.
+ */
+ public float distOrRoll = 0.0f;
+
+/**
+ * Source's velocity in world-space, for use in Doppler effect.
+ */
+ public Vector3D velocity;
+
+/**
+ * This source's volume (a float between 0.0 - 1.0). This value is used
+ * internally for attenuation, and should not be used to manually change a
+ * source's volume.
+ */
+ public float gain = 1.0f;
+
+/**
+ * This value should be used to manually increase or decrease source volume.
+ */
+ public float sourceVolume = 1.0f;
+
+/**
+ * This value represents the source's pitch (float value between 0.5f - 2.0f).
+ */
+ protected float pitch = 1.0f;
+
+/**
+ * This source's distance from the listener.
+ */
+ public float distanceFromListener = 0.0f;
+
+/**
+ * Channel to play this source on.
+ */
+ public Channel channel = null;
+
+/**
+ * Holds the data used by normal sources.
+ */
+ public SoundBuffer soundBuffer = null;
+
+/**
+ * False when this source gets culled.
+ */
+ private boolean active = true;
+
+/**
+ * Whether or not this source has been stopped.
+ */
+ private boolean stopped = true;
+
+/**
+ * Whether or not this source has been paused.
+ */
+ private boolean paused = false;
+
+/**
+ * Codec used to read data for streaming sources.
+ */
+ protected ICodec codec = null;
+
+/**
+ * Codec used to read in some initial data from the next sound in the queue.
+ */
+ protected ICodec nextCodec = null;
+
+/**
+ * List of buffers to hold some initial data from the next sound in the queue.
+ */
+ protected LinkedList<SoundBuffer> nextBuffers = null;
+
+
+/**
+ * The list of files to stream when the current stream finishes.
+ */
+ protected LinkedList<FilenameURL> soundSequenceQueue = null;
+
+/**
+ * Ensures that only one thread accesses the soundSequenceQueue at a time.
+ */
+ protected final Object soundSequenceLock = new Object();
+
+/**
+ * Used by streaming sources to indicate whether or not the initial
+ * stream-buffers still need to be queued.
+ */
+ public boolean preLoad = false;
+
+/**
+ * Specifies the gain factor used for the fade-out effect, or -1 when
+ * source is not currently fading out.
+ */
+ protected float fadeOutGain = -1.0f;
+
+/**
+ * Specifies the gain factor used for the fade-in effect, or 1 when
+ * source is not currently fading in.
+ */
+ protected float fadeInGain = 1.0f;
+
+/**
+ * Specifies the number of miliseconds it should take to fade out.
+ */
+ protected long fadeOutMilis = 0;
+
+/**
+ * Specifies the number of miliseconds it should take to fade in.
+ */
+ protected long fadeInMilis = 0;
+
+/**
+ * System time in miliseconds when the last fade in/out volume check occurred.
+ */
+ protected long lastFadeCheck = 0;
+
+/**
+ * Constructor: Creates a new source using the specified parameters.
+ * @param priority Setting this to true will prevent other sounds from overriding this one.
+ * @param toStream Setting this to true will create a streaming source.
+ * @param toLoop Should this source loop, or play only once.
+ * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.
+ * @param filenameURL The filename/URL of the sound file to play at this source.
+ * @param soundBuffer Buffer containing audio data, or null if not loaded yet.
+ * @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 'att'.
+ * @param temporary Whether or not to remove this source after it finishes playing.
+ */
+ public Source( boolean priority, boolean toStream, boolean toLoop,
+ String sourcename, FilenameURL filenameURL,
+ SoundBuffer soundBuffer, float x, float y, float z,
+ int attModel, float distOrRoll, boolean temporary )
+ {
+ // grab a handle to the message logger:
+ logger = SoundSystemConfig.getLogger();
+
+ this.priority = priority;
+ this.toStream = toStream;
+ this.toLoop = toLoop;
+ this.sourcename = sourcename;
+ this.filenameURL = filenameURL;
+ this.soundBuffer = soundBuffer;
+ position = new Vector3D( x, y, z );
+ this.attModel = attModel;
+ this.distOrRoll = distOrRoll;
+ this.velocity = new Vector3D( 0, 0, 0 );
+ this.temporary = temporary;
+
+ if( toStream && filenameURL != null )
+ codec = SoundSystemConfig.getCodec( filenameURL.getFilename() );
+ }
+
+/**
+ * Constructor: Creates a new source matching the specified one.
+ * @param old Source to copy information from.
+ * @param soundBuffer Buffer containing audio data, or null if not loaded yet.
+ */
+ public Source( Source old, SoundBuffer soundBuffer )
+ {
+ // grab a handle to the message logger:
+ logger = SoundSystemConfig.getLogger();
+
+ priority = old.priority;
+ toStream = old.toStream;
+ toLoop = old.toLoop;
+ sourcename = old.sourcename;
+ filenameURL = old.filenameURL;
+ position = old.position.clone();
+ attModel = old.attModel;
+ distOrRoll = old.distOrRoll;
+ velocity = old.velocity.clone();
+ temporary = old.temporary;
+
+ sourceVolume = old.sourceVolume;
+
+ rawDataStream = old.rawDataStream;
+ rawDataFormat = old.rawDataFormat;
+
+ this.soundBuffer = soundBuffer;
+
+ if( toStream && filenameURL != null )
+ codec = SoundSystemConfig.getCodec( filenameURL.getFilename() );
+ }
+
+/**
+ * Constructor: Creates a new streaming source that will be directly fed with
+ * raw audio data.
+ * @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 'att'.
+ */
+ public Source( AudioFormat audioFormat, boolean priority, String sourcename,
+ float x, float y, float z, int attModel, float distOrRoll )
+ {
+ // grab a handle to the message logger:
+ logger = SoundSystemConfig.getLogger();
+
+ this.priority = priority;
+ this.toStream = true;
+ this.toLoop = false;
+ this.sourcename = sourcename;
+ this.filenameURL = null;
+ this.soundBuffer = null;
+ position = new Vector3D( x, y, z );
+ this.attModel = attModel;
+ this.distOrRoll = distOrRoll;
+ this.velocity = new Vector3D( 0, 0, 0 );
+ this.temporary = false;
+
+ rawDataStream = true;
+ rawDataFormat = audioFormat;
+ }
+/* Override methods */
+
+/**
+ * Shuts the source down and removes references to all instantiated objects.
+ */
+ public void cleanup()
+ {
+ if( codec != null )
+ codec.cleanup();
+
+ synchronized( soundSequenceLock )
+ {
+ if( soundSequenceQueue != null )
+ soundSequenceQueue.clear();
+ soundSequenceQueue = null;
+ }
+
+ sourcename = null;
+ filenameURL = null;
+ position = null;
+ soundBuffer = null;
+ codec = null;
+ }
+
+/**
+ * If this is a streaming source, queues up the next sound to play when
+ * the previous stream ends. This method has no effect on non-streaming
+ * sources.
+ * @param filenameURL The filename/URL of the sound file to stream next.
+ */
+ public void queueSound( FilenameURL filenameURL )
+ {
+ if( !toStream )
+ {
+ errorMessage( "Method 'queueSound' may only be used for " +
+ "streaming and MIDI sources." );
+ return;
+ }
+ if( filenameURL == null )
+ {
+ errorMessage( "File not specified in method 'queueSound'" );
+ return;
+ }
+
+ synchronized( soundSequenceLock )
+ {
+ if( soundSequenceQueue == null )
+ soundSequenceQueue = new LinkedList<FilenameURL>();
+ soundSequenceQueue.add( filenameURL );
+ }
+ }
+
+/**
+ * Removes the first occurrence of the specified filename from the list of
+ * sounds to play when the previous stream ends. This method has no effect
+ * on non-streaming sources.
+ * @param filename Filename/identifier of a sound file to remove from the queue.
+ */
+ public void dequeueSound( String filename )
+ {
+ if( !toStream )
+ {
+ errorMessage( "Method 'dequeueSound' may only be used for " +
+ "streaming and MIDI sources." );
+ return;
+ }
+ if( filename == null || filename.equals( "" ) )
+ {
+ errorMessage( "Filename not specified in method 'dequeueSound'" );
+ return;
+ }
+
+ synchronized( soundSequenceLock )
+ {
+ if( soundSequenceQueue != null )
+ {
+ ListIterator<FilenameURL> i = soundSequenceQueue.listIterator();
+ while( i.hasNext() )
+ {
+ if( i.next().getFilename().equals( filename ) )
+ {
+ i.remove();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+/**
+ * Fades out the volume of whatever this source is currently playing, then
+ * begins playing the specified filename at the source's previously assigned
+ * volume level. If the filename parameter is null or empty, the 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 list of
+ * queued sounds that would have played next when the current sound finished
+ * playing. This method has no effect on non-streaming sources.
+ * @param filenameURL Filename/URL of the sound file to play next, or null for none.
+ * @param milis Number of miliseconds the fadeout should take.
+ */
+ public void fadeOut( FilenameURL filenameURL, long milis )
+ {
+ if( !toStream )
+ {
+ errorMessage( "Method 'fadeOut' may only be used for " +
+ "streaming and MIDI sources." );
+ return;
+ }
+ if( milis < 0 )
+ {
+ errorMessage( "Miliseconds may not be negative in method " +
+ "'fadeOut'." );
+ return;
+ }
+
+ fadeOutMilis = milis;
+ fadeInMilis = 0;
+ fadeOutGain = 1.0f;
+ lastFadeCheck = System.currentTimeMillis();
+
+ synchronized( soundSequenceLock )
+ {
+ if( soundSequenceQueue != null )
+ soundSequenceQueue.clear();
+
+ if( filenameURL != null )
+ {
+ if( soundSequenceQueue == null )
+ soundSequenceQueue = new LinkedList<FilenameURL>();
+ soundSequenceQueue.add( filenameURL );
+ }
+ }
+ }
+
+/**
+ * Fades out the volume of whatever this 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 list of queued sounds that would have played next
+ * when the current sound finished playing. This method has no effect on
+ * non-streaming sources.
+ * @param filenameURL Filename/URL of the 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( FilenameURL filenameURL, long milisOut, long milisIn )
+ {
+ if( !toStream )
+ {
+ errorMessage( "Method 'fadeOutIn' may only be used for " +
+ "streaming and MIDI sources." );
+ return;
+ }
+ if( filenameURL == null )
+ {
+ errorMessage( "Filename/URL not specified in method 'fadeOutIn'." );
+ return;
+ }
+ if( milisOut < 0 || milisIn < 0 )
+ {
+ errorMessage( "Miliseconds may not be negative in method " +
+ "'fadeOutIn'." );
+ return;
+ }
+
+ fadeOutMilis = milisOut;
+ fadeInMilis = milisIn;
+
+ fadeOutGain = 1.0f;
+ lastFadeCheck = System.currentTimeMillis();
+
+ synchronized( soundSequenceLock )
+ {
+ if( soundSequenceQueue == null )
+ soundSequenceQueue = new LinkedList<FilenameURL>();
+ soundSequenceQueue.clear();
+ soundSequenceQueue.add( filenameURL );
+ }
+ }
+
+/**
+ * Resets this source's volume if it is fading out or in. Returns true if this
+ * source is currently in the process of fading out. When fade-out completes,
+ * this method transitions the source to the next sound in the sound sequence
+ * queue if there is one. This method has no effect on non-streaming sources.
+ * @return True if this source is in the process of fading out.
+ */
+ public boolean checkFadeOut()
+ {
+ if( !toStream )
+ return false;
+
+ if( fadeOutGain == -1.0f && fadeInGain == 1.0f )
+ return false;
+
+ long currentTime = System.currentTimeMillis();
+ long milisPast = currentTime - lastFadeCheck;
+ lastFadeCheck = currentTime;
+
+ if( fadeOutGain >= 0.0f )
+ {
+ if( fadeOutMilis == 0 )
+ {
+ fadeOutGain = -1.0f;
+ fadeInGain = 0.0f;
+ if( !incrementSoundSequence() )
+ {
+ stop();
+ }
+ positionChanged();
+ preLoad = true;
+ return false;
+ }
+ else
+ {
+ float fadeOutReduction = ((float)milisPast) / ((float)fadeOutMilis);
+ fadeOutGain -= fadeOutReduction;
+ if( fadeOutGain <= 0.0f )
+ {
+ fadeOutGain = -1.0f;
+ fadeInGain = 0.0f;
+ if( !incrementSoundSequence() )
+ stop();
+ positionChanged();
+ preLoad = true;
+ return false;
+ }
+ }
+ positionChanged();
+ return true;
+ }
+
+ if( fadeInGain < 1.0f )
+ {
+ fadeOutGain = -1.0f;
+ if( fadeInMilis == 0 )
+ {
+ fadeOutGain = -1.0f;
+ fadeInGain = 1.0f;
+ }
+ else
+ {
+ float fadeInIncrease = ((float)milisPast) / ((float)fadeInMilis);
+ fadeInGain += fadeInIncrease;
+ if( fadeInGain >= 1.0f )
+ {
+ fadeOutGain = -1.0f;
+ fadeInGain = 1.0f;
+ }
+ }
+ positionChanged();
+ return true;
+ }
+ return false;
+ }
+
+/**
+ * Removes the next filename/URL from the sound sequence queue and assigns it to
+ * this source. This method has no effect on non-streaming sources. This
+ * method is used internally by SoundSystem, and it is unlikely that the user
+ * will ever need to use it.
+ * @return True if there was something in the queue.
+ */
+ public boolean incrementSoundSequence()
+ {
+ if( !toStream )
+ {
+ errorMessage( "Method 'incrementSoundSequence' may only be used " +
+ "for streaming and MIDI sources." );
+ return false;
+ }
+
+ synchronized( soundSequenceLock )
+ {
+ if( soundSequenceQueue != null && soundSequenceQueue.size() > 0 )
+ {
+ filenameURL = soundSequenceQueue.remove( 0 );
+ if( codec != null )
+ codec.cleanup();
+ codec = SoundSystemConfig.getCodec( filenameURL.getFilename() );
+ return true;
+ }
+ }
+ return false;
+ }
+
+/**
+ * Reads in initial buffers of data from the next sound in the sound sequence
+ * queue, to reduce lag when the transition occurrs. This method has no effect
+ * on non-streaming sources. This method is used internally by SoundSystem, and
+ * it is unlikely that the user will ever need to use it.
+ * @return False if there is nothing in the queue to read from.
+ */
+ public boolean readBuffersFromNextSoundInSequence()
+ {
+ if( !toStream )
+ {
+ errorMessage( "Method 'readBuffersFromNextSoundInSequence' may " +
+ "only be used for streaming sources." );
+ return false;
+ }
+
+ synchronized( soundSequenceLock )
+ {
+ if( soundSequenceQueue != null && soundSequenceQueue.size() > 0 )
+ {
+ if( nextCodec != null )
+ nextCodec.cleanup();
+ nextCodec = SoundSystemConfig.getCodec(
+ soundSequenceQueue.get( 0 ).getFilename() );
+ nextCodec.initialize( soundSequenceQueue.get( 0 ).getURL() );
+
+ SoundBuffer buffer = null;
+ for( int i = 0;
+ i < SoundSystemConfig.getNumberStreamingBuffers()
+ && !nextCodec.endOfStream();
+ i++ )
+ {
+ buffer = nextCodec.read();
+ if( buffer != null )
+ {
+ if( nextBuffers == null )
+ nextBuffers = new LinkedList<SoundBuffer>();
+ nextBuffers.add( buffer );
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+/**
+ * Returns the size of the sound sequence queue (if this is a streaming source).
+ * @return Number of sounds left in the queue, or zero if none.
+ */
+ public int getSoundSequenceQueueSize()
+ {
+ if( soundSequenceQueue == null )
+ return 0;
+ return soundSequenceQueue.size();
+ }
+
+/**
+ * Sets whether or not this source should be removed when it finishes playing.
+ * @param tmp True or false.
+ */
+ public void setTemporary( boolean tmp )
+ {
+ temporary = tmp;
+ }
+
+/**
+ * Called every time the listener's position or orientation changes.
+ */
+ public void listenerMoved()
+ {}
+
+/**
+ * Moves the source to the specified position.
+ * @param x X coordinate to move to.
+ * @param y Y coordinate to move to.
+ * @param z Z coordinate to move to.
+ */
+ public void setPosition( float x, float y, float z )
+ {
+ position.x = x;
+ position.y = y;
+ position.z = z;
+ }
+
+/**
+ * Called every time the source changes position.
+ */
+ public void positionChanged()
+ {}
+
+/**
+ * Sets whether or not this source is a priority source. A priority source
+ * will not be overritten by another source if there are no channels available
+ * to play on.
+ * @param pri True or false.
+ */
+ public void setPriority( boolean pri )
+ {
+ priority = pri;
+ }
+
+/**
+ * Sets whether this source should loop or only play once.
+ * @param lp True or false.
+ */
+ public void setLooping( boolean lp )
+ {
+ toLoop = lp;
+ }
+
+/**
+ * Sets this source's attenuation model.
+ * @param model Attenuation model to use.
+ */
+ public void setAttenuation( int model )
+ {
+ attModel = model;
+ }
+
+/**
+ * Sets this source's fade distance or rolloff factor, depending on the
+ * attenuation model.
+ * @param dr New value for fade distance or rolloff factor.
+ */
+ public void setDistOrRoll( float dr)
+ {
+ distOrRoll = dr;
+ }
+
+/**
+ * Sets this source's velocity, for use in 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 setVelocity( float x, float y, float z )
+ {
+ this.velocity.x = x;
+ this.velocity.y = y;
+ this.velocity.z = z;
+ }
+
+/**
+ * Returns the source's distance from the listener.
+ * @return How far away the source is.
+ */
+ public float getDistanceFromListener()
+ {
+ return distanceFromListener;
+ }
+
+/**
+ * Manually sets the specified source's pitch.
+ * @param value A float value ( 0.5f - 2.0f ).
+ */
+ public void setPitch( float value )
+ {
+ float newPitch = value;
+ if( newPitch < 0.5f )
+ newPitch = 0.5f;
+ else if( newPitch > 2.0f )
+ newPitch = 2.0f;
+ pitch = newPitch;
+ }
+
+/**
+ * Returns the pitch of the specified source.
+ * @return Float value representing the source pitch (0.5f - 2.0f).
+ */
+ public float getPitch()
+ {
+ return pitch;
+ }
+
+/**
+ * Indicates whether or not this source's associated library requires some
+ * codecs to reverse-order the audio data they generate.
+ * @return True if audio data should be reverse-ordered.
+ */
+ public boolean reverseByteOrder()
+ {
+ return SoundSystemConfig.reverseByteOrder( libraryType );
+ }
+
+/**
+ * Changes the sources peripheral information to match the supplied parameters.
+ * @param priority Setting this to true will prevent other sounds from overriding this one.
+ * @param toStream Setting this to true will create a streaming source.
+ * @param toLoop Should this source loop, or play only once.
+ * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.
+ * @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 'att'.
+ * @param temporary Whether or not to remove this source after it finishes playing.
+ */
+ public void changeSource( boolean priority, boolean toStream,
+ boolean toLoop, String sourcename,
+ FilenameURL filenameURL, SoundBuffer soundBuffer,
+ float x, float y, float z, int attModel,
+ float distOrRoll, boolean temporary )
+ {
+ this.priority = priority;
+ this.toStream = toStream;
+ this.toLoop = toLoop;
+ this.sourcename = sourcename;
+ this.filenameURL = filenameURL;
+ this.soundBuffer = soundBuffer;
+ position.x = x;
+ position.y = y;
+ position.z = z;
+ this.attModel = attModel;
+ this.distOrRoll = distOrRoll;
+ this.temporary = temporary;
+ }
+
+/**
+ * Feeds raw data to the specified channel.
+ * @param buffer Byte buffer containing raw audio data to stream.
+ * @param c Channel to stream on.
+ * @return Number of prior buffers that have been processed, or -1 if unable to queue the buffer (if the source was culled, for example).
+ */
+ public int feedRawAudioData( Channel c, byte[] buffer )
+ {
+ if( !active( GET, XXX) )
+ {
+ toPlay = true;
+ return -1;
+ }
+ if( channel != c )
+ {
+ channel = c;
+ channel.close();
+ channel.setAudioFormat( rawDataFormat );
+ positionChanged();
+ }
+
+ // change the state of this source to not stopped and not paused:
+ stopped( SET, false );
+ paused( SET, false );
+
+ return channel.feedRawAudioData( buffer );
+ }
+
+/**
+ * Plays the source on the specified channel.
+ * @param c Channel to play on.
+ */
+ public void play( Channel c )
+ {
+ if( !active( GET, XXX) )
+ {
+ if( toLoop )
+ toPlay = true;
+ return;
+ }
+ if( channel != c )
+ {
+ channel = c;
+ channel.close();
+ }
+ // change the state of this source to not stopped and not paused:
+ stopped( SET, false );
+ paused( SET, false );
+ }
+/* END Override methods */
+
+/**
+ * Streams the source on its current channel
+ * @return False when stream has finished playing.
+ */
+ public boolean stream()
+ {
+ if( channel == null )
+ return false;
+
+ if( preLoad )
+ {
+ if( rawDataStream )
+ preLoad = false;
+ else
+ return preLoad();
+ }
+
+ if( rawDataStream )
+ {
+ if( stopped() || paused() )
+ return true;
+ if( channel.buffersProcessed() > 0 )
+ channel.processBuffer();
+ return true;
+ }
+ else
+ {
+ if( codec == null )
+ return false;
+ if( stopped() )
+ return false;
+ if( paused() )
+ return true;
+
+ int processed = channel.buffersProcessed();
+
+ SoundBuffer buffer = null;
+ for( int i = 0; i < processed; i++ )
+ {
+ buffer = codec.read();
+ if( buffer != null )
+ {
+ if( buffer.audioData != null )
+ channel.queueBuffer( buffer.audioData );
+ buffer.cleanup();
+ buffer = null;
+ return true;
+ }
+ else if( codec.endOfStream() )
+ {
+ synchronized( soundSequenceLock )
+ {
+ if( SoundSystemConfig.getStreamQueueFormatsMatch() )
+ {
+ if( soundSequenceQueue != null &&
+ soundSequenceQueue.size() > 0 )
+ {
+ if( codec != null )
+ codec.cleanup();
+ filenameURL = soundSequenceQueue.remove( 0 );
+ codec = SoundSystemConfig.getCodec(
+ filenameURL.getFilename() );
+ codec.initialize( filenameURL.getURL() );
+ buffer = codec.read();
+ if( buffer != null )
+ {
+ if( buffer.audioData != null )
+ channel.queueBuffer( buffer.audioData );
+ buffer.cleanup();
+ buffer = null;
+ return true;
+ }
+ }
+ else if( toLoop )
+ {
+ codec.initialize( filenameURL.getURL() );
+ buffer = codec.read();
+ if( buffer != null )
+ {
+ if( buffer.audioData != null )
+ channel.queueBuffer( buffer.audioData );
+ buffer.cleanup();
+ buffer = null;
+ return true;
+ }
+ }
+ }
+ }
+ }
+/*
+ if( codec.endOfStream() )
+ {
+ synchronized( soundSequenceLock )
+ {
+ if( SoundSystemConfig.getStreamQueueFormatsMatch() )
+ {
+ if( soundSequenceQueue != null &&
+ soundSequenceQueue.size() > 0 )
+ {
+ if( codec != null )
+ codec.cleanup();
+ filenameURL = soundSequenceQueue.remove( 0 );
+ codec = SoundSystemConfig.getCodec(
+ filenameURL.getFilename() );
+ codec.initialize( filenameURL.getURL() );
+ return true;
+ }
+ else if( toLoop )
+ {
+ codec.initialize( filenameURL.getURL() );
+ buffer = codec.read();
+ if( buffer != null )
+ {
+ if( buffer.audioData != null )
+ channel.queueBuffer( buffer.audioData );
+ buffer.cleanup();
+ buffer = null;
+ }
+ }
+ }
+ }
+ return false;
+ }
+*/
+ }
+ }
+ return false;
+ }
+
+/**
+ * Queues up the initial stream-buffers for the stream.
+ * @return False if the end of the stream was reached.
+ */
+ public boolean preLoad()
+ {
+ if( channel == null )
+ return false;
+
+ if( codec == null )
+ return false;
+
+ SoundBuffer buffer = null;
+
+ boolean noNextBuffers = false;
+ synchronized( soundSequenceLock )
+ {
+ if( nextBuffers == null || nextBuffers.isEmpty() )
+ noNextBuffers = true;
+ }
+
+ if( nextCodec != null && !noNextBuffers )
+ {
+ codec = nextCodec;
+ nextCodec = null;
+ synchronized( soundSequenceLock )
+ {
+ while( !nextBuffers.isEmpty() )
+ {
+ buffer = nextBuffers.remove( 0 );
+ if( buffer != null )
+ {
+ if( buffer.audioData != null )
+ channel.queueBuffer( buffer.audioData );
+ buffer.cleanup();
+ buffer = null;
+ }
+ }
+ }
+ }
+ else
+ {
+ nextCodec = null;
+ URL url = filenameURL.getURL();
+
+ codec.initialize( url );
+ for( int i = 0; i < SoundSystemConfig.getNumberStreamingBuffers();
+ i++ )
+ {
+ buffer = codec.read();
+ if( buffer != null )
+ {
+ if( buffer.audioData != null )
+ channel.queueBuffer( buffer.audioData );
+ buffer.cleanup();
+ buffer = null;
+ }
+ }
+ }
+
+ return true;
+ }
+
+/**
+ * Pauses the source.
+ */
+ public void pause()
+ {
+ toPlay = false;
+ paused( SET, true );
+ if( channel != null )
+ channel.pause();
+ else
+ errorMessage( "Channel null in method 'pause'" );
+ }
+
+/**
+ * Stops the source.
+ */
+ public void stop()
+ {
+ toPlay = false;
+ stopped( SET, true );
+ paused( SET, false );
+ if( channel != null )
+ channel.stop();
+ else
+ errorMessage( "Channel null in method 'stop'" );
+ }
+
+/**
+ * Rewinds the source. If the source was paused, then it is stopped.
+ */
+ public void rewind()
+ {
+ if( paused( GET, XXX ) )
+ {
+ stop();
+ }
+ if( channel != null )
+ {
+ boolean rePlay = playing();
+ channel.rewind();
+ if( toStream && rePlay )
+ {
+ stop();
+ play( channel );
+ }
+ }
+ else
+ errorMessage( "Channel null in method 'rewind'" );
+ }
+
+/**
+ * Dequeues any previously queued data.
+ */
+ public void flush()
+ {
+ if( channel != null )
+ channel.flush();
+ else
+ errorMessage( "Channel null in method 'flush'" );
+ }
+
+/**
+ * Stops and flushes the source, and prevents it from being played again until
+ * the activate() is called.
+ */
+ public void cull()
+ {
+ if( !active( GET, XXX ) )
+ return;
+ if( playing() && toLoop )
+ toPlay = true;
+ if( rawDataStream )
+ toPlay = true;
+ active( SET, false );
+ if( channel != null )
+ channel.close();
+ channel = null;
+ }
+
+/**
+ * Allows a previously culled source to be played again.
+ */
+ public void activate()
+ {
+ active( SET, true );
+ }
+
+/**
+ * Returns false if the source has been culled.
+ * @return True or False
+ */
+ public boolean active()
+ {
+ return active( GET, XXX );
+ }
+
+/**
+ * Returns true if the source is playing.
+ * @return True or False
+ */
+ public boolean playing()
+ {
+ if( channel == null || channel.attachedSource != this )
+ return false;
+ else if( paused() || stopped() )
+ return false;
+ else
+ return channel.playing();
+ }
+
+/**
+ * Returns true if the source has been stopped.
+ * @return True or False
+ */
+ public boolean stopped()
+ {
+ return stopped( GET, XXX );
+ }
+
+/**
+ * Returns true if the source has been paused.
+ * @return True or False
+ */
+ public boolean paused()
+ {
+ return paused( GET, XXX );
+ }
+
+/**
+ * Returns the number of miliseconds since the source began playing.
+ * @return miliseconds, or -1 if not playing or unable to calculate
+ */
+ public float millisecondsPlayed()
+ {
+ if( channel == null )
+ return( -1 );
+ else
+ return channel.millisecondsPlayed();
+ }
+
+/**
+ * Sets or returns whether or not the source has been culled.
+ * @return True or False
+ */
+ private synchronized boolean active( boolean action, boolean value )
+ {
+ if( action == SET )
+ active = value;
+ return active;
+ }
+
+/**
+ * Sets or returns whether or not the source has been stopped.
+ * @return True or False
+ */
+ private synchronized boolean stopped( boolean action, boolean value )
+ {
+ if( action == SET )
+ stopped = value;
+ return stopped;
+ }
+
+/**
+ * Sets or returns whether or not the source has been paused.
+ * @return True or False
+ */
+ private synchronized boolean paused( boolean action, boolean value )
+ {
+ if( action == SET )
+ paused = value;
+ return paused;
+ }
+
+/**
+ * Returns the name of the class.
+ * @return SoundLibraryXXXX.
+ */
+ public String getClassName()
+ {
+ String libTitle = SoundSystemConfig.getLibraryTitle( libraryType );
+
+ if( libTitle.equals( "No Sound" ) )
+ return "Source";
+ else
+ return "Source" + libTitle;
+ }
+/**
+ * Prints a message.
+ * @param message Message to print.
+ */
+ protected void message( String message )
+ {
+ logger.message( message, 0 );
+ }
+
+/**
+ * Prints an important message.
+ * @param message Message to print.
+ */
+ protected void importantMessage( String message )
+ {
+ logger.importantMessage( message, 0 );
+ }
+
+/**
+ * Prints the specified message if error is true.
+ * @param error True or False.
+ * @param message Message to print if error is true.
+ * @return True if error is true.
+ */
+ protected boolean errorCheck( boolean error, String message )
+ {
+ return logger.errorCheck( error, getClassName(), message, 0 );
+ }
+
+/**
+ * Prints an error message.
+ * @param message Message to print.
+ */
+ protected void errorMessage( String message )
+ {
+ logger.errorMessage( getClassName(), message, 0 );
+ }
+
+/**
+ * Prints an exception's error message followed by the stack trace.
+ * @param e Exception containing the information to print.
+ */
+ protected void printStackTrace( Exception e )
+ {
+ logger.printStackTrace( e, 1 );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/StreamThread.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/StreamThread.java
new file mode 100644
index 0000000..77b411a
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/StreamThread.java
@@ -0,0 +1,306 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * The StreamThread class is used to process all streaming sources. This
+ * thread starts out asleep, and it sleeps when all streaming sources are
+ * finished playing, so it is necessary to call interrupt() after adding new
+ * streaming sources to the list.
+ *<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 StreamThread extends SimpleThread
+{
+/**
+ * Processes status messages, warnings, and error messages.
+ */
+ private SoundSystemLogger logger;
+
+/**
+ * List of sources that are currently streaming.
+ */
+ private List<Source> streamingSources;
+
+/**
+ * Used to synchronize access to the streaming sources list.
+ */
+ private final Object listLock = new Object();
+
+/**
+ * Constructor: Grabs a handle to the message logger and instantiates the
+ * streaming sources list.
+ */
+ public StreamThread()
+ {
+ // grab a handle to the message logger:
+ logger = SoundSystemConfig.getLogger();
+
+ streamingSources = new LinkedList<Source>();
+ }
+
+/**
+ * Removes all references to instantiated objects, and changes the thread's
+ * state to "not alive". Method alive() returns false when the cleanup()
+ * method has completed.
+ */
+ @Override
+ protected void cleanup()
+ {
+ kill();
+ super.cleanup(); // Important!!
+ }
+
+/**
+ * The main loop for processing commands. The thread sleeps when it finishes
+ * processing commands, and it must be interrupted to process more.
+ */
+ @Override
+ public void run()
+ {
+ ListIterator<Source> iter;
+ Source src;
+
+ // Start out asleep:
+ snooze( 3600000 );
+
+ while( !dying() )
+ {
+ while( !dying() && !streamingSources.isEmpty() )
+ {
+ // Make sure noone else is accessing the list of sources:
+ synchronized( listLock )
+ {
+ iter = streamingSources.listIterator();
+ while( !dying() && iter.hasNext() )
+ {
+ src = iter.next();
+ if( src == null )
+ {
+ iter.remove();
+ }
+ else if( src.stopped() )
+ {
+ if( !src.rawDataStream )
+ iter.remove();
+ }
+ else if( !src.active() )
+ {
+ if( src.toLoop || src.rawDataStream )
+ src.toPlay = true;
+ iter.remove();
+ }
+ else if( !src.paused() )
+ {
+ src.checkFadeOut();
+ if( (!src.stream()) && (!src.rawDataStream) )
+ {
+ if( src.channel == null
+ || !src.channel.processBuffer() )
+ {
+ if( src.nextCodec == null )
+ {
+ src.readBuffersFromNextSoundInSequence();
+ }
+/*
+ if( src.getSoundSequenceQueueSize() > 0 )
+ {
+ src.incrementSoundSequence();
+ }
+
+ // check if this is a looping source
+ else*/ if( src.toLoop )
+ {
+ // wait for stream to finish playing
+ if( !src.playing() )
+ {
+ // Generate an EOS event:
+ SoundSystemConfig.notifyEOS(
+ src.sourcename,
+ src.getSoundSequenceQueueSize()
+ );
+ // Check if the source is currently
+ // in the process of fading out.
+ if( src.checkFadeOut() )
+ {
+ // Source is fading out.
+ // Keep looping until it
+ // finishes.
+ src.preLoad = true;
+ }
+ else
+ {
+ // Source is not fading out.
+ // If there is another sound in
+ // the sequence, switch to it
+ // before replaying.
+ src.incrementSoundSequence();
+ src.preLoad = true; // replay
+ }
+ }
+ }
+ else
+ {
+ // wait for stream to finish playing
+ if( !src.playing() )
+ {
+ // Generate an EOS event:
+ SoundSystemConfig.notifyEOS(
+ src.sourcename,
+ src.getSoundSequenceQueueSize()
+ );
+ // Check if the source is currently
+ // in the process of fading out
+ if( !src.checkFadeOut() )
+ {
+ // Source is not fading out.
+ // Play anything else that is
+ // in the sound sequence queue.
+ if(
+ src.incrementSoundSequence() )
+ src.preLoad = true;
+ else
+ iter.remove(); // finished
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !dying() && !streamingSources.isEmpty() )
+ snooze( 20 ); // sleep a bit so we don't peg the cpu
+ }
+ if( !dying() && streamingSources.isEmpty() )
+ snooze( 3600000 ); // sleep until there is more to do.
+ }
+
+ cleanup(); // Important!!
+ }
+
+/**
+ * Adds a new streaming source to the list. If another source in the list is
+ * already playing on the same channel, it is stopped and removed from the
+ * list.
+ * @param source New source to stream.
+ */
+ public void watch( Source source )
+ {
+ // make sure the source exists:
+ if( source == null )
+ return;
+
+ // make sure we aren't already watching this source:
+ if( streamingSources.contains( source ) )
+ return;
+
+ ListIterator<Source> iter;
+ Source src;
+
+ // Make sure noone else is accessing the list of sources:
+ synchronized( listLock )
+ {
+ // Any currently watched source which is null or playing on the
+ // same channel as the new source should be stopped and removed
+ // from the list.
+ iter = streamingSources.listIterator();
+ while( iter.hasNext() )
+ {
+ src = iter.next();
+ if( src == null )
+ {
+ iter.remove();
+ }
+ else if( source.channel == src.channel )
+ {
+ src.stop();
+ iter.remove();
+ }
+ }
+
+ // Add the new source to the list:
+ streamingSources.add( source );
+ }
+ }
+
+/**
+ * Prints a message.
+ * @param message Message to print.
+ */
+ private void message( String message )
+ {
+ logger.message( message, 0 );
+ }
+
+/**
+ * Prints an important message.
+ * @param message Message to print.
+ */
+ private void importantMessage( String message )
+ {
+ logger.importantMessage( message, 0 );
+ }
+
+/**
+ * Prints the specified message if error is true.
+ * @param error True or False.
+ * @param message Message to print if error is true.
+ * @return True if error is true.
+ */
+ private boolean errorCheck( boolean error, String message )
+ {
+ return logger.errorCheck( error, "StreamThread", message, 0 );
+ }
+
+/**
+ * Prints an error message.
+ * @param message Message to print.
+ */
+ private void errorMessage( String message )
+ {
+ logger.errorMessage( "StreamThread", message, 0 );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/Vector3D.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/Vector3D.java
new file mode 100644
index 0000000..de1e8c0
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/Vector3D.java
@@ -0,0 +1,219 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio;
+
+/**
+ * The Vector3D class contains methods to simplify common 3D vector functions,
+ * such as cross and dot product, normalize, etc.
+ *<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 Vector3D
+{
+
+/**
+ * The vector's X coordinate.
+ */
+ public float x;
+
+/**
+ * The vector's Y coordinate.
+ */
+ public float y;
+
+/**
+ * The vector's Z coordinate.
+ */
+ public float z;
+
+/**
+ * Constructor: Places the vector at the origin.
+ */
+ public Vector3D()
+ {
+ x = 0.0f;
+ y = 0.0f;
+ z = 0.0f;
+ }
+
+/**
+ * Constructor: Places the vector at the specified 3D coordinates.
+ * @param nx X coordinate for the new vector.
+ * @param ny Y coordinate for the new vector.
+ * @param nz Z coordinate for the new vector.
+ */
+ public Vector3D( float nx, float ny, float nz )
+ {
+ x = nx;
+ y = ny;
+ z = nz;
+ }
+
+/**
+ * Returns a new instance containing the same information as this one.
+ * @return A new Vector3D.
+ */
+ @Override
+ public Vector3D clone()
+ {
+ return new Vector3D( x, y, z );
+ }
+
+/**
+ * Returns a vector containing the cross-product: A cross B.
+ * @param A First vector in the cross product.
+ * @param B Second vector in the cross product.
+ * @return A new Vector3D.
+ */
+ public Vector3D cross( Vector3D A, Vector3D B )
+ {
+ return new Vector3D(
+ A.y * B.z - B.y * A.z,
+ A.z * B.x - B.z * A.x,
+ A.x * B.y - B.x * A.y );
+ }
+
+/**
+ * Returns a vector containing the cross-product: (this) cross B.
+ * @param B Second vector in the cross product.
+ * @return A new Vector3D.
+ */
+ public Vector3D cross( Vector3D B )
+ {
+ return new Vector3D(
+ y * B.z - B.y * z,
+ z * B.x - B.z * x,
+ x * B.y - B.x * y );
+
+ }
+
+/**
+ * Returns the dot-product result of: A dot B.
+ * @param A First vector in the dot product.
+ * @param B Second vector in the dot product.
+ * @return Dot product.
+ */
+ public float dot( Vector3D A, Vector3D B )
+ {
+ return( (A.x * B.x) + (A.y * B.y) + (A.z * B.z) );
+ }
+
+/**
+ * Returns the dot-product result of: (this) dot B.
+ * @param B Second vector in the dot product.
+ * @return Dot product.
+ */
+ public float dot( Vector3D B )
+ {
+ return( (x * B.x) + (y * B.y) + (z * B.z) );
+ }
+
+/**
+ * Returns the vector represented by: A + B.
+ * @param A First vector.
+ * @param B Vector to add to A.
+ * @return A new Vector3D.
+ */
+ public Vector3D add( Vector3D A, Vector3D B )
+ {
+ return new Vector3D( A.x + B.x, A.y + B.y, A.z + B.z );
+ }
+
+/**
+ * Returns the vector represented by: (this) + B.
+ * @param B Vector to add to this one.
+ * @return A new Vector3D.
+ */
+ public Vector3D add( Vector3D B )
+ {
+ return new Vector3D( x + B.x, y + B.y, z + B.z );
+ }
+
+/**
+ * Returns the vector represented by: A - B.
+ * @param A First vector.
+ * @param B Vector to subtract from A.
+ * @return A new Vector3D.
+ */
+ public Vector3D subtract( Vector3D A, Vector3D B )
+ {
+ return new Vector3D( A.x - B.x, A.y - B.y, A.z - B.z );
+ }
+
+/**
+ * Returns the vector represented by: (this) - B.
+ * @param B Vector to subtract from this one.
+ * @return A new Vector3D.
+ */
+ public Vector3D subtract( Vector3D B )
+ {
+ return new Vector3D( x - B.x, y - B.y, z - B.z );
+ }
+
+/**
+ * Returns the length of this vector.
+ * @return Length.
+ */
+ public float length()
+ {
+ return (float) Math.sqrt( x * x + y * y + z * z );
+ }
+
+/**
+ * Changes the length of this vector to 1.0.
+ */
+ public void normalize()
+ {
+ double t = Math.sqrt( x*x + y*y + z*z );
+ x = (float) (x / t);
+ y = (float) (y / t);
+ z = (float) (z / t);
+ }
+
+/**
+ * Returns a string depicting this vector.
+ * @return "Vector3D (x, y, z)".
+ */
+ @Override
+ public String toString()
+ {
+ return "Vector3D (" + x + ", " + y + ", " + z + ")";
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/codecs/CodecJOrbis.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/codecs/CodecJOrbis.java
new file mode 100644
index 0000000..5bfde5d
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/codecs/CodecJOrbis.java
@@ -0,0 +1,823 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+package com.ardor3d.audio.codecs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownServiceException;
+import com.ardor3d.audio.sampled.AudioFormat;
+
+// From the JOrbis library, http://www.jcraft.com/jorbis/
+import com.jcraft.jogg.Packet;
+import com.jcraft.jogg.Page;
+import com.jcraft.jogg.StreamState;
+import com.jcraft.jogg.SyncState;
+import com.jcraft.jorbis.DspState;
+import com.jcraft.jorbis.Block;
+import com.jcraft.jorbis.Comment;
+import com.jcraft.jorbis.Info;
+
+import com.ardor3d.audio.ICodec;
+import com.ardor3d.audio.SoundBuffer;
+import com.ardor3d.audio.SoundSystemConfig;
+import com.ardor3d.audio.SoundSystemLogger;
+
+/**
+ * The CodecJOrbis class provides an ICodec interface to the external JOrbis
+ * library.
+ *<b><i> SoundSystem CodecJOrbis Class License:</b></i><br><b><br>
+ * You are free to use this class for any purpose, commercial or otherwise.
+ * You may modify this class 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 class or any
+ * unmodified portion of it.
+ *<br>
+ * 2) You may not copyright this class 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 class 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 class or any portion
+ * of it.
+ *<br>
+ * 6) I the author do not guarantee, warrant, or make any representations,
+ * either expressed or implied, regarding the use of this class or any
+ * portion of it.
+ * <br><br>
+ * Author: Paul Lamb
+ * <br>
+ * http://www.paulscode.com
+ *</b><br><br>
+ *<b>
+ * This software is based on or using the JOrbis library available from
+ * http://www.jcraft.com/jorbis/
+ *</b><br><br>
+ *<br><b>
+ * <br><br>
+ * Author: Paul Lamb
+ * <br>
+ * http://www.paulscode.com
+ * </b><br><br>
+ */
+public class CodecJOrbis implements ICodec
+{
+/**
+ * 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;
+
+/**
+ * URL to the audio file to stream from.
+ */
+ private URL url;
+
+/**
+ * Used for connecting to the URL.
+ */
+ private URLConnection urlConnection = null;
+
+/**
+ * InputStream context for reading data from the file.
+ */
+ private InputStream inputStream;
+
+/**
+ * Format in which the converted audio data is stored.
+ */
+ private AudioFormat audioFormat;
+
+/**
+ * True if there is no more data to read in.
+ */
+ private boolean endOfStream = false;
+
+/**
+ * True if the stream has finished initializing.
+ */
+ private boolean initialized = false;
+
+/**
+ * Used to hold the data as it is read in.
+ */
+ private byte[] buffer = null;
+
+/**
+ * Amount of data to read in at one time.
+ */
+ private int bufferSize;
+
+/**
+ * Number of bytes read.
+ */
+ private int count = 0;
+
+/**
+ * Location within the data.
+ */
+ private int index = 0;
+
+/**
+ * Size of the data after it has been converted into pcm.
+ */
+ private int convertedBufferSize;
+
+/**
+ * Linear buffer to hold the data after it has been converted into pcm.
+ */
+ private byte[] convertedBuffer = null;
+
+/**
+ * Nonlinear pcm data.
+ */
+ private float[][][] pcmInfo;
+
+/**
+ * Location within the data.
+ */
+ private int[] pcmIndex;
+
+/**
+ * Data packet.
+ */
+ private Packet joggPacket = new Packet();
+/**
+ * Data Page.
+ */
+ private Page joggPage = new Page();
+/**
+ * Stream state.
+ */
+ private StreamState joggStreamState = new StreamState();
+/**
+ * Packet streaming layer.
+ */
+ private SyncState joggSyncState = new SyncState();
+/**
+ * Internal data storage.
+ */
+ private DspState jorbisDspState = new DspState();
+/**
+ * Block of stored JOrbis data.
+ */
+ private Block jorbisBlock = new Block(jorbisDspState);
+/**
+ * Comment fields.
+ */
+ private Comment jorbisComment = new Comment();
+/**
+ * Info about the data.
+ */
+ private Info jorbisInfo = new Info();
+
+/**
+ * Processes status messages, warnings, and error messages.
+ */
+ private SoundSystemLogger logger;
+
+/**
+ * Constructor: Grabs a handle to the logger.
+ */
+ public CodecJOrbis()
+ {
+ logger = SoundSystemConfig.getLogger();
+ }
+
+/**
+ * This method is ignored by CodecJOrbis, because it produces "nice" data.
+ * @param b True if the calling audio library requires byte-reversal from certain codecs
+ */
+ public void reverseByteOrder( boolean b )
+ {}
+
+/**
+ * Prepares an input stream to read from. If another stream is already opened,
+ * it will be closed and a new input stream opened in its place.
+ * @param url URL to an ogg file to stream from.
+ * @return False if an error occurred or if end of stream was reached.
+ */
+ public boolean initialize( URL url )
+ {
+ initialized( SET, false );
+
+ if( joggStreamState != null )
+ joggStreamState.clear();
+ if( jorbisBlock != null )
+ jorbisBlock.clear();
+ if( jorbisDspState != null )
+ jorbisDspState.clear();
+ if( jorbisInfo != null )
+ jorbisInfo.clear();
+ if( joggSyncState != null )
+ joggSyncState.clear();
+
+ if( inputStream != null )
+ {
+ try
+ {
+ inputStream.close();
+ }
+ catch( IOException ioe )
+ {}
+ }
+
+ this.url = url;
+ //this.bufferSize = SoundSystemConfig.getStreamingBufferSize() / 2;
+ this.bufferSize = 4096*2;
+
+ buffer = null;
+ count = 0;
+ index = 0;
+
+ joggStreamState = new StreamState();
+ jorbisBlock = new Block(jorbisDspState);
+ jorbisDspState = new DspState();
+ jorbisInfo = new Info();
+ joggSyncState = new SyncState();
+
+ try
+ {
+ urlConnection = url.openConnection();
+ }
+ catch( UnknownServiceException use )
+ {
+ errorMessage( "Unable to create a UrlConnection in method " +
+ "'initialize'." );
+ printStackTrace( use );
+ cleanup();
+ return false;
+ }
+ catch( IOException ioe )
+ {
+ errorMessage( "Unable to create a UrlConnection in method " +
+ "'initialize'." );
+ printStackTrace( ioe );
+ cleanup();
+ return false;
+ }
+ if( urlConnection != null )
+ {
+ try
+ {
+ inputStream = urlConnection.getInputStream();
+ }
+ catch( IOException ioe )
+ {
+ errorMessage( "Unable to acquire inputstream in method " +
+ "'initialize'." );
+ printStackTrace( ioe );
+ cleanup();
+ return false;
+ }
+ }
+
+ endOfStream( SET, false );
+
+ joggSyncState.init();
+ joggSyncState.buffer( bufferSize );
+ buffer = joggSyncState.data;
+
+ try
+ {
+ if( !readHeader() )
+ {
+ errorMessage( "Error reading the header" );
+ return false;
+ }
+ }
+ catch( IOException ioe )
+ {
+ errorMessage( "Error reading the header" );
+ return false;
+ }
+
+ convertedBufferSize = bufferSize * 2;
+
+ jorbisDspState.synthesis_init( jorbisInfo );
+ jorbisBlock.init( jorbisDspState );
+
+ int channels = jorbisInfo.channels;
+ int rate = jorbisInfo.rate;
+
+ audioFormat = new AudioFormat( (float) rate, 16, channels, true,
+ false );
+ pcmInfo = new float[1][][];
+ pcmIndex = new int[ jorbisInfo.channels ];
+
+ initialized( SET, true );
+
+ return true;
+ }
+
+/**
+ * Returns false if the stream is busy initializing.
+ * @return True if steam is initialized.
+ */
+ public boolean initialized()
+ {
+ return initialized( GET, XXX );
+ }
+
+/**
+ * Reads in one stream buffer worth of audio data. See
+ * {@link com.ardor3d.audio.SoundSystemConfig SoundSystemConfig} for more
+ * information about accessing and changing default settings.
+ * @return The audio data wrapped into a SoundBuffer context.
+ */
+ public SoundBuffer read()
+ {
+ byte[] returnBuffer = null;
+
+ while( !endOfStream( GET, XXX ) && ( returnBuffer == null ||
+ returnBuffer.length < SoundSystemConfig.getStreamingBufferSize() ) )
+ {
+ if( returnBuffer == null )
+ returnBuffer = readBytes();
+ else
+ returnBuffer = appendByteArrays( returnBuffer, readBytes() );
+ }
+
+ if( returnBuffer == null )
+ return null;
+
+ return new SoundBuffer( returnBuffer, audioFormat );
+ }
+
+/**
+ * Reads in all the audio data from the stream (up to the default
+ * "maximum file size". See
+ * {@link com.ardor3d.audio.SoundSystemConfig SoundSystemConfig} for more
+ * information about accessing and changing default settings.
+ * @return the audio data wrapped into a SoundBuffer context.
+ */
+ public SoundBuffer readAll()
+ {
+ byte[] returnBuffer = null;
+
+ while( !endOfStream( GET, XXX ) )
+ {
+ if( returnBuffer == null )
+ returnBuffer = readBytes();
+ else
+ returnBuffer = appendByteArrays( returnBuffer, readBytes() );
+ }
+
+ if( returnBuffer == null )
+ return null;
+
+ return new SoundBuffer( returnBuffer, audioFormat );
+ }
+
+/**
+ * Returns false if there is still more data available to be read in.
+ * @return True if end of stream was reached.
+ */
+ public boolean endOfStream()
+ {
+ return endOfStream( GET, XXX );
+ }
+
+/**
+ * Closes the input stream and remove references to all instantiated objects.
+ */
+ public void cleanup()
+ {
+ joggStreamState.clear();
+ jorbisBlock.clear();
+ jorbisDspState.clear();
+ jorbisInfo.clear();
+ joggSyncState.clear();
+
+ if( inputStream != null )
+ {
+ try
+ {
+ inputStream.close();
+ }
+ catch( IOException ioe )
+ {}
+ }
+
+ joggStreamState = null;
+ jorbisBlock = null;
+ jorbisDspState = null;
+ jorbisInfo = null;
+ joggSyncState = null;
+ inputStream = null;
+ }
+
+/**
+ * Returns the audio format of the data being returned by the read() and
+ * readAll() methods.
+ * @return Information wrapped into an AudioFormat context.
+ */
+ public AudioFormat getAudioFormat()
+ {
+ return audioFormat;
+ }
+
+/**
+ * Reads in the header information for the ogg file, which is contained in the
+ * first three packets of data.
+ * @return True if successful.
+ */
+ private boolean readHeader() throws IOException
+ {
+ // Update up JOrbis internal buffer:
+ index = joggSyncState.buffer( bufferSize );
+ // Read in a buffer of data:
+ int bytes = inputStream.read( joggSyncState.data, index, bufferSize );
+ if( bytes < 0 )
+ bytes = 0;
+ // Let JOrbis know how many bytes we got:
+ joggSyncState.wrote( bytes );
+
+ if( joggSyncState.pageout( joggPage ) != 1 )
+ {
+ // Finished reading the entire file:
+ if( bytes < bufferSize )
+ return true;
+
+ errorMessage( "Ogg header not recognized in method 'readHeader'." );
+ return false;
+ }
+
+ // Initialize JOrbis:
+ joggStreamState.init( joggPage.serialno() );
+
+ jorbisInfo.init();
+ jorbisComment.init();
+ if( joggStreamState.pagein( joggPage ) < 0 )
+ {
+ errorMessage( "Problem with first Ogg header page in method " +
+ "'readHeader'." );
+ return false;
+ }
+
+ if( joggStreamState.packetout( joggPacket ) != 1 )
+ {
+ errorMessage( "Problem with first Ogg header packet in method " +
+ "'readHeader'." );
+ return false;
+ }
+
+ if( jorbisInfo.synthesis_headerin( jorbisComment, joggPacket ) < 0 )
+ {
+ errorMessage( "File does not contain Vorbis header in method " +
+ "'readHeader'." );
+ return false;
+ }
+
+ int i = 0;
+ while( i < 2 )
+ {
+ while( i < 2 )
+ {
+ int result = joggSyncState.pageout( joggPage );
+ if( result == 0 )
+ break;
+ if( result == 1 )
+ {
+ joggStreamState.pagein( joggPage );
+ while( i < 2 )
+ {
+ result = joggStreamState.packetout( joggPacket );
+ if( result == 0 )
+ break;
+
+ if( result == -1 )
+ {
+ errorMessage( "Secondary Ogg header corrupt in " +
+ "method 'readHeader'." );
+ return false;
+ }
+
+ jorbisInfo.synthesis_headerin( jorbisComment,
+ joggPacket );
+ i++;
+ }
+ }
+ }
+ index = joggSyncState.buffer( bufferSize );
+ bytes = inputStream.read( joggSyncState.data, index, bufferSize );
+ if( bytes < 0 )
+ bytes = 0;
+ if( bytes == 0 && i < 2 )
+ {
+ errorMessage( "End of file reached before finished reading" +
+ "Ogg header in method 'readHeader'" );
+ return false;
+ }
+
+ joggSyncState.wrote( bytes );
+ }
+
+ index = joggSyncState.buffer( bufferSize );
+ buffer = joggSyncState.data;
+
+ return true;
+ }
+
+/**
+ * Reads and decodes a chunk of data of length "convertedBufferSize".
+ * @return Array containing the converted audio data.
+ */
+ private byte[] readBytes()
+ {
+ if( !initialized( GET, XXX ) )
+ return null;
+
+ if( endOfStream( GET, XXX ) )
+ return null;
+
+ if( convertedBuffer == null )
+ convertedBuffer = new byte[ convertedBufferSize ];
+ byte[] returnBuffer = null;
+
+ float[][] pcmf;
+ int samples, bout, ptr, mono, val, i, j;
+
+ switch( joggSyncState.pageout( joggPage ) )
+ {
+ case( 0 ):
+ case( -1 ):
+ break;
+ default:
+ {
+ joggStreamState.pagein( joggPage );
+ if( joggPage.granulepos() == 0 )
+ {
+ endOfStream( SET, true );
+ return null;
+ }
+
+ processPackets: while( true )
+ {
+ switch( joggStreamState.packetout( joggPacket ) )
+ {
+ case( 0 ):
+ break processPackets;
+ case( -1 ):
+ break;
+ default:
+ {
+ if( jorbisBlock.synthesis( joggPacket ) == 0 )
+ jorbisDspState.synthesis_blockin( jorbisBlock );
+
+ while( ( samples=jorbisDspState.synthesis_pcmout(
+ pcmInfo, pcmIndex ) ) > 0 )
+ {
+ pcmf = pcmInfo[0];
+ bout = ( samples < convertedBufferSize ?
+ samples : convertedBufferSize );
+ for( i = 0; i < jorbisInfo.channels; i++ )
+ {
+ ptr = i * 2;
+ mono = pcmIndex[i];
+ for( j = 0; j < bout; j++ )
+ {
+ val = (int) ( pcmf[i][mono + j] *
+ 32767. );
+ if( val > 32767 )
+ val = 32767;
+ if( val < -32768 )
+ val = -32768;
+ if( val < 0 )
+ val = val | 0x8000;
+ convertedBuffer[ptr] = (byte) (val);
+ convertedBuffer[ptr+1] =
+ (byte) (val>>>8);
+ ptr += 2 * (jorbisInfo.channels);
+ }
+ }
+ jorbisDspState.synthesis_read( bout );
+
+ returnBuffer = appendByteArrays( returnBuffer,
+ convertedBuffer,
+ 2 * jorbisInfo.channels * bout );
+ }
+ }
+ }
+ }
+
+ if( joggPage.eos() != 0 )
+ endOfStream( SET, true );
+ }
+ }
+
+ if( !endOfStream( GET, XXX ) )
+ {
+ index = joggSyncState.buffer( bufferSize );
+ buffer = joggSyncState.data;
+ try
+ {
+ count = inputStream.read( buffer, index, bufferSize );
+ }
+ catch( Exception e )
+ {
+ printStackTrace( e );
+ return null;
+ }
+ if( count == -1 )
+ return returnBuffer;
+
+ joggSyncState.wrote( count );
+ if( count==0 )
+ endOfStream( SET, true );
+ }
+
+ return returnBuffer;
+ }
+
+/**
+ * Internal method for synchronizing access to the boolean 'initialized'.
+ * @param action GET or SET.
+ * @param value New value if action == SET, or XXX if action == GET.
+ * @return True if steam is initialized.
+ */
+ private synchronized boolean initialized( boolean action, boolean value )
+ {
+ if( action == SET )
+ initialized = value;
+ return initialized;
+ }
+
+/**
+ * Internal method for synchronizing access to the boolean 'endOfStream'.
+ * @param action GET or SET.
+ * @param value New value if action == SET, or XXX if action == GET.
+ * @return True if end of stream was reached.
+ */
+ private synchronized boolean endOfStream( boolean action, boolean value )
+ {
+ if( action == SET )
+ endOfStream = value;
+ return endOfStream;
+ }
+
+/**
+ * Trims down the size of the array if it is larger than the specified
+ * maximum length.
+ * @param array Array containing audio data.
+ * @param maxLength Maximum size this array may be.
+ * @return New array.
+ */
+ private static byte[] trimArray( byte[] array, int maxLength )
+ {
+ byte[] trimmedArray = null;
+ if( array != null && array.length > maxLength )
+ {
+ trimmedArray = new byte[maxLength];
+ System.arraycopy( array, 0, trimmedArray, 0, maxLength );
+ }
+ return trimmedArray;
+ }
+
+/**
+ * Creates a new array with the second array appended to the end of the first
+ * array.
+ * @param arrayOne The first array.
+ * @param arrayTwo The second array.
+ * @param arrayTwoBytes The number of bytes to append from the second array.
+ * @return Byte array containing information from both arrays.
+ */
+ private static byte[] appendByteArrays( byte[] arrayOne, byte[] arrayTwo,
+ int arrayTwoBytes )
+ {
+ byte[] newArray;
+ int bytes = arrayTwoBytes;
+
+ // Make sure we aren't trying to append more than is there:
+ if( arrayTwo == null || arrayTwo.length == 0 )
+ bytes = 0;
+ else if( arrayTwo.length < arrayTwoBytes )
+ bytes = arrayTwo.length;
+
+ if( arrayOne == null && (arrayTwo == null || bytes <= 0) )
+ {
+ // no data, just return
+ return null;
+ }
+ else if( arrayOne == null )
+ {
+ // create the new array, same length as arrayTwo:
+ newArray = new byte[ bytes ];
+ // fill the new array with the contents of arrayTwo:
+ System.arraycopy( arrayTwo, 0, newArray, 0, bytes );
+ arrayTwo = null;
+ }
+ else if( arrayTwo == null || bytes <= 0 )
+ {
+ // create the new array, same length as arrayOne:
+ newArray = new byte[ arrayOne.length ];
+ // fill the new array with the contents of arrayOne:
+ System.arraycopy( arrayOne, 0, newArray, 0, arrayOne.length );
+ arrayOne = null;
+ }
+ else
+ {
+ // create the new array large enough to hold both arrays:
+ newArray = new byte[ arrayOne.length + bytes ];
+ System.arraycopy( arrayOne, 0, newArray, 0, arrayOne.length );
+ // fill the new array with the contents of both arrays:
+ System.arraycopy( arrayTwo, 0, newArray, arrayOne.length,
+ bytes );
+ arrayOne = null;
+ arrayTwo = null;
+ }
+
+ return newArray;
+ }
+
+/**
+ * Creates a new array with the second array appended to the end of the first
+ * array.
+ * @param arrayOne The first array.
+ * @param arrayTwo The second array.
+ * @return Byte array containing information from both arrays.
+ */
+ private static byte[] appendByteArrays( byte[] arrayOne, byte[] arrayTwo )
+ {
+ byte[] newArray;
+ if( arrayOne == null && arrayTwo == null )
+ {
+ // no data, just return
+ return null;
+ }
+ else if( arrayOne == null )
+ {
+ // create the new array, same length as arrayTwo:
+ newArray = new byte[ arrayTwo.length ];
+ // fill the new array with the contents of arrayTwo:
+ System.arraycopy( arrayTwo, 0, newArray, 0, arrayTwo.length );
+ arrayTwo = null;
+ }
+ else if( arrayTwo == null )
+ {
+ // create the new array, same length as arrayOne:
+ newArray = new byte[ arrayOne.length ];
+ // fill the new array with the contents of arrayOne:
+ System.arraycopy( arrayOne, 0, newArray, 0, arrayOne.length );
+ arrayOne = null;
+ }
+ else
+ {
+ // create the new array large enough to hold both arrays:
+ newArray = new byte[ arrayOne.length + arrayTwo.length ];
+ System.arraycopy( arrayOne, 0, newArray, 0, arrayOne.length );
+ // fill the new array with the contents of both arrays:
+ System.arraycopy( arrayTwo, 0, newArray, arrayOne.length,
+ arrayTwo.length );
+ arrayOne = null;
+ arrayTwo = null;
+ }
+
+ return newArray;
+ }
+
+/**
+ * Prints an error message.
+ * @param message Message to print.
+ */
+ private void errorMessage( String message )
+ {
+ logger.errorMessage( "CodecJOrbis", message, 0 );
+ }
+
+/**
+ * Prints an exception's error message followed by the stack trace.
+ * @param e Exception containing the information to print.
+ */
+ private void printStackTrace( Exception e )
+ {
+ logger.printStackTrace( e, 1 );
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/ardor3d/audio/sampled/AudioFormat.java b/ardor3d-audio/src/main/java/com/ardor3d/audio/sampled/AudioFormat.java
new file mode 100644
index 0000000..a2959ea
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/ardor3d/audio/sampled/AudioFormat.java
@@ -0,0 +1,623 @@
+/**
+ * Copyright (c) 2008-2014 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+/*
+ * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.ardor3d.audio.sampled;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * {@code AudioFormat} is the class that specifies a particular arrangement of
+ * data in a sound stream. By examining the information stored in the audio
+ * format, you can discover how to interpret the bits in the binary sound data.
+ * <p>
+ * Every data line has an audio format associated with its data stream. The
+ * audio format of a source (playback) data line indicates what kind of data the
+ * data line expects to receive for output. For a target (capture) data line,
+ * the audio format specifies the kind of the data that can be read from the
+ * line.
+ * <p>
+ * Sound files also have audio formats, of course. The {@link AudioFileFormat}
+ * class encapsulates an {@code AudioFormat} in addition to other, file-specific
+ * information. Similarly, an {@link AudioInputStream} has an
+ * {@code AudioFormat}.
+ * <p>
+ * The {@code AudioFormat} class accommodates a number of common sound-file
+ * encoding techniques, including pulse-code modulation (PCM), mu-law encoding,
+ * and a-law encoding. These encoding techniques are predefined, but service
+ * providers can create new encoding types. The encoding that a specific format
+ * uses is named by its {@code encoding} field.
+ * <p>
+ * In addition to the encoding, the audio format includes other properties that
+ * further specify the exact arrangement of the data. These include the number
+ * of channels, sample rate, sample size, byte order, frame rate, and frame
+ * size. Sounds may have different numbers of audio channels: one for mono, two
+ * for stereo. The sample rate measures how many "snapshots" (samples) of the
+ * sound pressure are taken per second, per channel. (If the sound is stereo
+ * rather than mono, two samples are actually measured at each instant of time:
+ * one for the left channel, and another for the right channel; however, the
+ * sample rate still measures the number per channel, so the rate is the same
+ * regardless of the number of channels. This is the standard use of the term.)
+ * The sample size indicates how many bits are used to store each snapshot; 8
+ * and 16 are typical values. For 16-bit samples (or any other sample size
+ * larger than a byte), byte order is important; the bytes in each sample are
+ * arranged in either the "little-endian" or "big-endian" style. For encodings
+ * like PCM, a frame consists of the set of samples for all channels at a given
+ * point in time, and so the size of a frame (in bytes) is always equal to the
+ * size of a sample (in bytes) times the number of channels. However, with some
+ * other sorts of encodings a frame can contain a bundle of compressed data for
+ * a whole series of samples, as well as additional, non-sample data. For such
+ * encodings, the sample rate and sample size refer to the data after it is
+ * decoded into PCM, and so they are completely different from the frame rate
+ * and frame size.
+ * <p>
+ * An {@code AudioFormat} object can include a set of properties. A property is
+ * a pair of key and value: the key is of type {@code String}, the associated
+ * property value is an arbitrary object. Properties specify additional format
+ * specifications, like the bit rate for compressed formats. Properties are
+ * mainly used as a means to transport additional information of the audio
+ * format to and from the service providers. Therefore, properties are ignored
+ * in the {@link #matches(AudioFormat)} method. However, methods which rely on
+ * the installed service providers, like
+ * {@link AudioSystem#isConversionSupported (AudioFormat, AudioFormat)
+ * isConversionSupported} may consider properties, depending on the respective
+ * service provider implementation.
+ * <p>
+ * The following table lists some common properties which service providers
+ * should use, if applicable:
+ *
+ * <table class="striped">
+ * <caption>Audio Format Properties</caption>
+ * <thead>
+ * <tr>
+ * <th scope="col">Property key
+ * <th scope="col">Value type
+ * <th scope="col">Description
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th scope="row">"bitrate"
+ * <td>{@link java.lang.Integer Integer}
+ * <td>average bit rate in bits per second
+ * <tr>
+ * <th scope="row">"vbr"
+ * <td>{@link java.lang.Boolean Boolean}
+ * <td>{@code true}, if the file is encoded in variable bit rate (VBR)
+ * <tr>
+ * <th scope="row">"quality"
+ * <td>{@link java.lang.Integer Integer}
+ * <td>encoding/conversion quality, 1..100
+ * </tbody>
+ * </table>
+ * <p>
+ * Vendors of service providers (plugins) are encouraged to seek information
+ * about other already established properties in third party plugins, and follow
+ * the same conventions.
+ *
+ * @author Kara Kytle
+ * @author Florian Bomers
+ * @see DataLine#getFormat
+ * @see AudioInputStream#getFormat
+ * @see AudioFileFormat
+ * @see javax.sound.sampled.spi.FormatConversionProvider
+ * @since 1.3
+ */
+public class AudioFormat {
+
+ /**
+ * Field copied from AudioSystem
+ */
+ public static final int NOT_SPECIFIED = -1;
+
+ /**
+ * The audio encoding technique used by this format.
+ */
+ protected Encoding encoding;
+
+ /**
+ * The number of samples played or recorded per second, for sounds that have
+ * this format.
+ */
+ protected float sampleRate;
+
+ /**
+ * The number of bits in each sample of a sound that has this format.
+ */
+ protected int sampleSizeInBits;
+
+ /**
+ * The number of audio channels in this format (1 for mono, 2 for stereo).
+ */
+ protected int channels;
+
+ /**
+ * The number of bytes in each frame of a sound that has this format.
+ */
+ protected int frameSize;
+
+ /**
+ * The number of frames played or recorded per second, for sounds that have
+ * this format.
+ */
+ protected float frameRate;
+
+ /**
+ * Indicates whether the audio data is stored in big-endian or little-endian
+ * order.
+ */
+ protected boolean bigEndian;
+
+ /**
+ * The set of properties.
+ */
+ private HashMap<String, Object> properties;
+
+ /**
+ * Constructs an {@code AudioFormat} with the given parameters. The encoding
+ * specifies the convention used to represent the data. The other parameters
+ * are further explained in the {@link AudioFormat class description}.
+ *
+ * @param encoding the audio encoding technique
+ * @param sampleRate the number of samples per second
+ * @param sampleSizeInBits the number of bits in each sample
+ * @param channels the number of channels (1 for mono, 2 for stereo, and so
+ * on)
+ * @param frameSize the number of bytes in each frame
+ * @param frameRate the number of frames per second
+ * @param bigEndian indicates whether the data for a single sample is
+ * stored in big-endian byte order ({@code false} means
+ * little-endian)
+ */
+ public AudioFormat(Encoding encoding, float sampleRate, int sampleSizeInBits,
+ int channels, int frameSize, float frameRate, boolean bigEndian) {
+
+ this.encoding = encoding;
+ this.sampleRate = sampleRate;
+ this.sampleSizeInBits = sampleSizeInBits;
+ this.channels = channels;
+ this.frameSize = frameSize;
+ this.frameRate = frameRate;
+ this.bigEndian = bigEndian;
+ this.properties = null;
+ }
+
+ /**
+ * Constructs an {@code AudioFormat} with the given parameters. The encoding
+ * specifies the convention used to represent the data. The other parameters
+ * are further explained in the {@link AudioFormat class description}.
+ *
+ * @param encoding the audio encoding technique
+ * @param sampleRate the number of samples per second
+ * @param sampleSizeInBits the number of bits in each sample
+ * @param channels the number of channels (1 for mono, 2 for stereo, and so
+ * on)
+ * @param frameSize the number of bytes in each frame
+ * @param frameRate the number of frames per second
+ * @param bigEndian indicates whether the data for a single sample is
+ * stored in big-endian byte order ({@code false} means
+ * little-endian)
+ * @param properties a {@code Map<String, Object>} object containing format
+ * properties
+ * @since 1.5
+ */
+ public AudioFormat(Encoding encoding, float sampleRate,
+ int sampleSizeInBits, int channels,
+ int frameSize, float frameRate,
+ boolean bigEndian, Map<String, Object> properties) {
+ this(encoding, sampleRate, sampleSizeInBits, channels,
+ frameSize, frameRate, bigEndian);
+ this.properties = new HashMap<>(properties);
+ }
+
+ /**
+ * Constructs an {@code AudioFormat} with a linear PCM encoding and the
+ * given parameters. The frame size is set to the number of bytes required
+ * to contain one sample from each channel, and the frame rate is set to the
+ * sample rate.
+ *
+ * @param sampleRate the number of samples per second
+ * @param sampleSizeInBits the number of bits in each sample
+ * @param channels the number of channels (1 for mono, 2 for stereo, and so
+ * on)
+ * @param signed indicates whether the data is signed or unsigned
+ * @param bigEndian indicates whether the data for a single sample is
+ * stored in big-endian byte order ({@code false} means
+ * little-endian)
+ */
+ public AudioFormat(float sampleRate, int sampleSizeInBits,
+ int channels, boolean signed, boolean bigEndian) {
+
+ this((signed == true ? Encoding.PCM_SIGNED : Encoding.PCM_UNSIGNED),
+ sampleRate,
+ sampleSizeInBits,
+ channels,
+ (channels == NOT_SPECIFIED || sampleSizeInBits == NOT_SPECIFIED)?
+ NOT_SPECIFIED:
+ ((sampleSizeInBits + 7) / 8) * channels,
+ sampleRate,
+ bigEndian);
+ }
+
+ /**
+ * Obtains the type of encoding for sounds in this format.
+ *
+ * @return the encoding type
+ * @see Encoding#PCM_SIGNED
+ * @see Encoding#PCM_UNSIGNED
+ * @see Encoding#ULAW
+ * @see Encoding#ALAW
+ */
+ public Encoding getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Obtains the sample rate. For compressed formats, the return value is the
+ * sample rate of the uncompressed audio data. When this {@code AudioFormat}
+ * is used for queries (e.g.
+ * {@link AudioSystem#isConversionSupported(AudioFormat, AudioFormat)
+ * AudioSystem.isConversionSupported}) or capabilities (e.g.
+ * {@link DataLine.Info#getFormats DataLine.Info.getFormats}), a sample rate
+ * of {@code AudioSystem.NOT_SPECIFIED} means that any sample rate is
+ * acceptable. {@code AudioSystem.NOT_SPECIFIED} is also returned when the
+ * sample rate is not defined for this audio format.
+ *
+ * @return the number of samples per second, or
+ * {@code AudioSystem.NOT_SPECIFIED}
+ * @see #getFrameRate()
+ * @see AudioSystem#NOT_SPECIFIED
+ */
+ public float getSampleRate() {
+ return sampleRate;
+ }
+
+ /**
+ * Obtains the size of a sample. For compressed formats, the return value is
+ * the sample size of the uncompressed audio data. When this
+ * {@code AudioFormat} is used for queries (e.g.
+ * {@link AudioSystem#isConversionSupported(AudioFormat,AudioFormat)
+ * AudioSystem.isConversionSupported}) or capabilities (e.g.
+ * {@link DataLine.Info#getFormats DataLine.Info.getFormats}), a sample size
+ * of {@code AudioSystem.NOT_SPECIFIED} means that any sample size is
+ * acceptable. {@code AudioSystem.NOT_SPECIFIED} is also returned when the
+ * sample size is not defined for this audio format.
+ *
+ * @return the number of bits in each sample, or
+ * {@code AudioSystem.NOT_SPECIFIED}
+ * @see #getFrameSize()
+ * @see AudioSystem#NOT_SPECIFIED
+ */
+ public int getSampleSizeInBits() {
+ return sampleSizeInBits;
+ }
+
+ /**
+ * Obtains the number of channels. When this {@code AudioFormat} is used for
+ * queries (e.g. {@link AudioSystem#isConversionSupported(AudioFormat,
+ * AudioFormat) AudioSystem.isConversionSupported}) or capabilities (e.g.
+ * {@link DataLine.Info#getFormats DataLine.Info.getFormats}), a return
+ * value of {@code AudioSystem.NOT_SPECIFIED} means that any (positive)
+ * number of channels is acceptable.
+ *
+ * @return The number of channels (1 for mono, 2 for stereo, etc.), or
+ * {@code AudioSystem.NOT_SPECIFIED}
+ * @see AudioSystem#NOT_SPECIFIED
+ */
+ public int getChannels() {
+ return channels;
+ }
+
+ /**
+ * Obtains the frame size in bytes. When this {@code AudioFormat} is used
+ * for queries (e.g. {@link AudioSystem#isConversionSupported(AudioFormat,
+ * AudioFormat) AudioSystem.isConversionSupported}) or capabilities (e.g.
+ * {@link DataLine.Info#getFormats DataLine.Info.getFormats}), a frame size
+ * of {@code AudioSystem.NOT_SPECIFIED} means that any frame size is
+ * acceptable. {@code AudioSystem.NOT_SPECIFIED} is also returned when the
+ * frame size is not defined for this audio format.
+ *
+ * @return the number of bytes per frame, or
+ * {@code AudioSystem.NOT_SPECIFIED}
+ * @see #getSampleSizeInBits()
+ * @see AudioSystem#NOT_SPECIFIED
+ */
+ public int getFrameSize() {
+ return frameSize;
+ }
+
+ /**
+ * Obtains the frame rate in frames per second. When this
+ * {@code AudioFormat} is used for queries (e.g.
+ * {@link AudioSystem#isConversionSupported(AudioFormat,AudioFormat)
+ * AudioSystem.isConversionSupported}) or capabilities (e.g.
+ * {@link DataLine.Info#getFormats DataLine.Info.getFormats}), a frame rate
+ * of {@code AudioSystem.NOT_SPECIFIED} means that any frame rate is
+ * acceptable. {@code AudioSystem.NOT_SPECIFIED} is also returned when the
+ * frame rate is not defined for this audio format.
+ *
+ * @return the number of frames per second, or
+ * {@code AudioSystem.NOT_SPECIFIED}
+ * @see #getSampleRate()
+ * @see AudioSystem#NOT_SPECIFIED
+ */
+ public float getFrameRate() {
+ return frameRate;
+ }
+
+ /**
+ * Indicates whether the audio data is stored in big-endian or little-endian
+ * byte order. If the sample size is not more than one byte, the return
+ * value is irrelevant.
+ *
+ * @return {@code true} if the data is stored in big-endian byte order,
+ * {@code false} if little-endian
+ */
+ public boolean isBigEndian() {
+ return bigEndian;
+ }
+
+ /**
+ * Obtain an unmodifiable map of properties. The concept of properties is
+ * further explained in the {@link AudioFileFormat class description}.
+ *
+ * @return a {@code Map<String, Object>} object containing all properties.
+ * If no properties are recognized, an empty map is returned.
+ * @see #getProperty(String)
+ * @since 1.5
+ */
+ @SuppressWarnings("unchecked") // Cast of result of clone.
+ public Map<String,Object> properties() {
+ Map<String,Object> ret;
+ if (properties == null) {
+ ret = new HashMap<>(0);
+ } else {
+ ret = (Map<String,Object>) (properties.clone());
+ }
+ return Collections.unmodifiableMap(ret);
+ }
+
+ /**
+ * Obtain the property value specified by the key. The concept of properties
+ * is further explained in the {@link AudioFileFormat class description}.
+ * <p>
+ * If the specified property is not defined for a particular file format,
+ * this method returns {@code null}.
+ *
+ * @param key the key of the desired property
+ * @return the value of the property with the specified key, or {@code null}
+ * if the property does not exist
+ * @see #properties()
+ * @since 1.5
+ */
+ public Object getProperty(String key) {
+ if (properties == null) {
+ return null;
+ }
+ return properties.get(key);
+ }
+
+ /**
+ * Indicates whether this format matches the one specified. To match, two
+ * formats must have the same encoding, and consistent values of the number
+ * of channels, sample rate, sample size, frame rate, and frame size. The
+ * values of the property are consistent if they are equal or the specified
+ * format has the property value {@code AudioSystem.NOT_SPECIFIED}. The byte
+ * order (big-endian or little-endian) must be the same if the sample size
+ * is greater than one byte.
+ *
+ * @param format format to test for match
+ * @return {@code true} if this format matches the one specified,
+ * {@code false} otherwise
+ */
+ public boolean matches(AudioFormat format) {
+ if (format.getEncoding().equals(getEncoding())
+ && (format.getChannels() == NOT_SPECIFIED
+ || format.getChannels() == getChannels())
+ && (format.getSampleRate() == (float)NOT_SPECIFIED
+ || format.getSampleRate() == getSampleRate())
+ && (format.getSampleSizeInBits() == NOT_SPECIFIED
+ || format.getSampleSizeInBits() == getSampleSizeInBits())
+ && (format.getFrameRate() == (float)NOT_SPECIFIED
+ || format.getFrameRate() == getFrameRate())
+ && (format.getFrameSize() == NOT_SPECIFIED
+ || format.getFrameSize() == getFrameSize())
+ && (getSampleSizeInBits() <= 8
+ || format.isBigEndian() == isBigEndian())) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a string that describes the audio format, such as: "PCM SIGNED
+ * 22050 Hz 16 bit mono big-endian". The contents of the string may vary
+ * between implementations of Java Sound.
+ *
+ * @return a string representation of the audio format
+ */
+ @Override
+ public String toString() {
+ String sampleRate = getSampleRate() == NOT_SPECIFIED ?
+ "unknown sample rate" : getSampleRate() + " Hz";
+
+ String sampleSize = getSampleSizeInBits() == NOT_SPECIFIED ?
+ "unknown bits per sample" : getSampleSizeInBits() + " bit";
+
+ String channels = switch (getChannels()) {
+ case 1 -> "mono";
+ case 2 -> "stereo";
+ case NOT_SPECIFIED -> "unknown number of channels";
+ default -> getChannels() + " channels";
+ };
+
+ String frameSize = getFrameSize() == NOT_SPECIFIED ?
+ "unknown frame size" : getFrameSize() + " bytes/frame";
+
+ String frameRate = "";
+ if (Math.abs(getSampleRate() - getFrameRate()) > 0.00001) {
+ frameRate = getFrameRate() == NOT_SPECIFIED ?
+ ", unknown frame rate":", " + getFrameRate() + " frames/second";
+ }
+
+ String bigEndian = "";
+ if ((getEncoding().equals(Encoding.PCM_SIGNED)
+ || getEncoding().equals(Encoding.PCM_UNSIGNED))
+ && ((getSampleSizeInBits() > 8)
+ || (getSampleSizeInBits() == NOT_SPECIFIED))) {
+ bigEndian = isBigEndian() ? ", big-endian" : ", little-endian";
+ }
+
+ return String.format("%s %s, %s, %s, %s%s%s", getEncoding(),
+ sampleRate, sampleSize, channels, frameSize,
+ frameRate, bigEndian);
+ }
+
+ /**
+ * The {@code Encoding} class names the specific type of data representation
+ * used for an audio stream. The encoding includes aspects of the sound
+ * format other than the number of channels, sample rate, sample size, frame
+ * rate, frame size, and byte order.
+ * <p>
+ * One ubiquitous type of audio encoding is pulse-code modulation (PCM),
+ * which is simply a linear (proportional) representation of the sound
+ * waveform. With PCM, the number stored in each sample is proportional to
+ * the instantaneous amplitude of the sound pressure at that point in time.
+ * The numbers may be signed or unsigned integers or floats. Besides PCM,
+ * other encodings include mu-law and a-law, which are nonlinear mappings of
+ * the sound amplitude that are often used for recording speech.
+ * <p>
+ * You can use a predefined encoding by referring to one of the static
+ * objects created by this class, such as {@code PCM_SIGNED} or
+ * {@code PCM_UNSIGNED}. Service providers can create new encodings, such as
+ * compressed audio formats, and make these available through the
+ * {@link AudioSystem} class.
+ * <p>
+ * The {@code Encoding} class is static, so that all {@code AudioFormat}
+ * objects that have the same encoding will refer to the same object (rather
+ * than different instances of the same class). This allows matches to be
+ * made by checking that two format's encodings are equal.
+ *
+ * @author Kara Kytle
+ * @see AudioFormat
+ * @see javax.sound.sampled.spi.FormatConversionProvider
+ * @since 1.3
+ */
+ public static class Encoding {
+
+ /**
+ * Specifies signed, linear PCM data.
+ */
+ public static final Encoding PCM_SIGNED = new Encoding("PCM_SIGNED");
+
+ /**
+ * Specifies unsigned, linear PCM data.
+ */
+ public static final Encoding PCM_UNSIGNED = new Encoding("PCM_UNSIGNED");
+
+ /**
+ * Specifies floating-point PCM data.
+ *
+ * @since 1.7
+ */
+ public static final Encoding PCM_FLOAT = new Encoding("PCM_FLOAT");
+
+ /**
+ * Specifies u-law encoded data.
+ */
+ public static final Encoding ULAW = new Encoding("ULAW");
+
+ /**
+ * Specifies a-law encoded data.
+ */
+ public static final Encoding ALAW = new Encoding("ALAW");
+
+ /**
+ * Encoding name.
+ */
+ private final String name;
+
+ /**
+ * Constructs a new encoding.
+ *
+ * @param name the name of the new type of encoding
+ */
+ public Encoding(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Indicates whether the specified object is equal to this encoding,
+ * returning {@code true} if the objects are equal.
+ *
+ * @param obj the reference object with which to compare
+ * @return {@code true} if the specified object is equal to this
+ * encoding; {@code false} otherwise
+ */
+ @Override
+ public final boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Encoding)) {
+ return false;
+ }
+ return Objects.equals(name, ((Encoding) obj).name);
+ }
+
+ /**
+ * Returns a hash code value for this encoding.
+ *
+ * @return a hash code value for this encoding
+ */
+ @Override
+ public final int hashCode() {
+ return name != null ? name.hashCode() : 0;
+ }
+
+ /**
+ * Returns encoding's name as the string representation of the encoding.
+ * For the predefined encodings, the name is similar to the encoding's
+ * variable (field) name. For example, {@code PCM_SIGNED.toString()}
+ * returns the name "PCM_SIGNED".
+ *
+ * @return a string representation of the encoding
+ */
+ @Override
+ public final String toString() {
+ return name;
+ }
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jogg/Buffer.java b/ardor3d-audio/src/main/java/com/jcraft/jogg/Buffer.java
new file mode 100644
index 0000000..1e5c92e
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jogg/Buffer.java
@@ -0,0 +1,294 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jogg;
+
+public class Buffer{
+ private static final int BUFFER_INCREMENT=256;
+
+ private static final int[] mask= {0x00000000, 0x00000001, 0x00000003,
+ 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
+ 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff,
+ 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
+ 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff,
+ 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff};
+
+ int ptr=0;
+ byte[] buffer=null;
+ int endbit=0;
+ int endbyte=0;
+ int storage=0;
+
+ public void writeinit(){
+ buffer=new byte[BUFFER_INCREMENT];
+ ptr=0;
+ buffer[0]=(byte)'\0';
+ storage=BUFFER_INCREMENT;
+ }
+
+ public void write(byte[] s){
+ for(int i=0; i<s.length; i++){
+ if(s[i]==0)
+ break;
+ write(s[i], 8);
+ }
+ }
+
+ public void read(byte[] s, int bytes){
+ int i=0;
+ while(bytes--!=0){
+ s[i++]=(byte)(read(8));
+ }
+ }
+
+ void reset(){
+ ptr=0;
+ buffer[0]=(byte)'\0';
+ endbit=endbyte=0;
+ }
+
+ public void writeclear(){
+ buffer=null;
+ }
+
+ public void readinit(byte[] buf, int bytes){
+ readinit(buf, 0, bytes);
+ }
+
+ public void readinit(byte[] buf, int start, int bytes){
+ ptr=start;
+ buffer=buf;
+ endbit=endbyte=0;
+ storage=bytes;
+ }
+
+ public void write(int value, int bits){
+ if(endbyte+4>=storage){
+ byte[] foo=new byte[storage+BUFFER_INCREMENT];
+ System.arraycopy(buffer, 0, foo, 0, storage);
+ buffer=foo;
+ storage+=BUFFER_INCREMENT;
+ }
+
+ value&=mask[bits];
+ bits+=endbit;
+ buffer[ptr]|=(byte)(value<<endbit);
+
+ if(bits>=8){
+ buffer[ptr+1]=(byte)(value>>>(8-endbit));
+ if(bits>=16){
+ buffer[ptr+2]=(byte)(value>>>(16-endbit));
+ if(bits>=24){
+ buffer[ptr+3]=(byte)(value>>>(24-endbit));
+ if(bits>=32){
+ if(endbit>0)
+ buffer[ptr+4]=(byte)(value>>>(32-endbit));
+ else
+ buffer[ptr+4]=0;
+ }
+ }
+ }
+ }
+
+ endbyte+=bits/8;
+ ptr+=bits/8;
+ endbit=bits&7;
+ }
+
+ public int look(int bits){
+ int ret;
+ int m=mask[bits];
+
+ bits+=endbit;
+
+ if(endbyte+4>=storage){
+ if(endbyte+(bits-1)/8>=storage)
+ return (-1);
+ }
+
+ ret=((buffer[ptr])&0xff)>>>endbit;
+ if(bits>8){
+ ret|=((buffer[ptr+1])&0xff)<<(8-endbit);
+ if(bits>16){
+ ret|=((buffer[ptr+2])&0xff)<<(16-endbit);
+ if(bits>24){
+ ret|=((buffer[ptr+3])&0xff)<<(24-endbit);
+ if(bits>32&&endbit!=0){
+ ret|=((buffer[ptr+4])&0xff)<<(32-endbit);
+ }
+ }
+ }
+ }
+ return (m&ret);
+ }
+
+ public int look1(){
+ if(endbyte>=storage)
+ return (-1);
+ return ((buffer[ptr]>>endbit)&1);
+ }
+
+ public void adv(int bits){
+ bits+=endbit;
+ ptr+=bits/8;
+ endbyte+=bits/8;
+ endbit=bits&7;
+ }
+
+ public void adv1(){
+ ++endbit;
+ if(endbit>7){
+ endbit=0;
+ ptr++;
+ endbyte++;
+ }
+ }
+
+ public int read(int bits){
+ int ret;
+ int m=mask[bits];
+
+ bits+=endbit;
+
+ if(endbyte+4>=storage){
+ ret=-1;
+ if(endbyte+(bits-1)/8>=storage){
+ ptr+=bits/8;
+ endbyte+=bits/8;
+ endbit=bits&7;
+ return (ret);
+ }
+ }
+
+ ret=((buffer[ptr])&0xff)>>>endbit;
+ if(bits>8){
+ ret|=((buffer[ptr+1])&0xff)<<(8-endbit);
+ if(bits>16){
+ ret|=((buffer[ptr+2])&0xff)<<(16-endbit);
+ if(bits>24){
+ ret|=((buffer[ptr+3])&0xff)<<(24-endbit);
+ if(bits>32&&endbit!=0){
+ ret|=((buffer[ptr+4])&0xff)<<(32-endbit);
+ }
+ }
+ }
+ }
+
+ ret&=m;
+
+ ptr+=bits/8;
+ endbyte+=bits/8;
+ endbit=bits&7;
+ return (ret);
+ }
+
+ public int readB(int bits){
+ int ret;
+ int m=32-bits;
+
+ bits+=endbit;
+
+ if(endbyte+4>=storage){
+ /* not the main path */
+ ret=-1;
+ if(endbyte*8+bits>storage*8){
+ ptr+=bits/8;
+ endbyte+=bits/8;
+ endbit=bits&7;
+ return (ret);
+ }
+ }
+
+ ret=(buffer[ptr]&0xff)<<(24+endbit);
+ if(bits>8){
+ ret|=(buffer[ptr+1]&0xff)<<(16+endbit);
+ if(bits>16){
+ ret|=(buffer[ptr+2]&0xff)<<(8+endbit);
+ if(bits>24){
+ ret|=(buffer[ptr+3]&0xff)<<(endbit);
+ if(bits>32&&(endbit!=0))
+ ret|=(buffer[ptr+4]&0xff)>>(8-endbit);
+ }
+ }
+ }
+ ret=(ret>>>(m>>1))>>>((m+1)>>1);
+
+ ptr+=bits/8;
+ endbyte+=bits/8;
+ endbit=bits&7;
+ return (ret);
+ }
+
+ public int read1(){
+ int ret;
+ if(endbyte>=storage){
+ ret=-1;
+ endbit++;
+ if(endbit>7){
+ endbit=0;
+ ptr++;
+ endbyte++;
+ }
+ return (ret);
+ }
+
+ ret=(buffer[ptr]>>endbit)&1;
+
+ endbit++;
+ if(endbit>7){
+ endbit=0;
+ ptr++;
+ endbyte++;
+ }
+ return (ret);
+ }
+
+ public int bytes(){
+ return (endbyte+(endbit+7)/8);
+ }
+
+ public int bits(){
+ return (endbyte*8+endbit);
+ }
+
+ public byte[] buffer(){
+ return (buffer);
+ }
+
+ public static int ilog(int v){
+ int ret=0;
+ while(v>0){
+ ret++;
+ v>>>=1;
+ }
+ return (ret);
+ }
+
+ public static void report(String in){
+ System.err.println(in);
+ System.exit(1);
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jogg/Packet.java b/ardor3d-audio/src/main/java/com/jcraft/jogg/Packet.java
new file mode 100644
index 0000000..7df75d1
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jogg/Packet.java
@@ -0,0 +1,47 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jogg;
+
+public class Packet{
+ public byte[] packet_base;
+ public int packet;
+ public int bytes;
+ public int b_o_s;
+ public int e_o_s;
+
+ public long granulepos;
+
+ /**
+ * sequence number for decode; the framing
+ * knows where there's a hole in the data,
+ * but we need coupling so that the codec
+ * (which is in a seperate abstraction
+ * layer) also knows about the gap
+ */
+ public long packetno;
+
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jogg/Page.java b/ardor3d-audio/src/main/java/com/jcraft/jogg/Page.java
new file mode 100644
index 0000000..dee59d6
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jogg/Page.java
@@ -0,0 +1,135 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jogg;
+
+public class Page{
+ private static int[] crc_lookup=new int[256];
+ static{
+ for(int i=0; i<crc_lookup.length; i++){
+ crc_lookup[i]=crc_entry(i);
+ }
+ }
+
+ private static int crc_entry(int index){
+ int r=index<<24;
+ for(int i=0; i<8; i++){
+ if((r&0x80000000)!=0){
+ r=(r<<1)^0x04c11db7; /* The same as the ethernet generator
+ polynomial, although we use an
+ unreflected alg and an init/final
+ of 0, not 0xffffffff */
+ }
+ else{
+ r<<=1;
+ }
+ }
+ return (r&0xffffffff);
+ }
+
+ public byte[] header_base;
+ public int header;
+ public int header_len;
+ public byte[] body_base;
+ public int body;
+ public int body_len;
+
+ int version(){
+ return header_base[header+4]&0xff;
+ }
+
+ int continued(){
+ return (header_base[header+5]&0x01);
+ }
+
+ public int bos(){
+ return (header_base[header+5]&0x02);
+ }
+
+ public int eos(){
+ return (header_base[header+5]&0x04);
+ }
+
+ public long granulepos(){
+ long foo=header_base[header+13]&0xff;
+ foo=(foo<<8)|(header_base[header+12]&0xff);
+ foo=(foo<<8)|(header_base[header+11]&0xff);
+ foo=(foo<<8)|(header_base[header+10]&0xff);
+ foo=(foo<<8)|(header_base[header+9]&0xff);
+ foo=(foo<<8)|(header_base[header+8]&0xff);
+ foo=(foo<<8)|(header_base[header+7]&0xff);
+ foo=(foo<<8)|(header_base[header+6]&0xff);
+ return (foo);
+ }
+
+ public int serialno(){
+ return (header_base[header+14]&0xff)|((header_base[header+15]&0xff)<<8)
+ |((header_base[header+16]&0xff)<<16)
+ |((header_base[header+17]&0xff)<<24);
+ }
+
+ int pageno(){
+ return (header_base[header+18]&0xff)|((header_base[header+19]&0xff)<<8)
+ |((header_base[header+20]&0xff)<<16)
+ |((header_base[header+21]&0xff)<<24);
+ }
+
+ void checksum(){
+ int crc_reg=0;
+
+ for(int i=0; i<header_len; i++){
+ crc_reg=(crc_reg<<8)
+ ^crc_lookup[((crc_reg>>>24)&0xff)^(header_base[header+i]&0xff)];
+ }
+ for(int i=0; i<body_len; i++){
+ crc_reg=(crc_reg<<8)
+ ^crc_lookup[((crc_reg>>>24)&0xff)^(body_base[body+i]&0xff)];
+ }
+ header_base[header+22]=(byte)crc_reg;
+ header_base[header+23]=(byte)(crc_reg>>>8);
+ header_base[header+24]=(byte)(crc_reg>>>16);
+ header_base[header+25]=(byte)(crc_reg>>>24);
+ }
+
+ public Page copy(){
+ return copy(new Page());
+ }
+
+ public Page copy(Page p){
+ byte[] tmp=new byte[header_len];
+ System.arraycopy(header_base, header, tmp, 0, header_len);
+ p.header_len=header_len;
+ p.header_base=tmp;
+ p.header=0;
+ tmp=new byte[body_len];
+ System.arraycopy(body_base, body, tmp, 0, body_len);
+ p.body_len=body_len;
+ p.body_base=tmp;
+ p.body=0;
+ return p;
+ }
+
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jogg/StreamState.java b/ardor3d-audio/src/main/java/com/jcraft/jogg/StreamState.java
new file mode 100644
index 0000000..2fb6447
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jogg/StreamState.java
@@ -0,0 +1,526 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jogg;
+
+public class StreamState{
+ byte[] body_data; /* bytes from packet bodies */
+ int body_storage; /* storage elements allocated */
+ int body_fill; /* elements stored; fill mark */
+ private int body_returned; /* elements of fill returned */
+
+ int[] lacing_vals; /* The values that will go to the segment table */
+ long[] granule_vals; /* pcm_pos values for headers. Not compact
+ this way, but it is simple coupled to the
+ lacing fifo */
+ int lacing_storage;
+ int lacing_fill;
+ int lacing_packet;
+ int lacing_returned;
+
+ byte[] header=new byte[282]; /* working space for header encode */
+ int header_fill;
+
+ public int e_o_s; /* set when we have buffered the last packet in the
+ logical bitstream */
+ int b_o_s; /* set after we've written the initial page
+ of a logical bitstream */
+ int serialno;
+ int pageno;
+ long packetno; /* sequence number for decode; the framing
+ knows where there's a hole in the data,
+ but we need coupling so that the codec
+ (which is in a seperate abstraction
+ layer) also knows about the gap */
+ long granulepos;
+
+ public StreamState(){
+ init();
+ }
+
+ StreamState(int serialno){
+ this();
+ init(serialno);
+ }
+
+ void init(){
+ body_storage=16*1024;
+ body_data=new byte[body_storage];
+ lacing_storage=1024;
+ lacing_vals=new int[lacing_storage];
+ granule_vals=new long[lacing_storage];
+ }
+
+ public void init(int serialno){
+ if(body_data==null){
+ init();
+ }
+ else{
+ for(int i=0; i<body_data.length; i++)
+ body_data[i]=0;
+ for(int i=0; i<lacing_vals.length; i++)
+ lacing_vals[i]=0;
+ for(int i=0; i<granule_vals.length; i++)
+ granule_vals[i]=0;
+ }
+ this.serialno=serialno;
+ }
+
+ public void clear(){
+ body_data=null;
+ lacing_vals=null;
+ granule_vals=null;
+ }
+
+ void destroy(){
+ clear();
+ }
+
+ void body_expand(int needed){
+ if(body_storage<=body_fill+needed){
+ body_storage+=(needed+1024);
+ byte[] foo=new byte[body_storage];
+ System.arraycopy(body_data, 0, foo, 0, body_data.length);
+ body_data=foo;
+ }
+ }
+
+ void lacing_expand(int needed){
+ if(lacing_storage<=lacing_fill+needed){
+ lacing_storage+=(needed+32);
+ int[] foo=new int[lacing_storage];
+ System.arraycopy(lacing_vals, 0, foo, 0, lacing_vals.length);
+ lacing_vals=foo;
+
+ long[] bar=new long[lacing_storage];
+ System.arraycopy(granule_vals, 0, bar, 0, granule_vals.length);
+ granule_vals=bar;
+ }
+ }
+
+ /* submit data to the internal buffer of the framing engine */
+ public int packetin(Packet op){
+ int lacing_val=op.bytes/255+1;
+
+ if(body_returned!=0){
+ /* advance packet data according to the body_returned pointer. We
+ had to keep it around to return a pointer into the buffer last
+ call */
+
+ body_fill-=body_returned;
+ if(body_fill!=0){
+ System.arraycopy(body_data, body_returned, body_data, 0, body_fill);
+ }
+ body_returned=0;
+ }
+
+ /* make sure we have the buffer storage */
+ body_expand(op.bytes);
+ lacing_expand(lacing_val);
+
+ /* Copy in the submitted packet. Yes, the copy is a waste; this is
+ the liability of overly clean abstraction for the time being. It
+ will actually be fairly easy to eliminate the extra copy in the
+ future */
+
+ System.arraycopy(op.packet_base, op.packet, body_data, body_fill, op.bytes);
+ body_fill+=op.bytes;
+
+ /* Store lacing vals for this packet */
+ int j;
+ for(j=0; j<lacing_val-1; j++){
+ lacing_vals[lacing_fill+j]=255;
+ granule_vals[lacing_fill+j]=granulepos;
+ }
+ lacing_vals[lacing_fill+j]=(op.bytes)%255;
+ granulepos=granule_vals[lacing_fill+j]=op.granulepos;
+
+ /* flag the first segment as the beginning of the packet */
+ lacing_vals[lacing_fill]|=0x100;
+
+ lacing_fill+=lacing_val;
+
+ /* for the sake of completeness */
+ packetno++;
+
+ if(op.e_o_s!=0)
+ e_o_s=1;
+ return (0);
+ }
+
+ public int packetout(Packet op){
+
+ /* The last part of decode. We have the stream broken into packet
+ segments. Now we need to group them into packets (or return the
+ out of sync markers) */
+
+ int ptr=lacing_returned;
+
+ if(lacing_packet<=ptr){
+ return (0);
+ }
+
+ if((lacing_vals[ptr]&0x400)!=0){
+ /* We lost sync here; let the app know */
+ lacing_returned++;
+
+ /* we need to tell the codec there's a gap; it might need to
+ handle previous packet dependencies. */
+ packetno++;
+ return (-1);
+ }
+
+ /* Gather the whole packet. We'll have no holes or a partial packet */
+ {
+ int size=lacing_vals[ptr]&0xff;
+ int bytes=0;
+
+ op.packet_base=body_data;
+ op.packet=body_returned;
+ op.e_o_s=lacing_vals[ptr]&0x200; /* last packet of the stream? */
+ op.b_o_s=lacing_vals[ptr]&0x100; /* first packet of the stream? */
+ bytes+=size;
+
+ while(size==255){
+ int val=lacing_vals[++ptr];
+ size=val&0xff;
+ if((val&0x200)!=0)
+ op.e_o_s=0x200;
+ bytes+=size;
+ }
+
+ op.packetno=packetno;
+ op.granulepos=granule_vals[ptr];
+ op.bytes=bytes;
+
+ body_returned+=bytes;
+
+ lacing_returned=ptr+1;
+ }
+ packetno++;
+ return (1);
+ }
+
+ // add the incoming page to the stream state; we decompose the page
+ // into packet segments here as well.
+
+ public int pagein(Page og){
+ byte[] header_base=og.header_base;
+ int header=og.header;
+ byte[] body_base=og.body_base;
+ int body=og.body;
+ int bodysize=og.body_len;
+ int segptr=0;
+
+ int version=og.version();
+ int continued=og.continued();
+ int bos=og.bos();
+ int eos=og.eos();
+ long granulepos=og.granulepos();
+ int _serialno=og.serialno();
+ int _pageno=og.pageno();
+ int segments=header_base[header+26]&0xff;
+
+ // clean up 'returned data'
+ {
+ int lr=lacing_returned;
+ int br=body_returned;
+
+ // body data
+ if(br!=0){
+ body_fill-=br;
+ if(body_fill!=0){
+ System.arraycopy(body_data, br, body_data, 0, body_fill);
+ }
+ body_returned=0;
+ }
+
+ if(lr!=0){
+ // segment table
+ if((lacing_fill-lr)!=0){
+ System.arraycopy(lacing_vals, lr, lacing_vals, 0, lacing_fill-lr);
+ System.arraycopy(granule_vals, lr, granule_vals, 0, lacing_fill-lr);
+ }
+ lacing_fill-=lr;
+ lacing_packet-=lr;
+ lacing_returned=0;
+ }
+ }
+
+ // check the serial number
+ if(_serialno!=serialno)
+ return (-1);
+ if(version>0)
+ return (-1);
+
+ lacing_expand(segments+1);
+
+ // are we in sequence?
+ if(_pageno!=pageno){
+ int i;
+
+ // unroll previous partial packet (if any)
+ for(i=lacing_packet; i<lacing_fill; i++){
+ body_fill-=lacing_vals[i]&0xff;
+ //System.out.println("??");
+ }
+ lacing_fill=lacing_packet;
+
+ // make a note of dropped data in segment table
+ if(pageno!=-1){
+ lacing_vals[lacing_fill++]=0x400;
+ lacing_packet++;
+ }
+
+ // are we a 'continued packet' page? If so, we'll need to skip
+ // some segments
+ if(continued!=0){
+ bos=0;
+ for(; segptr<segments; segptr++){
+ int val=(header_base[header+27+segptr]&0xff);
+ body+=val;
+ bodysize-=val;
+ if(val<255){
+ segptr++;
+ break;
+ }
+ }
+ }
+ }
+
+ if(bodysize!=0){
+ body_expand(bodysize);
+ System.arraycopy(body_base, body, body_data, body_fill, bodysize);
+ body_fill+=bodysize;
+ }
+
+ {
+ int saved=-1;
+ while(segptr<segments){
+ int val=(header_base[header+27+segptr]&0xff);
+ lacing_vals[lacing_fill]=val;
+ granule_vals[lacing_fill]=-1;
+
+ if(bos!=0){
+ lacing_vals[lacing_fill]|=0x100;
+ bos=0;
+ }
+
+ if(val<255)
+ saved=lacing_fill;
+
+ lacing_fill++;
+ segptr++;
+
+ if(val<255)
+ lacing_packet=lacing_fill;
+ }
+
+ /* set the granulepos on the last pcmval of the last full packet */
+ if(saved!=-1){
+ granule_vals[saved]=granulepos;
+ }
+ }
+
+ if(eos!=0){
+ e_o_s=1;
+ if(lacing_fill>0)
+ lacing_vals[lacing_fill-1]|=0x200;
+ }
+
+ pageno=_pageno+1;
+ return (0);
+ }
+
+ /* This will flush remaining packets into a page (returning nonzero),
+ even if there is not enough data to trigger a flush normally
+ (undersized page). If there are no packets or partial packets to
+ flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will
+ try to flush a normal sized page like ogg_stream_pageout; a call to
+ ogg_stream_flush does not gurantee that all packets have flushed.
+ Only a return value of 0 from ogg_stream_flush indicates all packet
+ data is flushed into pages.
+
+ ogg_stream_page will flush the last page in a stream even if it's
+ undersized; you almost certainly want to use ogg_stream_pageout
+ (and *not* ogg_stream_flush) unless you need to flush an undersized
+ page in the middle of a stream for some reason. */
+
+ public int flush(Page og){
+
+ int i;
+ int vals=0;
+ int maxvals=(lacing_fill>255 ? 255 : lacing_fill);
+ int bytes=0;
+ int acc=0;
+ long granule_pos=granule_vals[0];
+
+ if(maxvals==0)
+ return (0);
+
+ /* construct a page */
+ /* decide how many segments to include */
+
+ /* If this is the initial header case, the first page must only include
+ the initial header packet */
+ if(b_o_s==0){ /* 'initial header page' case */
+ granule_pos=0;
+ for(vals=0; vals<maxvals; vals++){
+ if((lacing_vals[vals]&0x0ff)<255){
+ vals++;
+ break;
+ }
+ }
+ }
+ else{
+ for(vals=0; vals<maxvals; vals++){
+ if(acc>4096)
+ break;
+ acc+=(lacing_vals[vals]&0x0ff);
+ granule_pos=granule_vals[vals];
+ }
+ }
+
+ /* construct the header in temp storage */
+ System.arraycopy("OggS".getBytes(), 0, header, 0, 4);
+
+ /* stream structure version */
+ header[4]=0x00;
+
+ /* continued packet flag? */
+ header[5]=0x00;
+ if((lacing_vals[0]&0x100)==0)
+ header[5]|=0x01;
+ /* first page flag? */
+ if(b_o_s==0)
+ header[5]|=0x02;
+ /* last page flag? */
+ if(e_o_s!=0&&lacing_fill==vals)
+ header[5]|=0x04;
+ b_o_s=1;
+
+ /* 64 bits of PCM position */
+ for(i=6; i<14; i++){
+ header[i]=(byte)granule_pos;
+ granule_pos>>>=8;
+ }
+
+ /* 32 bits of stream serial number */
+ {
+ int _serialno=serialno;
+ for(i=14; i<18; i++){
+ header[i]=(byte)_serialno;
+ _serialno>>>=8;
+ }
+ }
+
+ /* 32 bits of page counter (we have both counter and page header
+ because this val can roll over) */
+ if(pageno==-1)
+ pageno=0; /* because someone called
+ stream_reset; this would be a
+ strange thing to do in an
+ encode stream, but it has
+ plausible uses */
+ {
+ int _pageno=pageno++;
+ for(i=18; i<22; i++){
+ header[i]=(byte)_pageno;
+ _pageno>>>=8;
+ }
+ }
+
+ /* zero for computation; filled in later */
+ header[22]=0;
+ header[23]=0;
+ header[24]=0;
+ header[25]=0;
+
+ /* segment table */
+ header[26]=(byte)vals;
+ for(i=0; i<vals; i++){
+ header[i+27]=(byte)lacing_vals[i];
+ bytes+=(header[i+27]&0xff);
+ }
+
+ /* set pointers in the ogg_page struct */
+ og.header_base=header;
+ og.header=0;
+ og.header_len=header_fill=vals+27;
+ og.body_base=body_data;
+ og.body=body_returned;
+ og.body_len=bytes;
+
+ /* advance the lacing data and set the body_returned pointer */
+
+ lacing_fill-=vals;
+ System.arraycopy(lacing_vals, vals, lacing_vals, 0, lacing_fill*4);
+ System.arraycopy(granule_vals, vals, granule_vals, 0, lacing_fill*8);
+ body_returned+=bytes;
+
+ /* calculate the checksum */
+
+ og.checksum();
+
+ /* done */
+ return (1);
+ }
+
+ /* This constructs pages from buffered packet segments. The pointers
+ returned are to static buffers; do not free. The returned buffers are
+ good only until the next call (using the same ogg_stream_state) */
+ public int pageout(Page og){
+ if((e_o_s!=0&&lacing_fill!=0)|| /* 'were done, now flush' case */
+ body_fill-body_returned>4096|| /* 'page nominal size' case */
+ lacing_fill>=255|| /* 'segment table full' case */
+ (lacing_fill!=0&&b_o_s==0)){ /* 'initial header page' case */
+ return flush(og);
+ }
+ return 0;
+ }
+
+ public int eof(){
+ return e_o_s;
+ }
+
+ public int reset(){
+ body_fill=0;
+ body_returned=0;
+
+ lacing_fill=0;
+ lacing_packet=0;
+ lacing_returned=0;
+
+ header_fill=0;
+
+ e_o_s=0;
+ b_o_s=0;
+ pageno=-1;
+ packetno=0;
+ granulepos=0;
+ return (0);
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jogg/SyncState.java b/ardor3d-audio/src/main/java/com/jcraft/jogg/SyncState.java
new file mode 100644
index 0000000..f9d3406
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jogg/SyncState.java
@@ -0,0 +1,275 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jogg;
+
+// DECODING PRIMITIVES: packet streaming layer
+
+// This has two layers to place more of the multi-serialno and paging
+// control in the application's hands. First, we expose a data buffer
+// using ogg_decode_buffer(). The app either copies into the
+// buffer, or passes it directly to read(), etc. We then call
+// ogg_decode_wrote() to tell how many bytes we just added.
+//
+// Pages are returned (pointers into the buffer in ogg_sync_state)
+// by ogg_decode_stream(). The page is then submitted to
+// ogg_decode_page() along with the appropriate
+// ogg_stream_state* (ie, matching serialno). We then get raw
+// packets out calling ogg_stream_packet() with a
+// ogg_stream_state. See the 'frame-prog.txt' docs for details and
+// example code.
+
+public class SyncState{
+
+ public byte[] data;
+ int storage;
+ int fill;
+ int returned;
+
+ int unsynced;
+ int headerbytes;
+ int bodybytes;
+
+ public int clear(){
+ data=null;
+ return (0);
+ }
+
+ public int buffer(int size){
+ // first, clear out any space that has been previously returned
+ if(returned!=0){
+ fill-=returned;
+ if(fill>0){
+ System.arraycopy(data, returned, data, 0, fill);
+ }
+ returned=0;
+ }
+
+ if(size>storage-fill){
+ // We need to extend the internal buffer
+ int newsize=size+fill+4096; // an extra page to be nice
+ if(data!=null){
+ byte[] foo=new byte[newsize];
+ System.arraycopy(data, 0, foo, 0, data.length);
+ data=foo;
+ }
+ else{
+ data=new byte[newsize];
+ }
+ storage=newsize;
+ }
+
+ return (fill);
+ }
+
+ public int wrote(int bytes){
+ if(fill+bytes>storage)
+ return (-1);
+ fill+=bytes;
+ return (0);
+ }
+
+ // sync the stream. This is meant to be useful for finding page
+ // boundaries.
+ //
+ // return values for this:
+ // -n) skipped n bytes
+ // 0) page not ready; more data (no bytes skipped)
+ // n) page synced at current location; page length n bytes
+ private Page pageseek=new Page();
+ private byte[] chksum=new byte[4];
+
+ public int pageseek(Page og){
+ int page=returned;
+ int next;
+ int bytes=fill-returned;
+
+ if(headerbytes==0){
+ int _headerbytes, i;
+ if(bytes<27)
+ return (0); // not enough for a header
+
+ /* verify capture pattern */
+ if(data[page]!='O'||data[page+1]!='g'||data[page+2]!='g'
+ ||data[page+3]!='S'){
+ headerbytes=0;
+ bodybytes=0;
+
+ // search for possible capture
+ next=0;
+ for(int ii=0; ii<bytes-1; ii++){
+ if(data[page+1+ii]=='O'){
+ next=page+1+ii;
+ break;
+ }
+ }
+ //next=memchr(page+1,'O',bytes-1);
+ if(next==0)
+ next=fill;
+
+ returned=next;
+ return (-(next-page));
+ }
+ _headerbytes=(data[page+26]&0xff)+27;
+ if(bytes<_headerbytes)
+ return (0); // not enough for header + seg table
+
+ // count up body length in the segment table
+
+ for(i=0; i<(data[page+26]&0xff); i++){
+ bodybytes+=(data[page+27+i]&0xff);
+ }
+ headerbytes=_headerbytes;
+ }
+
+ if(bodybytes+headerbytes>bytes)
+ return (0);
+
+ // The whole test page is buffered. Verify the checksum
+ synchronized(chksum){
+ // Grab the checksum bytes, set the header field to zero
+
+ System.arraycopy(data, page+22, chksum, 0, 4);
+ data[page+22]=0;
+ data[page+23]=0;
+ data[page+24]=0;
+ data[page+25]=0;
+
+ // set up a temp page struct and recompute the checksum
+ Page log=pageseek;
+ log.header_base=data;
+ log.header=page;
+ log.header_len=headerbytes;
+
+ log.body_base=data;
+ log.body=page+headerbytes;
+ log.body_len=bodybytes;
+ log.checksum();
+
+ // Compare
+ if(chksum[0]!=data[page+22]||chksum[1]!=data[page+23]
+ ||chksum[2]!=data[page+24]||chksum[3]!=data[page+25]){
+ // D'oh. Mismatch! Corrupt page (or miscapture and not a page at all)
+ // replace the computed checksum with the one actually read in
+ System.arraycopy(chksum, 0, data, page+22, 4);
+ // Bad checksum. Lose sync */
+
+ headerbytes=0;
+ bodybytes=0;
+ // search for possible capture
+ next=0;
+ for(int ii=0; ii<bytes-1; ii++){
+ if(data[page+1+ii]=='O'){
+ next=page+1+ii;
+ break;
+ }
+ }
+ //next=memchr(page+1,'O',bytes-1);
+ if(next==0)
+ next=fill;
+ returned=next;
+ return (-(next-page));
+ }
+ }
+
+ // yes, have a whole page all ready to go
+ {
+ page=returned;
+
+ if(og!=null){
+ og.header_base=data;
+ og.header=page;
+ og.header_len=headerbytes;
+ og.body_base=data;
+ og.body=page+headerbytes;
+ og.body_len=bodybytes;
+ }
+
+ unsynced=0;
+ returned+=(bytes=headerbytes+bodybytes);
+ headerbytes=0;
+ bodybytes=0;
+ return (bytes);
+ }
+ }
+
+ // sync the stream and get a page. Keep trying until we find a page.
+ // Supress 'sync errors' after reporting the first.
+ //
+ // return values:
+ // -1) recapture (hole in data)
+ // 0) need more data
+ // 1) page returned
+ //
+ // Returns pointers into buffered data; invalidated by next call to
+ // _stream, _clear, _init, or _buffer
+
+ public int pageout(Page og){
+ // all we need to do is verify a page at the head of the stream
+ // buffer. If it doesn't verify, we look for the next potential
+ // frame
+
+ while(true){
+ int ret=pageseek(og);
+ if(ret>0){
+ // have a page
+ return (1);
+ }
+ if(ret==0){
+ // need more data
+ return (0);
+ }
+
+ // head did not start a synced page... skipped some bytes
+ if(unsynced==0){
+ unsynced=1;
+ return (-1);
+ }
+ // loop. keep looking
+ }
+ }
+
+ // clear things to an initial state. Good to call, eg, before seeking
+ public int reset(){
+ fill=0;
+ returned=0;
+ unsynced=0;
+ headerbytes=0;
+ bodybytes=0;
+ return (0);
+ }
+
+ public void init(){
+ }
+
+ public int getDataOffset(){
+ return returned;
+ }
+
+ public int getBufferOffset(){
+ return fill;
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Block.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Block.java
new file mode 100644
index 0000000..f1d8678
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Block.java
@@ -0,0 +1,128 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+public class Block{
+ ///necessary stream state for linking to the framing abstraction
+ float[][] pcm=new float[0][]; // this is a pointer into local storage
+ Buffer opb=new Buffer();
+
+ int lW;
+ int W;
+ int nW;
+ int pcmend;
+ int mode;
+
+ int eofflag;
+ long granulepos;
+ long sequence;
+ DspState vd; // For read-only access of configuration
+
+ // bitmetrics for the frame
+ int glue_bits;
+ int time_bits;
+ int floor_bits;
+ int res_bits;
+
+ public Block(DspState vd){
+ this.vd=vd;
+ if(vd.analysisp!=0){
+ opb.writeinit();
+ }
+ }
+
+ public void init(DspState vd){
+ this.vd=vd;
+ }
+
+ public int clear(){
+ if(vd!=null){
+ if(vd.analysisp!=0){
+ opb.writeclear();
+ }
+ }
+ return (0);
+ }
+
+ public int synthesis(Packet op){
+ Info vi=vd.vi;
+
+ // first things first. Make sure decode is ready
+ opb.readinit(op.packet_base, op.packet, op.bytes);
+
+ // Check the packet type
+ if(opb.read(1)!=0){
+ // Oops. This is not an audio data packet
+ return (-1);
+ }
+
+ // read our mode and pre/post windowsize
+ int _mode=opb.read(vd.modebits);
+ if(_mode==-1)
+ return (-1);
+
+ mode=_mode;
+ W=vi.mode_param[mode].blockflag;
+ if(W!=0){
+ lW=opb.read(1);
+ nW=opb.read(1);
+ if(nW==-1)
+ return (-1);
+ }
+ else{
+ lW=0;
+ nW=0;
+ }
+
+ // more setup
+ granulepos=op.granulepos;
+ sequence=op.packetno-3; // first block is third packet
+ eofflag=op.e_o_s;
+
+ // alloc pcm passback storage
+ pcmend=vi.blocksizes[W];
+ if(pcm.length<vi.channels){
+ pcm=new float[vi.channels][];
+ }
+ for(int i=0; i<vi.channels; i++){
+ if(pcm[i]==null||pcm[i].length<pcmend){
+ pcm[i]=new float[pcmend];
+ }
+ else{
+ for(int j=0; j<pcmend; j++){
+ pcm[i][j]=0;
+ }
+ }
+ }
+
+ // unpack_header enforces range checking
+ int type=vi.map_type[vi.mode_param[mode].mapping];
+ return (FuncMapping.mapping_P[type].inverse(this, vd.mode[mode]));
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/ChainingExample.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/ChainingExample.java
new file mode 100644
index 0000000..7ed0199
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/ChainingExample.java
@@ -0,0 +1,69 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+class ChainingExample{
+ public static void main(String[] arg){
+ VorbisFile ov=null;
+
+ try{
+ if(arg.length>0){
+ ov=new VorbisFile(arg[0]);
+ }
+ else{
+ ov=new VorbisFile(System.in, null, -1);
+ }
+ }
+ catch(Exception e){
+ System.err.println(e);
+ return;
+ }
+
+ if(ov.seekable()){
+ System.out.println("Input bitstream contained "+ov.streams()
+ +" logical bitstream section(s).");
+ System.out.println("Total bitstream playing time: "+ov.time_total(-1)
+ +" seconds\n");
+ }
+ else{
+ System.out.println("Standard input was not seekable.");
+ System.out.println("First logical bitstream information:\n");
+ }
+
+ for(int i=0; i<ov.streams(); i++){
+ Info vi=ov.getInfo(i);
+ System.out.println("\tlogical bitstream section "+(i+1)+" information:");
+ System.out.println("\t\t"+vi.rate+"Hz "+vi.channels+" channels bitrate "
+ +(ov.bitrate(i)/1000)+"kbps serial number="+ov.serialnumber(i));
+ System.out.print("\t\tcompressed length: "+ov.raw_total(i)+" bytes ");
+ System.out.println(" play time: "+ov.time_total(i)+"s");
+ Comment vc=ov.getComment(i);
+ System.out.println(vc);
+ }
+ //ov.clear();
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/CodeBook.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/CodeBook.java
new file mode 100644
index 0000000..51041c1
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/CodeBook.java
@@ -0,0 +1,478 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+class CodeBook{
+ int dim; // codebook dimensions (elements per vector)
+ int entries; // codebook entries
+ StaticCodeBook c=new StaticCodeBook();
+
+ float[] valuelist; // list of dim*entries actual entry values
+ int[] codelist; // list of bitstream codewords for each entry
+ DecodeAux decode_tree;
+
+ // returns the number of bits
+ int encode(int a, Buffer b){
+ b.write(codelist[a], c.lengthlist[a]);
+ return (c.lengthlist[a]);
+ }
+
+ // One the encode side, our vector writers are each designed for a
+ // specific purpose, and the encoder is not flexible without modification:
+ //
+ // The LSP vector coder uses a single stage nearest-match with no
+ // interleave, so no step and no error return. This is specced by floor0
+ // and doesn't change.
+ //
+ // Residue0 encoding interleaves, uses multiple stages, and each stage
+ // peels of a specific amount of resolution from a lattice (thus we want
+ // to match by threshhold, not nearest match). Residue doesn't *have* to
+ // be encoded that way, but to change it, one will need to add more
+ // infrastructure on the encode side (decode side is specced and simpler)
+
+ // floor0 LSP (single stage, non interleaved, nearest match)
+ // returns entry number and *modifies a* to the quantization value
+ int errorv(float[] a){
+ int best=best(a, 1);
+ for(int k=0; k<dim; k++){
+ a[k]=valuelist[best*dim+k];
+ }
+ return (best);
+ }
+
+ // returns the number of bits and *modifies a* to the quantization value
+ int encodev(int best, float[] a, Buffer b){
+ for(int k=0; k<dim; k++){
+ a[k]=valuelist[best*dim+k];
+ }
+ return (encode(best, b));
+ }
+
+ // res0 (multistage, interleave, lattice)
+ // returns the number of bits and *modifies a* to the remainder value
+ int encodevs(float[] a, Buffer b, int step, int addmul){
+ int best=besterror(a, step, addmul);
+ return (encode(best, b));
+ }
+
+ private int[] t=new int[15]; // decodevs_add is synchronized for re-using t.
+
+ synchronized int decodevs_add(float[] a, int offset, Buffer b, int n){
+ int step=n/dim;
+ int entry;
+ int i, j, o;
+
+ if(t.length<step){
+ t=new int[step];
+ }
+
+ for(i=0; i<step; i++){
+ entry=decode(b);
+ if(entry==-1)
+ return (-1);
+ t[i]=entry*dim;
+ }
+ for(i=0, o=0; i<dim; i++, o+=step){
+ for(j=0; j<step; j++){
+ a[offset+o+j]+=valuelist[t[j]+i];
+ }
+ }
+
+ return (0);
+ }
+
+ int decodev_add(float[] a, int offset, Buffer b, int n){
+ int i, j, entry;
+ int t;
+
+ if(dim>8){
+ for(i=0; i<n;){
+ entry=decode(b);
+ if(entry==-1)
+ return (-1);
+ t=entry*dim;
+ for(j=0; j<dim;){
+ a[offset+(i++)]+=valuelist[t+(j++)];
+ }
+ }
+ }
+ else{
+ for(i=0; i<n;){
+ entry=decode(b);
+ if(entry==-1)
+ return (-1);
+ t=entry*dim;
+ j=0;
+ switch(dim){
+ case 8:
+ a[offset+(i++)]+=valuelist[t+(j++)];
+ case 7:
+ a[offset+(i++)]+=valuelist[t+(j++)];
+ case 6:
+ a[offset+(i++)]+=valuelist[t+(j++)];
+ case 5:
+ a[offset+(i++)]+=valuelist[t+(j++)];
+ case 4:
+ a[offset+(i++)]+=valuelist[t+(j++)];
+ case 3:
+ a[offset+(i++)]+=valuelist[t+(j++)];
+ case 2:
+ a[offset+(i++)]+=valuelist[t+(j++)];
+ case 1:
+ a[offset+(i++)]+=valuelist[t+(j++)];
+ case 0:
+ break;
+ }
+ }
+ }
+ return (0);
+ }
+
+ int decodev_set(float[] a, int offset, Buffer b, int n){
+ int i, j, entry;
+ int t;
+
+ for(i=0; i<n;){
+ entry=decode(b);
+ if(entry==-1)
+ return (-1);
+ t=entry*dim;
+ for(j=0; j<dim;){
+ a[offset+i++]=valuelist[t+(j++)];
+ }
+ }
+ return (0);
+ }
+
+ int decodevv_add(float[][] a, int offset, int ch, Buffer b, int n){
+ int i, j, entry;
+ int chptr=0;
+
+ for(i=offset/ch; i<(offset+n)/ch;){
+ entry=decode(b);
+ if(entry==-1)
+ return (-1);
+
+ int t=entry*dim;
+ for(j=0; j<dim; j++){
+ a[chptr++][i]+=valuelist[t+j];
+ if(chptr==ch){
+ chptr=0;
+ i++;
+ }
+ }
+ }
+ return (0);
+ }
+
+ // Decode side is specced and easier, because we don't need to find
+ // matches using different criteria; we simply read and map. There are
+ // two things we need to do 'depending':
+ //
+ // We may need to support interleave. We don't really, but it's
+ // convenient to do it here rather than rebuild the vector later.
+ //
+ // Cascades may be additive or multiplicitive; this is not inherent in
+ // the codebook, but set in the code using the codebook. Like
+ // interleaving, it's easiest to do it here.
+ // stage==0 -> declarative (set the value)
+ // stage==1 -> additive
+ // stage==2 -> multiplicitive
+
+ // returns the entry number or -1 on eof
+ int decode(Buffer b){
+ int ptr=0;
+ DecodeAux t=decode_tree;
+ int lok=b.look(t.tabn);
+
+ if(lok>=0){
+ ptr=t.tab[lok];
+ b.adv(t.tabl[lok]);
+ if(ptr<=0){
+ return -ptr;
+ }
+ }
+ do{
+ switch(b.read1()){
+ case 0:
+ ptr=t.ptr0[ptr];
+ break;
+ case 1:
+ ptr=t.ptr1[ptr];
+ break;
+ case -1:
+ default:
+ return (-1);
+ }
+ }
+ while(ptr>0);
+ return (-ptr);
+ }
+
+ // returns the entry number or -1 on eof
+ int decodevs(float[] a, int index, Buffer b, int step, int addmul){
+ int entry=decode(b);
+ if(entry==-1)
+ return (-1);
+ switch(addmul){
+ case -1:
+ for(int i=0, o=0; i<dim; i++, o+=step)
+ a[index+o]=valuelist[entry*dim+i];
+ break;
+ case 0:
+ for(int i=0, o=0; i<dim; i++, o+=step)
+ a[index+o]+=valuelist[entry*dim+i];
+ break;
+ case 1:
+ for(int i=0, o=0; i<dim; i++, o+=step)
+ a[index+o]*=valuelist[entry*dim+i];
+ break;
+ default:
+ //System.err.println("CodeBook.decodeves: addmul="+addmul);
+ }
+ return (entry);
+ }
+
+ int best(float[] a, int step){
+ // brute force it!
+ {
+ int besti=-1;
+ float best=0.f;
+ int e=0;
+ for(int i=0; i<entries; i++){
+ if(c.lengthlist[i]>0){
+ float _this=dist(dim, valuelist, e, a, step);
+ if(besti==-1||_this<best){
+ best=_this;
+ besti=i;
+ }
+ }
+ e+=dim;
+ }
+ return (besti);
+ }
+ }
+
+ // returns the entry number and *modifies a* to the remainder value
+ int besterror(float[] a, int step, int addmul){
+ int best=best(a, step);
+ switch(addmul){
+ case 0:
+ for(int i=0, o=0; i<dim; i++, o+=step)
+ a[o]-=valuelist[best*dim+i];
+ break;
+ case 1:
+ for(int i=0, o=0; i<dim; i++, o+=step){
+ float val=valuelist[best*dim+i];
+ if(val==0){
+ a[o]=0;
+ }
+ else{
+ a[o]/=val;
+ }
+ }
+ break;
+ }
+ return (best);
+ }
+
+ void clear(){
+ }
+
+ private static float dist(int el, float[] ref, int index, float[] b, int step){
+ float acc=(float)0.;
+ for(int i=0; i<el; i++){
+ float val=(ref[index+i]-b[i*step]);
+ acc+=val*val;
+ }
+ return (acc);
+ }
+
+ int init_decode(StaticCodeBook s){
+ c=s;
+ entries=s.entries;
+ dim=s.dim;
+ valuelist=s.unquantize();
+
+ decode_tree=make_decode_tree();
+ if(decode_tree==null){
+ clear();
+ return (-1);
+ }
+ return (0);
+ }
+
+ // given a list of word lengths, generate a list of codewords. Works
+ // for length ordered or unordered, always assigns the lowest valued
+ // codewords first. Extended to handle unused entries (length 0)
+ static int[] make_words(int[] l, int n){
+ int[] marker=new int[33];
+ int[] r=new int[n];
+
+ for(int i=0; i<n; i++){
+ int length=l[i];
+ if(length>0){
+ int entry=marker[length];
+
+ // when we claim a node for an entry, we also claim the nodes
+ // below it (pruning off the imagined tree that may have dangled
+ // from it) as well as blocking the use of any nodes directly
+ // above for leaves
+
+ // update ourself
+ if(length<32&&(entry>>>length)!=0){
+ // error condition; the lengths must specify an overpopulated tree
+ //free(r);
+ return (null);
+ }
+ r[i]=entry;
+
+ // Look to see if the next shorter marker points to the node
+ // above. if so, update it and repeat.
+ {
+ for(int j=length; j>0; j--){
+ if((marker[j]&1)!=0){
+ // have to jump branches
+ if(j==1)
+ marker[1]++;
+ else
+ marker[j]=marker[j-1]<<1;
+ break; // invariant says next upper marker would already
+ // have been moved if it was on the same path
+ }
+ marker[j]++;
+ }
+ }
+
+ // prune the tree; the implicit invariant says all the longer
+ // markers were dangling from our just-taken node. Dangle them
+ // from our *new* node.
+ for(int j=length+1; j<33; j++){
+ if((marker[j]>>>1)==entry){
+ entry=marker[j];
+ marker[j]=marker[j-1]<<1;
+ }
+ else{
+ break;
+ }
+ }
+ }
+ }
+
+ // bitreverse the words because our bitwise packer/unpacker is LSb
+ // endian
+ for(int i=0; i<n; i++){
+ int temp=0;
+ for(int j=0; j<l[i]; j++){
+ temp<<=1;
+ temp|=(r[i]>>>j)&1;
+ }
+ r[i]=temp;
+ }
+
+ return (r);
+ }
+
+ // build the decode helper tree from the codewords
+ DecodeAux make_decode_tree(){
+ int top=0;
+ DecodeAux t=new DecodeAux();
+ int[] ptr0=t.ptr0=new int[entries*2];
+ int[] ptr1=t.ptr1=new int[entries*2];
+ int[] codelist=make_words(c.lengthlist, c.entries);
+
+ if(codelist==null)
+ return (null);
+ t.aux=entries*2;
+
+ for(int i=0; i<entries; i++){
+ if(c.lengthlist[i]>0){
+ int ptr=0;
+ int j;
+ for(j=0; j<c.lengthlist[i]-1; j++){
+ int bit=(codelist[i]>>>j)&1;
+ if(bit==0){
+ if(ptr0[ptr]==0){
+ ptr0[ptr]=++top;
+ }
+ ptr=ptr0[ptr];
+ }
+ else{
+ if(ptr1[ptr]==0){
+ ptr1[ptr]=++top;
+ }
+ ptr=ptr1[ptr];
+ }
+ }
+
+ if(((codelist[i]>>>j)&1)==0){
+ ptr0[ptr]=-i;
+ }
+ else{
+ ptr1[ptr]=-i;
+ }
+
+ }
+ }
+
+ t.tabn=Util.ilog(entries)-4;
+
+ if(t.tabn<5)
+ t.tabn=5;
+ int n=1<<t.tabn;
+ t.tab=new int[n];
+ t.tabl=new int[n];
+ for(int i=0; i<n; i++){
+ int p=0;
+ int j=0;
+ for(j=0; j<t.tabn&&(p>0||j==0); j++){
+ if((i&(1<<j))!=0){
+ p=ptr1[p];
+ }
+ else{
+ p=ptr0[p];
+ }
+ }
+ t.tab[i]=p; // -code
+ t.tabl[i]=j; // length
+ }
+
+ return (t);
+ }
+
+ class DecodeAux{
+ int[] tab;
+ int[] tabl;
+ int tabn;
+
+ int[] ptr0;
+ int[] ptr1;
+ int aux; // number of tree entries
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Comment.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Comment.java
new file mode 100644
index 0000000..02b3084
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Comment.java
@@ -0,0 +1,243 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+// the comments are not part of vorbis_info so that vorbis_info can be
+// static storage
+public class Comment{
+ private static byte[] _vorbis="vorbis".getBytes();
+ private static byte[] _vendor="Xiphophorus libVorbis I 20000508".getBytes();
+
+ private static final int OV_EIMPL=-130;
+
+ // unlimited user comment fields.
+ public byte[][] user_comments;
+ public int[] comment_lengths;
+ public int comments;
+ public byte[] vendor;
+
+ public void init(){
+ user_comments=null;
+ comments=0;
+ vendor=null;
+ }
+
+ public void add(String comment){
+ add(comment.getBytes());
+ }
+
+ private void add(byte[] comment){
+ byte[][] foo=new byte[comments+2][];
+ if(user_comments!=null){
+ System.arraycopy(user_comments, 0, foo, 0, comments);
+ }
+ user_comments=foo;
+
+ int[] goo=new int[comments+2];
+ if(comment_lengths!=null){
+ System.arraycopy(comment_lengths, 0, goo, 0, comments);
+ }
+ comment_lengths=goo;
+
+ byte[] bar=new byte[comment.length+1];
+ System.arraycopy(comment, 0, bar, 0, comment.length);
+ user_comments[comments]=bar;
+ comment_lengths[comments]=comment.length;
+ comments++;
+ user_comments[comments]=null;
+ }
+
+ public void add_tag(String tag, String contents){
+ if(contents==null)
+ contents="";
+ add(tag+"="+contents);
+ }
+
+ static boolean tagcompare(byte[] s1, byte[] s2, int n){
+ int c=0;
+ byte u1, u2;
+ while(c<n){
+ u1=s1[c];
+ u2=s2[c];
+ if('Z'>=u1&&u1>='A')
+ u1=(byte)(u1-'A'+'a');
+ if('Z'>=u2&&u2>='A')
+ u2=(byte)(u2-'A'+'a');
+ if(u1!=u2){
+ return false;
+ }
+ c++;
+ }
+ return true;
+ }
+
+ public String query(String tag){
+ return query(tag, 0);
+ }
+
+ public String query(String tag, int count){
+ int foo=query(tag.getBytes(), count);
+ if(foo==-1)
+ return null;
+ byte[] comment=user_comments[foo];
+ for(int i=0; i<comment_lengths[foo]; i++){
+ if(comment[i]=='='){
+ return new String(comment, i+1, comment_lengths[foo]-(i+1));
+ }
+ }
+ return null;
+ }
+
+ private int query(byte[] tag, int count){
+ int i=0;
+ int found=0;
+ int fulltaglen=tag.length+1;
+ byte[] fulltag=new byte[fulltaglen];
+ System.arraycopy(tag, 0, fulltag, 0, tag.length);
+ fulltag[tag.length]=(byte)'=';
+
+ for(i=0; i<comments; i++){
+ if(tagcompare(user_comments[i], fulltag, fulltaglen)){
+ if(count==found){
+ // We return a pointer to the data, not a copy
+ //return user_comments[i] + taglen + 1;
+ return i;
+ }
+ else{
+ found++;
+ }
+ }
+ }
+ return -1;
+ }
+
+ int unpack(Buffer opb){
+ int vendorlen=opb.read(32);
+ if(vendorlen<0){
+ clear();
+ return (-1);
+ }
+ vendor=new byte[vendorlen+1];
+ opb.read(vendor, vendorlen);
+ comments=opb.read(32);
+ if(comments<0){
+ clear();
+ return (-1);
+ }
+ user_comments=new byte[comments+1][];
+ comment_lengths=new int[comments+1];
+
+ for(int i=0; i<comments; i++){
+ int len=opb.read(32);
+ if(len<0){
+ clear();
+ return (-1);
+ }
+ comment_lengths[i]=len;
+ user_comments[i]=new byte[len+1];
+ opb.read(user_comments[i], len);
+ }
+ if(opb.read(1)!=1){
+ clear();
+ return (-1);
+
+ }
+ return (0);
+ }
+
+ int pack(Buffer opb){
+ // preamble
+ opb.write(0x03, 8);
+ opb.write(_vorbis);
+
+ // vendor
+ opb.write(_vendor.length, 32);
+ opb.write(_vendor);
+
+ // comments
+ opb.write(comments, 32);
+ if(comments!=0){
+ for(int i=0; i<comments; i++){
+ if(user_comments[i]!=null){
+ opb.write(comment_lengths[i], 32);
+ opb.write(user_comments[i]);
+ }
+ else{
+ opb.write(0, 32);
+ }
+ }
+ }
+ opb.write(1, 1);
+ return (0);
+ }
+
+ public int header_out(Packet op){
+ Buffer opb=new Buffer();
+ opb.writeinit();
+
+ if(pack(opb)!=0)
+ return OV_EIMPL;
+
+ op.packet_base=new byte[opb.bytes()];
+ op.packet=0;
+ op.bytes=opb.bytes();
+ System.arraycopy(opb.buffer(), 0, op.packet_base, 0, op.bytes);
+ op.b_o_s=0;
+ op.e_o_s=0;
+ op.granulepos=0;
+ return 0;
+ }
+
+ void clear(){
+ for(int i=0; i<comments; i++)
+ user_comments[i]=null;
+ user_comments=null;
+ vendor=null;
+ }
+
+ public String getVendor(){
+ return new String(vendor, 0, vendor.length-1);
+ }
+
+ public String getComment(int i){
+ if(comments<=i)
+ return null;
+ return new String(user_comments[i], 0, user_comments[i].length-1);
+ }
+
+ public String toString(){
+ String foo="Vendor: "+new String(vendor, 0, vendor.length-1);
+ for(int i=0; i<comments; i++){
+ foo=foo+"\nComment: "
+ +new String(user_comments[i], 0, user_comments[i].length-1);
+ }
+ foo=foo+"\n";
+ return foo;
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/DecodeExample.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/DecodeExample.java
new file mode 100644
index 0000000..b73889e
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/DecodeExample.java
@@ -0,0 +1,324 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+// Takes a vorbis bitstream from stdin and writes raw stereo PCM to
+// stdout. Decodes simple and chained OggVorbis files from beginning
+// to end. Vorbisfile.a is somewhat more complex than the code below.
+
+class DecodeExample{
+ static int convsize=4096*2;
+ static byte[] convbuffer=new byte[convsize]; // take 8k out of the data segment, not the stack
+
+ public static void main(String[] arg){
+ java.io.InputStream input=System.in;
+ if(arg.length>0){
+ try{
+ input=new java.io.FileInputStream(arg[0]);
+ }
+ catch(Exception e){
+ System.err.println(e);
+ }
+ }
+
+ SyncState oy=new SyncState(); // sync and verify incoming physical bitstream
+ StreamState os=new StreamState(); // take physical pages, weld into a logical stream of packets
+ Page og=new Page(); // one Ogg bitstream page. Vorbis packets are inside
+ Packet op=new Packet(); // one raw packet of data for decode
+
+ Info vi=new Info(); // struct that stores all the static vorbis bitstream settings
+ Comment vc=new Comment(); // struct that stores all the bitstream user comments
+ DspState vd=new DspState(); // central working state for the packet->PCM decoder
+ Block vb=new Block(vd); // local working space for packet->PCM decode
+
+ byte[] buffer;
+ int bytes=0;
+
+ // Decode setup
+
+ oy.init(); // Now we can read pages
+
+ while(true){ // we repeat if the bitstream is chained
+ int eos=0;
+
+ // grab some data at the head of the stream. We want the first page
+ // (which is guaranteed to be small and only contain the Vorbis
+ // stream initial header) We need the first page to get the stream
+ // serialno.
+
+ // submit a 4k block to libvorbis' Ogg layer
+ int index=oy.buffer(4096);
+ buffer=oy.data;
+ try{
+ bytes=input.read(buffer, index, 4096);
+ }
+ catch(Exception e){
+ System.err.println(e);
+ System.exit(-1);
+ }
+ oy.wrote(bytes);
+
+ // Get the first page.
+ if(oy.pageout(og)!=1){
+ // have we simply run out of data? If so, we're done.
+ if(bytes<4096)
+ break;
+
+ // error case. Must not be Vorbis data
+ System.err.println("Input does not appear to be an Ogg bitstream.");
+ System.exit(1);
+ }
+
+ // Get the serial number and set up the rest of decode.
+ // serialno first; use it to set up a logical stream
+ os.init(og.serialno());
+
+ // extract the initial header from the first page and verify that the
+ // Ogg bitstream is in fact Vorbis data
+
+ // I handle the initial header first instead of just having the code
+ // read all three Vorbis headers at once because reading the initial
+ // header is an easy way to identify a Vorbis bitstream and it's
+ // useful to see that functionality seperated out.
+
+ vi.init();
+ vc.init();
+ if(os.pagein(og)<0){
+ // error; stream version mismatch perhaps
+ System.err.println("Error reading first page of Ogg bitstream data.");
+ System.exit(1);
+ }
+
+ if(os.packetout(op)!=1){
+ // no page? must not be vorbis
+ System.err.println("Error reading initial header packet.");
+ System.exit(1);
+ }
+
+ if(vi.synthesis_headerin(vc, op)<0){
+ // error case; not a vorbis header
+ System.err
+ .println("This Ogg bitstream does not contain Vorbis audio data.");
+ System.exit(1);
+ }
+
+ // At this point, we're sure we're Vorbis. We've set up the logical
+ // (Ogg) bitstream decoder. Get the comment and codebook headers and
+ // set up the Vorbis decoder
+
+ // The next two packets in order are the comment and codebook headers.
+ // They're likely large and may span multiple pages. Thus we reead
+ // and submit data until we get our two pacakets, watching that no
+ // pages are missing. If a page is missing, error out; losing a
+ // header page is the only place where missing data is fatal. */
+
+ int i=0;
+ while(i<2){
+ while(i<2){
+
+ int result=oy.pageout(og);
+ if(result==0)
+ break; // Need more data
+ // Don't complain about missing or corrupt data yet. We'll
+ // catch it at the packet output phase
+
+ if(result==1){
+ os.pagein(og); // we can ignore any errors here
+ // as they'll also become apparent
+ // at packetout
+ while(i<2){
+ result=os.packetout(op);
+ if(result==0)
+ break;
+ if(result==-1){
+ // Uh oh; data at some point was corrupted or missing!
+ // We can't tolerate that in a header. Die.
+ System.err.println("Corrupt secondary header. Exiting.");
+ System.exit(1);
+ }
+ vi.synthesis_headerin(vc, op);
+ i++;
+ }
+ }
+ }
+ // no harm in not checking before adding more
+ index=oy.buffer(4096);
+ buffer=oy.data;
+ try{
+ bytes=input.read(buffer, index, 4096);
+ }
+ catch(Exception e){
+ System.err.println(e);
+ System.exit(1);
+ }
+ if(bytes==0&&i<2){
+ System.err.println("End of file before finding all Vorbis headers!");
+ System.exit(1);
+ }
+ oy.wrote(bytes);
+ }
+
+ // Throw the comments plus a few lines about the bitstream we're
+ // decoding
+ {
+ byte[][] ptr=vc.user_comments;
+ for(int j=0; j<ptr.length; j++){
+ if(ptr[j]==null)
+ break;
+ System.err.println(new String(ptr[j], 0, ptr[j].length-1));
+ }
+ System.err.println("\nBitstream is "+vi.channels+" channel, "+vi.rate
+ +"Hz");
+ System.err.println("Encoded by: "
+ +new String(vc.vendor, 0, vc.vendor.length-1)+"\n");
+ }
+
+ convsize=4096/vi.channels;
+
+ // OK, got and parsed all three headers. Initialize the Vorbis
+ // packet->PCM decoder.
+ vd.synthesis_init(vi); // central decode state
+ vb.init(vd); // local state for most of the decode
+ // so multiple block decodes can
+ // proceed in parallel. We could init
+ // multiple vorbis_block structures
+ // for vd here
+
+ float[][][] _pcm=new float[1][][];
+ int[] _index=new int[vi.channels];
+ // The rest is just a straight decode loop until end of stream
+ while(eos==0){
+ while(eos==0){
+
+ int result=oy.pageout(og);
+ if(result==0)
+ break; // need more data
+ if(result==-1){ // missing or corrupt data at this page position
+ System.err
+ .println("Corrupt or missing data in bitstream; continuing...");
+ }
+ else{
+ os.pagein(og); // can safely ignore errors at
+ // this point
+ while(true){
+ result=os.packetout(op);
+
+ if(result==0)
+ break; // need more data
+ if(result==-1){ // missing or corrupt data at this page position
+ // no reason to complain; already complained above
+ }
+ else{
+ // we have a packet. Decode it
+ int samples;
+ if(vb.synthesis(op)==0){ // test for success!
+ vd.synthesis_blockin(vb);
+ }
+
+ // **pcm is a multichannel float vector. In stereo, for
+ // example, pcm[0] is left, and pcm[1] is right. samples is
+ // the size of each channel. Convert the float values
+ // (-1.<=range<=1.) to whatever PCM format and write it out
+
+ while((samples=vd.synthesis_pcmout(_pcm, _index))>0){
+ float[][] pcm=_pcm[0];
+ int bout=(samples<convsize ? samples : convsize);
+
+ // convert floats to 16 bit signed ints (host order) and
+ // interleave
+ for(i=0; i<vi.channels; i++){
+ int ptr=i*2;
+ //int ptr=i;
+ int mono=_index[i];
+ for(int j=0; j<bout; j++){
+ int val=(int)(pcm[i][mono+j]*32767.);
+ // short val=(short)(pcm[i][mono+j]*32767.);
+ // int val=(int)Math.round(pcm[i][mono+j]*32767.);
+ // might as well guard against clipping
+ if(val>32767){
+ val=32767;
+ }
+ if(val<-32768){
+ val=-32768;
+ }
+ if(val<0)
+ val=val|0x8000;
+ convbuffer[ptr]=(byte)(val);
+ convbuffer[ptr+1]=(byte)(val>>>8);
+ ptr+=2*(vi.channels);
+ }
+ }
+
+ System.out.write(convbuffer, 0, 2*vi.channels*bout);
+
+ // tell libvorbis how
+ // many samples we
+ // actually consumed
+ vd.synthesis_read(bout);
+ }
+ }
+ }
+ if(og.eos()!=0)
+ eos=1;
+ }
+ }
+ if(eos==0){
+ index=oy.buffer(4096);
+ buffer=oy.data;
+ try{
+ bytes=input.read(buffer, index, 4096);
+ }
+ catch(Exception e){
+ System.err.println(e);
+ System.exit(1);
+ }
+ oy.wrote(bytes);
+ if(bytes==0)
+ eos=1;
+ }
+ }
+
+ // clean up this logical bitstream; before exit we see if we're
+ // followed by another [chained]
+
+ os.clear();
+
+ // ogg_page and ogg_packet structs always point to storage in
+ // libvorbis. They're never freed or manipulated directly
+
+ vb.clear();
+ vd.clear();
+ vi.clear(); // must be called last
+ }
+
+ // OK, clean up the framer
+ oy.clear();
+ System.err.println("Done.");
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Drft.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Drft.java
new file mode 100644
index 0000000..3aaad34
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Drft.java
@@ -0,0 +1,1327 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+class Drft{
+ int n;
+ float[] trigcache;
+ int[] splitcache;
+
+ void backward(float[] data){
+ if(n==1)
+ return;
+ drftb1(n, data, trigcache, trigcache, n, splitcache);
+ }
+
+ void init(int n){
+ this.n=n;
+ trigcache=new float[3*n];
+ splitcache=new int[32];
+ fdrffti(n, trigcache, splitcache);
+ }
+
+ void clear(){
+ if(trigcache!=null)
+ trigcache=null;
+ if(splitcache!=null)
+ splitcache=null;
+ }
+
+ static int[] ntryh= {4, 2, 3, 5};
+ static float tpi=6.28318530717958647692528676655900577f;
+ static float hsqt2=.70710678118654752440084436210485f;
+ static float taui=.86602540378443864676372317075293618f;
+ static float taur=-.5f;
+ static float sqrt2=1.4142135623730950488016887242097f;
+
+ static void drfti1(int n, float[] wa, int index, int[] ifac){
+ float arg, argh, argld, fi;
+ int ntry=0, i, j=-1;
+ int k1, l1, l2, ib;
+ int ld, ii, ip, is, nq, nr;
+ int ido, ipm, nfm1;
+ int nl=n;
+ int nf=0;
+
+ int state=101;
+
+ loop: while(true){
+ switch(state){
+ case 101:
+ j++;
+ if(j<4)
+ ntry=ntryh[j];
+ else
+ ntry+=2;
+ case 104:
+ nq=nl/ntry;
+ nr=nl-ntry*nq;
+ if(nr!=0){
+ state=101;
+ break;
+ }
+ nf++;
+ ifac[nf+1]=ntry;
+ nl=nq;
+ if(ntry!=2){
+ state=107;
+ break;
+ }
+ if(nf==1){
+ state=107;
+ break;
+ }
+
+ for(i=1; i<nf; i++){
+ ib=nf-i+1;
+ ifac[ib+1]=ifac[ib];
+ }
+ ifac[2]=2;
+ case 107:
+ if(nl!=1){
+ state=104;
+ break;
+ }
+ ifac[0]=n;
+ ifac[1]=nf;
+ argh=tpi/n;
+ is=0;
+ nfm1=nf-1;
+ l1=1;
+
+ if(nfm1==0)
+ return;
+
+ for(k1=0; k1<nfm1; k1++){
+ ip=ifac[k1+2];
+ ld=0;
+ l2=l1*ip;
+ ido=n/l2;
+ ipm=ip-1;
+
+ for(j=0; j<ipm; j++){
+ ld+=l1;
+ i=is;
+ argld=(float)ld*argh;
+ fi=0.f;
+ for(ii=2; ii<ido; ii+=2){
+ fi+=1.f;
+ arg=fi*argld;
+ wa[index+i++]=(float)Math.cos(arg);
+ wa[index+i++]=(float)Math.sin(arg);
+ }
+ is+=ido;
+ }
+ l1=l2;
+ }
+ break loop;
+ }
+ }
+ }
+
+ static void fdrffti(int n, float[] wsave, int[] ifac){
+ if(n==1)
+ return;
+ drfti1(n, wsave, n, ifac);
+ }
+
+ static void dradf2(int ido, int l1, float[] cc, float[] ch, float[] wa1,
+ int index){
+ int i, k;
+ float ti2, tr2;
+ int t0, t1, t2, t3, t4, t5, t6;
+
+ t1=0;
+ t0=(t2=l1*ido);
+ t3=ido<<1;
+ for(k=0; k<l1; k++){
+ ch[t1<<1]=cc[t1]+cc[t2];
+ ch[(t1<<1)+t3-1]=cc[t1]-cc[t2];
+ t1+=ido;
+ t2+=ido;
+ }
+
+ if(ido<2)
+ return;
+
+ if(ido!=2){
+ t1=0;
+ t2=t0;
+ for(k=0; k<l1; k++){
+ t3=t2;
+ t4=(t1<<1)+(ido<<1);
+ t5=t1;
+ t6=t1+t1;
+ for(i=2; i<ido; i+=2){
+ t3+=2;
+ t4-=2;
+ t5+=2;
+ t6+=2;
+ tr2=wa1[index+i-2]*cc[t3-1]+wa1[index+i-1]*cc[t3];
+ ti2=wa1[index+i-2]*cc[t3]-wa1[index+i-1]*cc[t3-1];
+ ch[t6]=cc[t5]+ti2;
+ ch[t4]=ti2-cc[t5];
+ ch[t6-1]=cc[t5-1]+tr2;
+ ch[t4-1]=cc[t5-1]-tr2;
+ }
+ t1+=ido;
+ t2+=ido;
+ }
+ if(ido%2==1)
+ return;
+ }
+
+ t3=(t2=(t1=ido)-1);
+ t2+=t0;
+ for(k=0; k<l1; k++){
+ ch[t1]=-cc[t2];
+ ch[t1-1]=cc[t3];
+ t1+=ido<<1;
+ t2+=ido;
+ t3+=ido;
+ }
+ }
+
+ static void dradf4(int ido, int l1, float[] cc, float[] ch, float[] wa1,
+ int index1, float[] wa2, int index2, float[] wa3, int index3){
+ int i, k, t0, t1, t2, t3, t4, t5, t6;
+ float ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+ t0=l1*ido;
+
+ t1=t0;
+ t4=t1<<1;
+ t2=t1+(t1<<1);
+ t3=0;
+
+ for(k=0; k<l1; k++){
+ tr1=cc[t1]+cc[t2];
+ tr2=cc[t3]+cc[t4];
+
+ ch[t5=t3<<2]=tr1+tr2;
+ ch[(ido<<2)+t5-1]=tr2-tr1;
+ ch[(t5+=(ido<<1))-1]=cc[t3]-cc[t4];
+ ch[t5]=cc[t2]-cc[t1];
+
+ t1+=ido;
+ t2+=ido;
+ t3+=ido;
+ t4+=ido;
+ }
+ if(ido<2)
+ return;
+
+ if(ido!=2){
+ t1=0;
+ for(k=0; k<l1; k++){
+ t2=t1;
+ t4=t1<<2;
+ t5=(t6=ido<<1)+t4;
+ for(i=2; i<ido; i+=2){
+ t3=(t2+=2);
+ t4+=2;
+ t5-=2;
+
+ t3+=t0;
+ cr2=wa1[index1+i-2]*cc[t3-1]+wa1[index1+i-1]*cc[t3];
+ ci2=wa1[index1+i-2]*cc[t3]-wa1[index1+i-1]*cc[t3-1];
+ t3+=t0;
+ cr3=wa2[index2+i-2]*cc[t3-1]+wa2[index2+i-1]*cc[t3];
+ ci3=wa2[index2+i-2]*cc[t3]-wa2[index2+i-1]*cc[t3-1];
+ t3+=t0;
+ cr4=wa3[index3+i-2]*cc[t3-1]+wa3[index3+i-1]*cc[t3];
+ ci4=wa3[index3+i-2]*cc[t3]-wa3[index3+i-1]*cc[t3-1];
+
+ tr1=cr2+cr4;
+ tr4=cr4-cr2;
+ ti1=ci2+ci4;
+ ti4=ci2-ci4;
+
+ ti2=cc[t2]+ci3;
+ ti3=cc[t2]-ci3;
+ tr2=cc[t2-1]+cr3;
+ tr3=cc[t2-1]-cr3;
+
+ ch[t4-1]=tr1+tr2;
+ ch[t4]=ti1+ti2;
+
+ ch[t5-1]=tr3-ti4;
+ ch[t5]=tr4-ti3;
+
+ ch[t4+t6-1]=ti4+tr3;
+ ch[t4+t6]=tr4+ti3;
+
+ ch[t5+t6-1]=tr2-tr1;
+ ch[t5+t6]=ti1-ti2;
+ }
+ t1+=ido;
+ }
+ if((ido&1)!=0)
+ return;
+ }
+
+ t2=(t1=t0+ido-1)+(t0<<1);
+ t3=ido<<2;
+ t4=ido;
+ t5=ido<<1;
+ t6=ido;
+
+ for(k=0; k<l1; k++){
+ ti1=-hsqt2*(cc[t1]+cc[t2]);
+ tr1=hsqt2*(cc[t1]-cc[t2]);
+
+ ch[t4-1]=tr1+cc[t6-1];
+ ch[t4+t5-1]=cc[t6-1]-tr1;
+
+ ch[t4]=ti1-cc[t1+t0];
+ ch[t4+t5]=ti1+cc[t1+t0];
+
+ t1+=ido;
+ t2+=ido;
+ t4+=t3;
+ t6+=ido;
+ }
+ }
+
+ static void dradfg(int ido, int ip, int l1, int idl1, float[] cc, float[] c1,
+ float[] c2, float[] ch, float[] ch2, float[] wa, int index){
+ int idij, ipph, i, j, k, l, ic, ik, is;
+ int t0, t1, t2=0, t3, t4, t5, t6, t7, t8, t9, t10;
+ float dc2, ai1, ai2, ar1, ar2, ds2;
+ int nbd;
+ float dcp=0, arg, dsp=0, ar1h, ar2h;
+ int idp2, ipp2;
+
+ arg=tpi/(float)ip;
+ dcp=(float)Math.cos(arg);
+ dsp=(float)Math.sin(arg);
+ ipph=(ip+1)>>1;
+ ipp2=ip;
+ idp2=ido;
+ nbd=(ido-1)>>1;
+ t0=l1*ido;
+ t10=ip*ido;
+
+ int state=100;
+ loop: while(true){
+ switch(state){
+ case 101:
+ if(ido==1){
+ state=119;
+ break;
+ }
+ for(ik=0; ik<idl1; ik++)
+ ch2[ik]=c2[ik];
+
+ t1=0;
+ for(j=1; j<ip; j++){
+ t1+=t0;
+ t2=t1;
+ for(k=0; k<l1; k++){
+ ch[t2]=c1[t2];
+ t2+=ido;
+ }
+ }
+
+ is=-ido;
+ t1=0;
+ if(nbd>l1){
+ for(j=1; j<ip; j++){
+ t1+=t0;
+ is+=ido;
+ t2=-ido+t1;
+ for(k=0; k<l1; k++){
+ idij=is-1;
+ t2+=ido;
+ t3=t2;
+ for(i=2; i<ido; i+=2){
+ idij+=2;
+ t3+=2;
+ ch[t3-1]=wa[index+idij-1]*c1[t3-1]+wa[index+idij]*c1[t3];
+ ch[t3]=wa[index+idij-1]*c1[t3]-wa[index+idij]*c1[t3-1];
+ }
+ }
+ }
+ }
+ else{
+
+ for(j=1; j<ip; j++){
+ is+=ido;
+ idij=is-1;
+ t1+=t0;
+ t2=t1;
+ for(i=2; i<ido; i+=2){
+ idij+=2;
+ t2+=2;
+ t3=t2;
+ for(k=0; k<l1; k++){
+ ch[t3-1]=wa[index+idij-1]*c1[t3-1]+wa[index+idij]*c1[t3];
+ ch[t3]=wa[index+idij-1]*c1[t3]-wa[index+idij]*c1[t3-1];
+ t3+=ido;
+ }
+ }
+ }
+ }
+
+ t1=0;
+ t2=ipp2*t0;
+ if(nbd<l1){
+ for(j=1; j<ipph; j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(i=2; i<ido; i+=2){
+ t3+=2;
+ t4+=2;
+ t5=t3-ido;
+ t6=t4-ido;
+ for(k=0; k<l1; k++){
+ t5+=ido;
+ t6+=ido;
+ c1[t5-1]=ch[t5-1]+ch[t6-1];
+ c1[t6-1]=ch[t5]-ch[t6];
+ c1[t5]=ch[t5]+ch[t6];
+ c1[t6]=ch[t6-1]-ch[t5-1];
+ }
+ }
+ }
+ }
+ else{
+ for(j=1; j<ipph; j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(k=0; k<l1; k++){
+ t5=t3;
+ t6=t4;
+ for(i=2; i<ido; i+=2){
+ t5+=2;
+ t6+=2;
+ c1[t5-1]=ch[t5-1]+ch[t6-1];
+ c1[t6-1]=ch[t5]-ch[t6];
+ c1[t5]=ch[t5]+ch[t6];
+ c1[t6]=ch[t6-1]-ch[t5-1];
+ }
+ t3+=ido;
+ t4+=ido;
+ }
+ }
+ }
+ case 119:
+ for(ik=0; ik<idl1; ik++)
+ c2[ik]=ch2[ik];
+
+ t1=0;
+ t2=ipp2*idl1;
+ for(j=1; j<ipph; j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1-ido;
+ t4=t2-ido;
+ for(k=0; k<l1; k++){
+ t3+=ido;
+ t4+=ido;
+ c1[t3]=ch[t3]+ch[t4];
+ c1[t4]=ch[t4]-ch[t3];
+ }
+ }
+
+ ar1=1.f;
+ ai1=0.f;
+ t1=0;
+ t2=ipp2*idl1;
+ t3=(ip-1)*idl1;
+ for(l=1; l<ipph; l++){
+ t1+=idl1;
+ t2-=idl1;
+ ar1h=dcp*ar1-dsp*ai1;
+ ai1=dcp*ai1+dsp*ar1;
+ ar1=ar1h;
+ t4=t1;
+ t5=t2;
+ t6=t3;
+ t7=idl1;
+
+ for(ik=0; ik<idl1; ik++){
+ ch2[t4++]=c2[ik]+ar1*c2[t7++];
+ ch2[t5++]=ai1*c2[t6++];
+ }
+
+ dc2=ar1;
+ ds2=ai1;
+ ar2=ar1;
+ ai2=ai1;
+
+ t4=idl1;
+ t5=(ipp2-1)*idl1;
+ for(j=2; j<ipph; j++){
+ t4+=idl1;
+ t5-=idl1;
+
+ ar2h=dc2*ar2-ds2*ai2;
+ ai2=dc2*ai2+ds2*ar2;
+ ar2=ar2h;
+
+ t6=t1;
+ t7=t2;
+ t8=t4;
+ t9=t5;
+ for(ik=0; ik<idl1; ik++){
+ ch2[t6++]+=ar2*c2[t8++];
+ ch2[t7++]+=ai2*c2[t9++];
+ }
+ }
+ }
+ t1=0;
+ for(j=1; j<ipph; j++){
+ t1+=idl1;
+ t2=t1;
+ for(ik=0; ik<idl1; ik++)
+ ch2[ik]+=c2[t2++];
+ }
+
+ if(ido<l1){
+ state=132;
+ break;
+ }
+
+ t1=0;
+ t2=0;
+ for(k=0; k<l1; k++){
+ t3=t1;
+ t4=t2;
+ for(i=0; i<ido; i++)
+ cc[t4++]=ch[t3++];
+ t1+=ido;
+ t2+=t10;
+ }
+ state=135;
+ break;
+
+ case 132:
+ for(i=0; i<ido; i++){
+ t1=i;
+ t2=i;
+ for(k=0; k<l1; k++){
+ cc[t2]=ch[t1];
+ t1+=ido;
+ t2+=t10;
+ }
+ }
+ case 135:
+ t1=0;
+ t2=ido<<1;
+ t3=0;
+ t4=ipp2*t0;
+ for(j=1; j<ipph; j++){
+ t1+=t2;
+ t3+=t0;
+ t4-=t0;
+
+ t5=t1;
+ t6=t3;
+ t7=t4;
+
+ for(k=0; k<l1; k++){
+ cc[t5-1]=ch[t6];
+ cc[t5]=ch[t7];
+ t5+=t10;
+ t6+=ido;
+ t7+=ido;
+ }
+ }
+
+ if(ido==1)
+ return;
+ if(nbd<l1){
+ state=141;
+ break;
+ }
+
+ t1=-ido;
+ t3=0;
+ t4=0;
+ t5=ipp2*t0;
+ for(j=1; j<ipph; j++){
+ t1+=t2;
+ t3+=t2;
+ t4+=t0;
+ t5-=t0;
+ t6=t1;
+ t7=t3;
+ t8=t4;
+ t9=t5;
+ for(k=0; k<l1; k++){
+ for(i=2; i<ido; i+=2){
+ ic=idp2-i;
+ cc[i+t7-1]=ch[i+t8-1]+ch[i+t9-1];
+ cc[ic+t6-1]=ch[i+t8-1]-ch[i+t9-1];
+ cc[i+t7]=ch[i+t8]+ch[i+t9];
+ cc[ic+t6]=ch[i+t9]-ch[i+t8];
+ }
+ t6+=t10;
+ t7+=t10;
+ t8+=ido;
+ t9+=ido;
+ }
+ }
+ return;
+ case 141:
+ t1=-ido;
+ t3=0;
+ t4=0;
+ t5=ipp2*t0;
+ for(j=1; j<ipph; j++){
+ t1+=t2;
+ t3+=t2;
+ t4+=t0;
+ t5-=t0;
+ for(i=2; i<ido; i+=2){
+ t6=idp2+t1-i;
+ t7=i+t3;
+ t8=i+t4;
+ t9=i+t5;
+ for(k=0; k<l1; k++){
+ cc[t7-1]=ch[t8-1]+ch[t9-1];
+ cc[t6-1]=ch[t8-1]-ch[t9-1];
+ cc[t7]=ch[t8]+ch[t9];
+ cc[t6]=ch[t9]-ch[t8];
+ t6+=t10;
+ t7+=t10;
+ t8+=ido;
+ t9+=ido;
+ }
+ }
+ }
+ break loop;
+ }
+ }
+ }
+
+ static void drftf1(int n, float[] c, float[] ch, float[] wa, int[] ifac){
+ int i, k1, l1, l2;
+ int na, kh, nf;
+ int ip, iw, ido, idl1, ix2, ix3;
+
+ nf=ifac[1];
+ na=1;
+ l2=n;
+ iw=n;
+
+ for(k1=0; k1<nf; k1++){
+ kh=nf-k1;
+ ip=ifac[kh+1];
+ l1=l2/ip;
+ ido=n/l2;
+ idl1=ido*l1;
+ iw-=(ip-1)*ido;
+ na=1-na;
+
+ int state=100;
+ loop: while(true){
+ switch(state){
+ case 100:
+ if(ip!=4){
+ state=102;
+ break;
+ }
+
+ ix2=iw+ido;
+ ix3=ix2+ido;
+ if(na!=0)
+ dradf4(ido, l1, ch, c, wa, iw-1, wa, ix2-1, wa, ix3-1);
+ else
+ dradf4(ido, l1, c, ch, wa, iw-1, wa, ix2-1, wa, ix3-1);
+ state=110;
+ break;
+ case 102:
+ if(ip!=2){
+ state=104;
+ break;
+ }
+ if(na!=0){
+ state=103;
+ break;
+ }
+ dradf2(ido, l1, c, ch, wa, iw-1);
+ state=110;
+ break;
+ case 103:
+ dradf2(ido, l1, ch, c, wa, iw-1);
+ case 104:
+ if(ido==1)
+ na=1-na;
+ if(na!=0){
+ state=109;
+ break;
+ }
+ dradfg(ido, ip, l1, idl1, c, c, c, ch, ch, wa, iw-1);
+ na=1;
+ state=110;
+ break;
+ case 109:
+ dradfg(ido, ip, l1, idl1, ch, ch, ch, c, c, wa, iw-1);
+ na=0;
+ case 110:
+ l2=l1;
+ break loop;
+ }
+ }
+ }
+ if(na==1)
+ return;
+ for(i=0; i<n; i++)
+ c[i]=ch[i];
+ }
+
+ static void dradb2(int ido, int l1, float[] cc, float[] ch, float[] wa1,
+ int index){
+ int i, k, t0, t1, t2, t3, t4, t5, t6;
+ float ti2, tr2;
+
+ t0=l1*ido;
+
+ t1=0;
+ t2=0;
+ t3=(ido<<1)-1;
+ for(k=0; k<l1; k++){
+ ch[t1]=cc[t2]+cc[t3+t2];
+ ch[t1+t0]=cc[t2]-cc[t3+t2];
+ t2=(t1+=ido)<<1;
+ }
+
+ if(ido<2)
+ return;
+ if(ido!=2){
+ t1=0;
+ t2=0;
+ for(k=0; k<l1; k++){
+ t3=t1;
+ t5=(t4=t2)+(ido<<1);
+ t6=t0+t1;
+ for(i=2; i<ido; i+=2){
+ t3+=2;
+ t4+=2;
+ t5-=2;
+ t6+=2;
+ ch[t3-1]=cc[t4-1]+cc[t5-1];
+ tr2=cc[t4-1]-cc[t5-1];
+ ch[t3]=cc[t4]-cc[t5];
+ ti2=cc[t4]+cc[t5];
+ ch[t6-1]=wa1[index+i-2]*tr2-wa1[index+i-1]*ti2;
+ ch[t6]=wa1[index+i-2]*ti2+wa1[index+i-1]*tr2;
+ }
+ t2=(t1+=ido)<<1;
+ }
+ if((ido%2)==1)
+ return;
+ }
+
+ t1=ido-1;
+ t2=ido-1;
+ for(k=0; k<l1; k++){
+ ch[t1]=cc[t2]+cc[t2];
+ ch[t1+t0]=-(cc[t2+1]+cc[t2+1]);
+ t1+=ido;
+ t2+=ido<<1;
+ }
+ }
+
+ static void dradb3(int ido, int l1, float[] cc, float[] ch, float[] wa1,
+ int index1, float[] wa2, int index2){
+ int i, k, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10;
+ float ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2;
+ t0=l1*ido;
+
+ t1=0;
+ t2=t0<<1;
+ t3=ido<<1;
+ t4=ido+(ido<<1);
+ t5=0;
+ for(k=0; k<l1; k++){
+ tr2=cc[t3-1]+cc[t3-1];
+ cr2=cc[t5]+(taur*tr2);
+ ch[t1]=cc[t5]+tr2;
+ ci3=taui*(cc[t3]+cc[t3]);
+ ch[t1+t0]=cr2-ci3;
+ ch[t1+t2]=cr2+ci3;
+ t1+=ido;
+ t3+=t4;
+ t5+=t4;
+ }
+
+ if(ido==1)
+ return;
+
+ t1=0;
+ t3=ido<<1;
+ for(k=0; k<l1; k++){
+ t7=t1+(t1<<1);
+ t6=(t5=t7+t3);
+ t8=t1;
+ t10=(t9=t1+t0)+t0;
+
+ for(i=2; i<ido; i+=2){
+ t5+=2;
+ t6-=2;
+ t7+=2;
+ t8+=2;
+ t9+=2;
+ t10+=2;
+ tr2=cc[t5-1]+cc[t6-1];
+ cr2=cc[t7-1]+(taur*tr2);
+ ch[t8-1]=cc[t7-1]+tr2;
+ ti2=cc[t5]-cc[t6];
+ ci2=cc[t7]+(taur*ti2);
+ ch[t8]=cc[t7]+ti2;
+ cr3=taui*(cc[t5-1]-cc[t6-1]);
+ ci3=taui*(cc[t5]+cc[t6]);
+ dr2=cr2-ci3;
+ dr3=cr2+ci3;
+ di2=ci2+cr3;
+ di3=ci2-cr3;
+ ch[t9-1]=wa1[index1+i-2]*dr2-wa1[index1+i-1]*di2;
+ ch[t9]=wa1[index1+i-2]*di2+wa1[index1+i-1]*dr2;
+ ch[t10-1]=wa2[index2+i-2]*dr3-wa2[index2+i-1]*di3;
+ ch[t10]=wa2[index2+i-2]*di3+wa2[index2+i-1]*dr3;
+ }
+ t1+=ido;
+ }
+ }
+
+ static void dradb4(int ido, int l1, float[] cc, float[] ch, float[] wa1,
+ int index1, float[] wa2, int index2, float[] wa3, int index3){
+ int i, k, t0, t1, t2, t3, t4, t5, t6, t7, t8;
+ float ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+ t0=l1*ido;
+
+ t1=0;
+ t2=ido<<2;
+ t3=0;
+ t6=ido<<1;
+ for(k=0; k<l1; k++){
+ t4=t3+t6;
+ t5=t1;
+ tr3=cc[t4-1]+cc[t4-1];
+ tr4=cc[t4]+cc[t4];
+ tr1=cc[t3]-cc[(t4+=t6)-1];
+ tr2=cc[t3]+cc[t4-1];
+ ch[t5]=tr2+tr3;
+ ch[t5+=t0]=tr1-tr4;
+ ch[t5+=t0]=tr2-tr3;
+ ch[t5+=t0]=tr1+tr4;
+ t1+=ido;
+ t3+=t2;
+ }
+
+ if(ido<2)
+ return;
+ if(ido!=2){
+ t1=0;
+ for(k=0; k<l1; k++){
+ t5=(t4=(t3=(t2=t1<<2)+t6))+t6;
+ t7=t1;
+ for(i=2; i<ido; i+=2){
+ t2+=2;
+ t3+=2;
+ t4-=2;
+ t5-=2;
+ t7+=2;
+ ti1=cc[t2]+cc[t5];
+ ti2=cc[t2]-cc[t5];
+ ti3=cc[t3]-cc[t4];
+ tr4=cc[t3]+cc[t4];
+ tr1=cc[t2-1]-cc[t5-1];
+ tr2=cc[t2-1]+cc[t5-1];
+ ti4=cc[t3-1]-cc[t4-1];
+ tr3=cc[t3-1]+cc[t4-1];
+ ch[t7-1]=tr2+tr3;
+ cr3=tr2-tr3;
+ ch[t7]=ti2+ti3;
+ ci3=ti2-ti3;
+ cr2=tr1-tr4;
+ cr4=tr1+tr4;
+ ci2=ti1+ti4;
+ ci4=ti1-ti4;
+
+ ch[(t8=t7+t0)-1]=wa1[index1+i-2]*cr2-wa1[index1+i-1]*ci2;
+ ch[t8]=wa1[index1+i-2]*ci2+wa1[index1+i-1]*cr2;
+ ch[(t8+=t0)-1]=wa2[index2+i-2]*cr3-wa2[index2+i-1]*ci3;
+ ch[t8]=wa2[index2+i-2]*ci3+wa2[index2+i-1]*cr3;
+ ch[(t8+=t0)-1]=wa3[index3+i-2]*cr4-wa3[index3+i-1]*ci4;
+ ch[t8]=wa3[index3+i-2]*ci4+wa3[index3+i-1]*cr4;
+ }
+ t1+=ido;
+ }
+ if(ido%2==1)
+ return;
+ }
+
+ t1=ido;
+ t2=ido<<2;
+ t3=ido-1;
+ t4=ido+(ido<<1);
+ for(k=0; k<l1; k++){
+ t5=t3;
+ ti1=cc[t1]+cc[t4];
+ ti2=cc[t4]-cc[t1];
+ tr1=cc[t1-1]-cc[t4-1];
+ tr2=cc[t1-1]+cc[t4-1];
+ ch[t5]=tr2+tr2;
+ ch[t5+=t0]=sqrt2*(tr1-ti1);
+ ch[t5+=t0]=ti2+ti2;
+ ch[t5+=t0]=-sqrt2*(tr1+ti1);
+
+ t3+=ido;
+ t1+=t2;
+ t4+=t2;
+ }
+ }
+
+ static void dradbg(int ido, int ip, int l1, int idl1, float[] cc, float[] c1,
+ float[] c2, float[] ch, float[] ch2, float[] wa, int index){
+
+ int idij, ipph=0, i, j, k, l, ik, is, t0=0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10=0, t11, t12;
+ float dc2, ai1, ai2, ar1, ar2, ds2;
+ int nbd=0;
+ float dcp=0, arg, dsp=0, ar1h, ar2h;
+ int ipp2=0;
+
+ int state=100;
+
+ loop: while(true){
+ switch(state){
+ case 100:
+ t10=ip*ido;
+ t0=l1*ido;
+ arg=tpi/(float)ip;
+ dcp=(float)Math.cos(arg);
+ dsp=(float)Math.sin(arg);
+ nbd=(ido-1)>>>1;
+ ipp2=ip;
+ ipph=(ip+1)>>>1;
+ if(ido<l1){
+ state=103;
+ break;
+ }
+ t1=0;
+ t2=0;
+ for(k=0; k<l1; k++){
+ t3=t1;
+ t4=t2;
+ for(i=0; i<ido; i++){
+ ch[t3]=cc[t4];
+ t3++;
+ t4++;
+ }
+ t1+=ido;
+ t2+=t10;
+ }
+ state=106;
+ break;
+ case 103:
+ t1=0;
+ for(i=0; i<ido; i++){
+ t2=t1;
+ t3=t1;
+ for(k=0; k<l1; k++){
+ ch[t2]=cc[t3];
+ t2+=ido;
+ t3+=t10;
+ }
+ t1++;
+ }
+ case 106:
+ t1=0;
+ t2=ipp2*t0;
+ t7=(t5=ido<<1);
+ for(j=1; j<ipph; j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ t6=t5;
+ for(k=0; k<l1; k++){
+ ch[t3]=cc[t6-1]+cc[t6-1];
+ ch[t4]=cc[t6]+cc[t6];
+ t3+=ido;
+ t4+=ido;
+ t6+=t10;
+ }
+ t5+=t7;
+ }
+ if(ido==1){
+ state=116;
+ break;
+ }
+ if(nbd<l1){
+ state=112;
+ break;
+ }
+
+ t1=0;
+ t2=ipp2*t0;
+ t7=0;
+ for(j=1; j<ipph; j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+
+ t7+=(ido<<1);
+ t8=t7;
+ for(k=0; k<l1; k++){
+ t5=t3;
+ t6=t4;
+ t9=t8;
+ t11=t8;
+ for(i=2; i<ido; i+=2){
+ t5+=2;
+ t6+=2;
+ t9+=2;
+ t11-=2;
+ ch[t5-1]=cc[t9-1]+cc[t11-1];
+ ch[t6-1]=cc[t9-1]-cc[t11-1];
+ ch[t5]=cc[t9]-cc[t11];
+ ch[t6]=cc[t9]+cc[t11];
+ }
+ t3+=ido;
+ t4+=ido;
+ t8+=t10;
+ }
+ }
+ state=116;
+ break;
+ case 112:
+ t1=0;
+ t2=ipp2*t0;
+ t7=0;
+ for(j=1; j<ipph; j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ t7+=(ido<<1);
+ t8=t7;
+ t9=t7;
+ for(i=2; i<ido; i+=2){
+ t3+=2;
+ t4+=2;
+ t8+=2;
+ t9-=2;
+ t5=t3;
+ t6=t4;
+ t11=t8;
+ t12=t9;
+ for(k=0; k<l1; k++){
+ ch[t5-1]=cc[t11-1]+cc[t12-1];
+ ch[t6-1]=cc[t11-1]-cc[t12-1];
+ ch[t5]=cc[t11]-cc[t12];
+ ch[t6]=cc[t11]+cc[t12];
+ t5+=ido;
+ t6+=ido;
+ t11+=t10;
+ t12+=t10;
+ }
+ }
+ }
+ case 116:
+ ar1=1.f;
+ ai1=0.f;
+ t1=0;
+ t9=(t2=ipp2*idl1);
+ t3=(ip-1)*idl1;
+ for(l=1; l<ipph; l++){
+ t1+=idl1;
+ t2-=idl1;
+
+ ar1h=dcp*ar1-dsp*ai1;
+ ai1=dcp*ai1+dsp*ar1;
+ ar1=ar1h;
+ t4=t1;
+ t5=t2;
+ t6=0;
+ t7=idl1;
+ t8=t3;
+ for(ik=0; ik<idl1; ik++){
+ c2[t4++]=ch2[t6++]+ar1*ch2[t7++];
+ c2[t5++]=ai1*ch2[t8++];
+ }
+ dc2=ar1;
+ ds2=ai1;
+ ar2=ar1;
+ ai2=ai1;
+
+ t6=idl1;
+ t7=t9-idl1;
+ for(j=2; j<ipph; j++){
+ t6+=idl1;
+ t7-=idl1;
+ ar2h=dc2*ar2-ds2*ai2;
+ ai2=dc2*ai2+ds2*ar2;
+ ar2=ar2h;
+ t4=t1;
+ t5=t2;
+ t11=t6;
+ t12=t7;
+ for(ik=0; ik<idl1; ik++){
+ c2[t4++]+=ar2*ch2[t11++];
+ c2[t5++]+=ai2*ch2[t12++];
+ }
+ }
+ }
+
+ t1=0;
+ for(j=1; j<ipph; j++){
+ t1+=idl1;
+ t2=t1;
+ for(ik=0; ik<idl1; ik++)
+ ch2[ik]+=ch2[t2++];
+ }
+
+ t1=0;
+ t2=ipp2*t0;
+ for(j=1; j<ipph; j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(k=0; k<l1; k++){
+ ch[t3]=c1[t3]-c1[t4];
+ ch[t4]=c1[t3]+c1[t4];
+ t3+=ido;
+ t4+=ido;
+ }
+ }
+
+ if(ido==1){
+ state=132;
+ break;
+ }
+ if(nbd<l1){
+ state=128;
+ break;
+ }
+
+ t1=0;
+ t2=ipp2*t0;
+ for(j=1; j<ipph; j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(k=0; k<l1; k++){
+ t5=t3;
+ t6=t4;
+ for(i=2; i<ido; i+=2){
+ t5+=2;
+ t6+=2;
+ ch[t5-1]=c1[t5-1]-c1[t6];
+ ch[t6-1]=c1[t5-1]+c1[t6];
+ ch[t5]=c1[t5]+c1[t6-1];
+ ch[t6]=c1[t5]-c1[t6-1];
+ }
+ t3+=ido;
+ t4+=ido;
+ }
+ }
+ state=132;
+ break;
+ case 128:
+ t1=0;
+ t2=ipp2*t0;
+ for(j=1; j<ipph; j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(i=2; i<ido; i+=2){
+ t3+=2;
+ t4+=2;
+ t5=t3;
+ t6=t4;
+ for(k=0; k<l1; k++){
+ ch[t5-1]=c1[t5-1]-c1[t6];
+ ch[t6-1]=c1[t5-1]+c1[t6];
+ ch[t5]=c1[t5]+c1[t6-1];
+ ch[t6]=c1[t5]-c1[t6-1];
+ t5+=ido;
+ t6+=ido;
+ }
+ }
+ }
+ case 132:
+ if(ido==1)
+ return;
+
+ for(ik=0; ik<idl1; ik++)
+ c2[ik]=ch2[ik];
+
+ t1=0;
+ for(j=1; j<ip; j++){
+ t2=(t1+=t0);
+ for(k=0; k<l1; k++){
+ c1[t2]=ch[t2];
+ t2+=ido;
+ }
+ }
+
+ if(nbd>l1){
+ state=139;
+ break;
+ }
+
+ is=-ido-1;
+ t1=0;
+ for(j=1; j<ip; j++){
+ is+=ido;
+ t1+=t0;
+ idij=is;
+ t2=t1;
+ for(i=2; i<ido; i+=2){
+ t2+=2;
+ idij+=2;
+ t3=t2;
+ for(k=0; k<l1; k++){
+ c1[t3-1]=wa[index+idij-1]*ch[t3-1]-wa[index+idij]*ch[t3];
+ c1[t3]=wa[index+idij-1]*ch[t3]+wa[index+idij]*ch[t3-1];
+ t3+=ido;
+ }
+ }
+ }
+ return;
+
+ case 139:
+ is=-ido-1;
+ t1=0;
+ for(j=1; j<ip; j++){
+ is+=ido;
+ t1+=t0;
+ t2=t1;
+ for(k=0; k<l1; k++){
+ idij=is;
+ t3=t2;
+ for(i=2; i<ido; i+=2){
+ idij+=2;
+ t3+=2;
+ c1[t3-1]=wa[index+idij-1]*ch[t3-1]-wa[index+idij]*ch[t3];
+ c1[t3]=wa[index+idij-1]*ch[t3]+wa[index+idij]*ch[t3-1];
+ }
+ t2+=ido;
+ }
+ }
+ break loop;
+ }
+ }
+ }
+
+ static void drftb1(int n, float[] c, float[] ch, float[] wa, int index,
+ int[] ifac){
+ int i, k1, l1, l2=0;
+ int na;
+ int nf, ip=0, iw, ix2, ix3, ido=0, idl1=0;
+
+ nf=ifac[1];
+ na=0;
+ l1=1;
+ iw=1;
+
+ for(k1=0; k1<nf; k1++){
+ int state=100;
+ loop: while(true){
+ switch(state){
+ case 100:
+ ip=ifac[k1+2];
+ l2=ip*l1;
+ ido=n/l2;
+ idl1=ido*l1;
+ if(ip!=4){
+ state=103;
+ break;
+ }
+ ix2=iw+ido;
+ ix3=ix2+ido;
+
+ if(na!=0)
+ dradb4(ido, l1, ch, c, wa, index+iw-1, wa, index+ix2-1, wa, index
+ +ix3-1);
+ else
+ dradb4(ido, l1, c, ch, wa, index+iw-1, wa, index+ix2-1, wa, index
+ +ix3-1);
+ na=1-na;
+ state=115;
+ break;
+ case 103:
+ if(ip!=2){
+ state=106;
+ break;
+ }
+
+ if(na!=0)
+ dradb2(ido, l1, ch, c, wa, index+iw-1);
+ else
+ dradb2(ido, l1, c, ch, wa, index+iw-1);
+ na=1-na;
+ state=115;
+ break;
+
+ case 106:
+ if(ip!=3){
+ state=109;
+ break;
+ }
+
+ ix2=iw+ido;
+ if(na!=0)
+ dradb3(ido, l1, ch, c, wa, index+iw-1, wa, index+ix2-1);
+ else
+ dradb3(ido, l1, c, ch, wa, index+iw-1, wa, index+ix2-1);
+ na=1-na;
+ state=115;
+ break;
+ case 109:
+ if(na!=0)
+ dradbg(ido, ip, l1, idl1, ch, ch, ch, c, c, wa, index+iw-1);
+ else
+ dradbg(ido, ip, l1, idl1, c, c, c, ch, ch, wa, index+iw-1);
+ if(ido==1)
+ na=1-na;
+
+ case 115:
+ l1=l2;
+ iw+=(ip-1)*ido;
+ break loop;
+ }
+ }
+ }
+ if(na==0)
+ return;
+ for(i=0; i<n; i++)
+ c[i]=ch[i];
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/DspState.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/DspState.java
new file mode 100644
index 0000000..052482d
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/DspState.java
@@ -0,0 +1,376 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+public class DspState{
+ static final float M_PI=3.1415926539f;
+ static final int VI_TRANSFORMB=1;
+ static final int VI_WINDOWB=1;
+
+ int analysisp;
+ Info vi;
+ int modebits;
+
+ float[][] pcm;
+ int pcm_storage;
+ int pcm_current;
+ int pcm_returned;
+
+ float[] multipliers;
+ int envelope_storage;
+ int envelope_current;
+
+ int eofflag;
+
+ int lW;
+ int W;
+ int nW;
+ int centerW;
+
+ long granulepos;
+ long sequence;
+
+ long glue_bits;
+ long time_bits;
+ long floor_bits;
+ long res_bits;
+
+ // local lookup storage
+ float[][][][][] window; // block, leadin, leadout, type
+ Object[][] transform;
+ CodeBook[] fullbooks;
+ // backend lookups are tied to the mode, not the backend or naked mapping
+ Object[] mode;
+
+ // local storage, only used on the encoding side. This way the
+ // application does not need to worry about freeing some packets'
+ // memory and not others'; packet storage is always tracked.
+ // Cleared next call to a _dsp_ function
+ byte[] header;
+ byte[] header1;
+ byte[] header2;
+
+ public DspState(){
+ transform=new Object[2][];
+ window=new float[2][][][][];
+ window[0]=new float[2][][][];
+ window[0][0]=new float[2][][];
+ window[0][1]=new float[2][][];
+ window[0][0][0]=new float[2][];
+ window[0][0][1]=new float[2][];
+ window[0][1][0]=new float[2][];
+ window[0][1][1]=new float[2][];
+ window[1]=new float[2][][][];
+ window[1][0]=new float[2][][];
+ window[1][1]=new float[2][][];
+ window[1][0][0]=new float[2][];
+ window[1][0][1]=new float[2][];
+ window[1][1][0]=new float[2][];
+ window[1][1][1]=new float[2][];
+ }
+
+ static float[] window(int type, int window, int left, int right){
+ float[] ret=new float[window];
+ switch(type){
+ case 0:
+ // The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi)
+ {
+ int leftbegin=window/4-left/2;
+ int rightbegin=window-window/4-right/2;
+
+ for(int i=0; i<left; i++){
+ float x=(float)((i+.5)/left*M_PI/2.);
+ x=(float)Math.sin(x);
+ x*=x;
+ x*=M_PI/2.;
+ x=(float)Math.sin(x);
+ ret[i+leftbegin]=x;
+ }
+
+ for(int i=leftbegin+left; i<rightbegin; i++){
+ ret[i]=1.f;
+ }
+
+ for(int i=0; i<right; i++){
+ float x=(float)((right-i-.5)/right*M_PI/2.);
+ x=(float)Math.sin(x);
+ x*=x;
+ x*=M_PI/2.;
+ x=(float)Math.sin(x);
+ ret[i+rightbegin]=x;
+ }
+ }
+ break;
+ default:
+ //free(ret);
+ return (null);
+ }
+ return (ret);
+ }
+
+ // Analysis side code, but directly related to blocking. Thus it's
+ // here and not in analysis.c (which is for analysis transforms only).
+ // The init is here because some of it is shared
+
+ int init(Info vi, boolean encp){
+ this.vi=vi;
+ modebits=Util.ilog2(vi.modes);
+
+ transform[0]=new Object[VI_TRANSFORMB];
+ transform[1]=new Object[VI_TRANSFORMB];
+
+ // MDCT is tranform 0
+
+ transform[0][0]=new Mdct();
+ transform[1][0]=new Mdct();
+ ((Mdct)transform[0][0]).init(vi.blocksizes[0]);
+ ((Mdct)transform[1][0]).init(vi.blocksizes[1]);
+
+ window[0][0][0]=new float[VI_WINDOWB][];
+ window[0][0][1]=window[0][0][0];
+ window[0][1][0]=window[0][0][0];
+ window[0][1][1]=window[0][0][0];
+ window[1][0][0]=new float[VI_WINDOWB][];
+ window[1][0][1]=new float[VI_WINDOWB][];
+ window[1][1][0]=new float[VI_WINDOWB][];
+ window[1][1][1]=new float[VI_WINDOWB][];
+
+ for(int i=0; i<VI_WINDOWB; i++){
+ window[0][0][0][i]=window(i, vi.blocksizes[0], vi.blocksizes[0]/2,
+ vi.blocksizes[0]/2);
+ window[1][0][0][i]=window(i, vi.blocksizes[1], vi.blocksizes[0]/2,
+ vi.blocksizes[0]/2);
+ window[1][0][1][i]=window(i, vi.blocksizes[1], vi.blocksizes[0]/2,
+ vi.blocksizes[1]/2);
+ window[1][1][0][i]=window(i, vi.blocksizes[1], vi.blocksizes[1]/2,
+ vi.blocksizes[0]/2);
+ window[1][1][1][i]=window(i, vi.blocksizes[1], vi.blocksizes[1]/2,
+ vi.blocksizes[1]/2);
+ }
+
+ fullbooks=new CodeBook[vi.books];
+ for(int i=0; i<vi.books; i++){
+ fullbooks[i]=new CodeBook();
+ fullbooks[i].init_decode(vi.book_param[i]);
+ }
+
+ // initialize the storage vectors to a decent size greater than the
+ // minimum
+
+ pcm_storage=8192; // we'll assume later that we have
+ // a minimum of twice the blocksize of
+ // accumulated samples in analysis
+ pcm=new float[vi.channels][];
+ {
+ for(int i=0; i<vi.channels; i++){
+ pcm[i]=new float[pcm_storage];
+ }
+ }
+
+ // all 1 (large block) or 0 (small block)
+ // explicitly set for the sake of clarity
+ lW=0; // previous window size
+ W=0; // current window size
+
+ // all vector indexes; multiples of samples_per_envelope_step
+ centerW=vi.blocksizes[1]/2;
+
+ pcm_current=centerW;
+
+ // initialize all the mapping/backend lookups
+ mode=new Object[vi.modes];
+ for(int i=0; i<vi.modes; i++){
+ int mapnum=vi.mode_param[i].mapping;
+ int maptype=vi.map_type[mapnum];
+ mode[i]=FuncMapping.mapping_P[maptype].look(this, vi.mode_param[i],
+ vi.map_param[mapnum]);
+ }
+ return (0);
+ }
+
+ public int synthesis_init(Info vi){
+ init(vi, false);
+ // Adjust centerW to allow an easier mechanism for determining output
+ pcm_returned=centerW;
+ centerW-=vi.blocksizes[W]/4+vi.blocksizes[lW]/4;
+ granulepos=-1;
+ sequence=-1;
+ return (0);
+ }
+
+ DspState(Info vi){
+ this();
+ init(vi, false);
+ // Adjust centerW to allow an easier mechanism for determining output
+ pcm_returned=centerW;
+ centerW-=vi.blocksizes[W]/4+vi.blocksizes[lW]/4;
+ granulepos=-1;
+ sequence=-1;
+ }
+
+ // Unike in analysis, the window is only partially applied for each
+ // block. The time domain envelope is not yet handled at the point of
+ // calling (as it relies on the previous block).
+
+ public int synthesis_blockin(Block vb){
+ // Shift out any PCM/multipliers that we returned previously
+ // centerW is currently the center of the last block added
+ if(centerW>vi.blocksizes[1]/2&&pcm_returned>8192){
+ // don't shift too much; we need to have a minimum PCM buffer of
+ // 1/2 long block
+
+ int shiftPCM=centerW-vi.blocksizes[1]/2;
+ shiftPCM=(pcm_returned<shiftPCM ? pcm_returned : shiftPCM);
+
+ pcm_current-=shiftPCM;
+ centerW-=shiftPCM;
+ pcm_returned-=shiftPCM;
+ if(shiftPCM!=0){
+ for(int i=0; i<vi.channels; i++){
+ System.arraycopy(pcm[i], shiftPCM, pcm[i], 0, pcm_current);
+ }
+ }
+ }
+
+ lW=W;
+ W=vb.W;
+ nW=-1;
+
+ glue_bits+=vb.glue_bits;
+ time_bits+=vb.time_bits;
+ floor_bits+=vb.floor_bits;
+ res_bits+=vb.res_bits;
+
+ if(sequence+1!=vb.sequence)
+ granulepos=-1; // out of sequence; lose count
+
+ sequence=vb.sequence;
+
+ {
+ int sizeW=vi.blocksizes[W];
+ int _centerW=centerW+vi.blocksizes[lW]/4+sizeW/4;
+ int beginW=_centerW-sizeW/2;
+ int endW=beginW+sizeW;
+ int beginSl=0;
+ int endSl=0;
+
+ // Do we have enough PCM/mult storage for the block?
+ if(endW>pcm_storage){
+ // expand the storage
+ pcm_storage=endW+vi.blocksizes[1];
+ for(int i=0; i<vi.channels; i++){
+ float[] foo=new float[pcm_storage];
+ System.arraycopy(pcm[i], 0, foo, 0, pcm[i].length);
+ pcm[i]=foo;
+ }
+ }
+
+ // overlap/add PCM
+ switch(W){
+ case 0:
+ beginSl=0;
+ endSl=vi.blocksizes[0]/2;
+ break;
+ case 1:
+ beginSl=vi.blocksizes[1]/4-vi.blocksizes[lW]/4;
+ endSl=beginSl+vi.blocksizes[lW]/2;
+ break;
+ }
+
+ for(int j=0; j<vi.channels; j++){
+ int _pcm=beginW;
+ // the overlap/add section
+ int i=0;
+ for(i=beginSl; i<endSl; i++){
+ pcm[j][_pcm+i]+=vb.pcm[j][i];
+ }
+ // the remaining section
+ for(; i<sizeW; i++){
+ pcm[j][_pcm+i]=vb.pcm[j][i];
+ }
+ }
+
+ // track the frame number... This is for convenience, but also
+ // making sure our last packet doesn't end with added padding. If
+ // the last packet is partial, the number of samples we'll have to
+ // return will be past the vb->granulepos.
+ //
+ // This is not foolproof! It will be confused if we begin
+ // decoding at the last page after a seek or hole. In that case,
+ // we don't have a starting point to judge where the last frame
+ // is. For this reason, vorbisfile will always try to make sure
+ // it reads the last two marked pages in proper sequence
+
+ if(granulepos==-1){
+ granulepos=vb.granulepos;
+ }
+ else{
+ granulepos+=(_centerW-centerW);
+ if(vb.granulepos!=-1&&granulepos!=vb.granulepos){
+ if(granulepos>vb.granulepos&&vb.eofflag!=0){
+ // partial last frame. Strip the padding off
+ _centerW-=(granulepos-vb.granulepos);
+ }// else{ Shouldn't happen *unless* the bitstream is out of
+ // spec. Either way, believe the bitstream }
+ granulepos=vb.granulepos;
+ }
+ }
+
+ // Update, cleanup
+
+ centerW=_centerW;
+ pcm_current=endW;
+ if(vb.eofflag!=0)
+ eofflag=1;
+ }
+ return (0);
+ }
+
+ // pcm==NULL indicates we just want the pending samples, no more
+ public int synthesis_pcmout(float[][][] _pcm, int[] index){
+ if(pcm_returned<centerW){
+ if(_pcm!=null){
+ for(int i=0; i<vi.channels; i++){
+ index[i]=pcm_returned;
+ }
+ _pcm[0]=pcm;
+ }
+ return (centerW-pcm_returned);
+ }
+ return (0);
+ }
+
+ public int synthesis_read(int bytes){
+ if(bytes!=0&&pcm_returned+bytes>centerW)
+ return (-1);
+ pcm_returned+=bytes;
+ return (0);
+ }
+
+ public void clear(){
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Floor0.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Floor0.java
new file mode 100644
index 0000000..02e8393
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Floor0.java
@@ -0,0 +1,335 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+class Floor0 extends FuncFloor{
+
+ void pack(Object i, Buffer opb){
+ InfoFloor0 info=(InfoFloor0)i;
+ opb.write(info.order, 8);
+ opb.write(info.rate, 16);
+ opb.write(info.barkmap, 16);
+ opb.write(info.ampbits, 6);
+ opb.write(info.ampdB, 8);
+ opb.write(info.numbooks-1, 4);
+ for(int j=0; j<info.numbooks; j++)
+ opb.write(info.books[j], 8);
+ }
+
+ Object unpack(Info vi, Buffer opb){
+ InfoFloor0 info=new InfoFloor0();
+ info.order=opb.read(8);
+ info.rate=opb.read(16);
+ info.barkmap=opb.read(16);
+ info.ampbits=opb.read(6);
+ info.ampdB=opb.read(8);
+ info.numbooks=opb.read(4)+1;
+
+ if((info.order<1)||(info.rate<1)||(info.barkmap<1)||(info.numbooks<1)){
+ return (null);
+ }
+
+ for(int j=0; j<info.numbooks; j++){
+ info.books[j]=opb.read(8);
+ if(info.books[j]<0||info.books[j]>=vi.books){
+ return (null);
+ }
+ }
+ return (info);
+ }
+
+ Object look(DspState vd, InfoMode mi, Object i){
+ float scale;
+ Info vi=vd.vi;
+ InfoFloor0 info=(InfoFloor0)i;
+ LookFloor0 look=new LookFloor0();
+ look.m=info.order;
+ look.n=vi.blocksizes[mi.blockflag]/2;
+ look.ln=info.barkmap;
+ look.vi=info;
+ look.lpclook.init(look.ln, look.m);
+
+ // we choose a scaling constant so that:
+ scale=look.ln/toBARK((float)(info.rate/2.));
+
+ // the mapping from a linear scale to a smaller bark scale is
+ // straightforward. We do *not* make sure that the linear mapping
+ // does not skip bark-scale bins; the decoder simply skips them and
+ // the encoder may do what it wishes in filling them. They're
+ // necessary in some mapping combinations to keep the scale spacing
+ // accurate
+ look.linearmap=new int[look.n];
+ for(int j=0; j<look.n; j++){
+ int val=(int)Math.floor(toBARK((float)((info.rate/2.)/look.n*j))*scale); // bark numbers represent band edges
+ if(val>=look.ln)
+ val=look.ln; // guard against the approximation
+ look.linearmap[j]=val;
+ }
+ return look;
+ }
+
+ static float toBARK(float f){
+ return (float)(13.1*Math.atan(.00074*(f))+2.24*Math.atan((f)*(f)*1.85e-8)+1e-4*(f));
+ }
+
+ Object state(Object i){
+ EchstateFloor0 state=new EchstateFloor0();
+ InfoFloor0 info=(InfoFloor0)i;
+
+ // a safe size if usually too big (dim==1)
+ state.codewords=new int[info.order];
+ state.curve=new float[info.barkmap];
+ state.frameno=-1;
+ return (state);
+ }
+
+ void free_info(Object i){
+ }
+
+ void free_look(Object i){
+ }
+
+ void free_state(Object vs){
+ }
+
+ int forward(Block vb, Object i, float[] in, float[] out, Object vs){
+ return 0;
+ }
+
+ float[] lsp=null;
+
+ int inverse(Block vb, Object i, float[] out){
+ //System.err.println("Floor0.inverse "+i.getClass()+"]");
+ LookFloor0 look=(LookFloor0)i;
+ InfoFloor0 info=look.vi;
+ int ampraw=vb.opb.read(info.ampbits);
+ if(ampraw>0){ // also handles the -1 out of data case
+ int maxval=(1<<info.ampbits)-1;
+ float amp=(float)ampraw/maxval*info.ampdB;
+ int booknum=vb.opb.read(Util.ilog(info.numbooks));
+
+ if(booknum!=-1&&booknum<info.numbooks){
+
+ synchronized(this){
+ if(lsp==null||lsp.length<look.m){
+ lsp=new float[look.m];
+ }
+ else{
+ for(int j=0; j<look.m; j++)
+ lsp[j]=0.f;
+ }
+
+ CodeBook b=vb.vd.fullbooks[info.books[booknum]];
+ float last=0.f;
+
+ for(int j=0; j<look.m; j++)
+ out[j]=0.0f;
+
+ for(int j=0; j<look.m; j+=b.dim){
+ if(b.decodevs(lsp, j, vb.opb, 1, -1)==-1){
+ for(int k=0; k<look.n; k++)
+ out[k]=0.0f;
+ return (0);
+ }
+ }
+ for(int j=0; j<look.m;){
+ for(int k=0; k<b.dim; k++, j++)
+ lsp[j]+=last;
+ last=lsp[j-1];
+ }
+ // take the coefficients back to a spectral envelope curve
+ Lsp.lsp_to_curve(out, look.linearmap, look.n, look.ln, lsp, look.m,
+ amp, info.ampdB);
+
+ return (1);
+ }
+ }
+ }
+ return (0);
+ }
+
+ Object inverse1(Block vb, Object i, Object memo){
+ LookFloor0 look=(LookFloor0)i;
+ InfoFloor0 info=look.vi;
+ float[] lsp=null;
+ if(memo instanceof float[]){
+ lsp=(float[])memo;
+ }
+
+ int ampraw=vb.opb.read(info.ampbits);
+ if(ampraw>0){ // also handles the -1 out of data case
+ int maxval=(1<<info.ampbits)-1;
+ float amp=(float)ampraw/maxval*info.ampdB;
+ int booknum=vb.opb.read(Util.ilog(info.numbooks));
+
+ if(booknum!=-1&&booknum<info.numbooks){
+ CodeBook b=vb.vd.fullbooks[info.books[booknum]];
+ float last=0.f;
+
+ if(lsp==null||lsp.length<look.m+1){
+ lsp=new float[look.m+1];
+ }
+ else{
+ for(int j=0; j<lsp.length; j++)
+ lsp[j]=0.f;
+ }
+
+ for(int j=0; j<look.m; j+=b.dim){
+ if(b.decodev_set(lsp, j, vb.opb, b.dim)==-1){
+ return (null);
+ }
+ }
+
+ for(int j=0; j<look.m;){
+ for(int k=0; k<b.dim; k++, j++)
+ lsp[j]+=last;
+ last=lsp[j-1];
+ }
+ lsp[look.m]=amp;
+ return (lsp);
+ }
+ }
+ return (null);
+ }
+
+ int inverse2(Block vb, Object i, Object memo, float[] out){
+ LookFloor0 look=(LookFloor0)i;
+ InfoFloor0 info=look.vi;
+
+ if(memo!=null){
+ float[] lsp=(float[])memo;
+ float amp=lsp[look.m];
+
+ Lsp.lsp_to_curve(out, look.linearmap, look.n, look.ln, lsp, look.m, amp,
+ info.ampdB);
+ return (1);
+ }
+ for(int j=0; j<look.n; j++){
+ out[j]=0.f;
+ }
+ return (0);
+ }
+
+ static float fromdB(float x){
+ return (float)(Math.exp((x)*.11512925));
+ }
+
+ static void lsp_to_lpc(float[] lsp, float[] lpc, int m){
+ int i, j, m2=m/2;
+ float[] O=new float[m2];
+ float[] E=new float[m2];
+ float A;
+ float[] Ae=new float[m2+1];
+ float[] Ao=new float[m2+1];
+ float B;
+ float[] Be=new float[m2];
+ float[] Bo=new float[m2];
+ float temp;
+
+ // even/odd roots setup
+ for(i=0; i<m2; i++){
+ O[i]=(float)(-2.*Math.cos(lsp[i*2]));
+ E[i]=(float)(-2.*Math.cos(lsp[i*2+1]));
+ }
+
+ // set up impulse response
+ for(j=0; j<m2; j++){
+ Ae[j]=0.f;
+ Ao[j]=1.f;
+ Be[j]=0.f;
+ Bo[j]=1.f;
+ }
+ Ao[j]=1.f;
+ Ae[j]=1.f;
+
+ // run impulse response
+ for(i=1; i<m+1; i++){
+ A=B=0.f;
+ for(j=0; j<m2; j++){
+ temp=O[j]*Ao[j]+Ae[j];
+ Ae[j]=Ao[j];
+ Ao[j]=A;
+ A+=temp;
+
+ temp=E[j]*Bo[j]+Be[j];
+ Be[j]=Bo[j];
+ Bo[j]=B;
+ B+=temp;
+ }
+ lpc[i-1]=(A+Ao[j]+B-Ae[j])/2;
+ Ao[j]=A;
+ Ae[j]=B;
+ }
+ }
+
+ static void lpc_to_curve(float[] curve, float[] lpc, float amp, LookFloor0 l,
+ String name, int frameno){
+ // l->m+1 must be less than l->ln, but guard in case we get a bad stream
+ float[] lcurve=new float[Math.max(l.ln*2, l.m*2+2)];
+
+ if(amp==0){
+ for(int j=0; j<l.n; j++)
+ curve[j]=0.0f;
+ return;
+ }
+ l.lpclook.lpc_to_curve(lcurve, lpc, amp);
+
+ for(int i=0; i<l.n; i++)
+ curve[i]=lcurve[l.linearmap[i]];
+ }
+
+ class InfoFloor0{
+ int order;
+ int rate;
+ int barkmap;
+
+ int ampbits;
+ int ampdB;
+
+ int numbooks; // <= 16
+ int[] books=new int[16];
+ }
+
+ class LookFloor0{
+ int n;
+ int ln;
+ int m;
+ int[] linearmap;
+
+ InfoFloor0 vi;
+ Lpc lpclook=new Lpc();
+ }
+
+ class EchstateFloor0{
+ int[] codewords;
+ float[] curve;
+ long frameno;
+ long codes;
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Floor1.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Floor1.java
new file mode 100644
index 0000000..6d8cede
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Floor1.java
@@ -0,0 +1,611 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+class Floor1 extends FuncFloor{
+ static final int floor1_rangedb=140;
+ static final int VIF_POSIT=63;
+
+ void pack(Object i, Buffer opb){
+ InfoFloor1 info=(InfoFloor1)i;
+
+ int count=0;
+ int rangebits;
+ int maxposit=info.postlist[1];
+ int maxclass=-1;
+
+ /* save out partitions */
+ opb.write(info.partitions, 5); /* only 0 to 31 legal */
+ for(int j=0; j<info.partitions; j++){
+ opb.write(info.partitionclass[j], 4); /* only 0 to 15 legal */
+ if(maxclass<info.partitionclass[j])
+ maxclass=info.partitionclass[j];
+ }
+
+ /* save out partition classes */
+ for(int j=0; j<maxclass+1; j++){
+ opb.write(info.class_dim[j]-1, 3); /* 1 to 8 */
+ opb.write(info.class_subs[j], 2); /* 0 to 3 */
+ if(info.class_subs[j]!=0){
+ opb.write(info.class_book[j], 8);
+ }
+ for(int k=0; k<(1<<info.class_subs[j]); k++){
+ opb.write(info.class_subbook[j][k]+1, 8);
+ }
+ }
+
+ /* save out the post list */
+ opb.write(info.mult-1, 2); /* only 1,2,3,4 legal now */
+ opb.write(Util.ilog2(maxposit), 4);
+ rangebits=Util.ilog2(maxposit);
+
+ for(int j=0, k=0; j<info.partitions; j++){
+ count+=info.class_dim[info.partitionclass[j]];
+ for(; k<count; k++){
+ opb.write(info.postlist[k+2], rangebits);
+ }
+ }
+ }
+
+ Object unpack(Info vi, Buffer opb){
+ int count=0, maxclass=-1, rangebits;
+ InfoFloor1 info=new InfoFloor1();
+
+ /* read partitions */
+ info.partitions=opb.read(5); /* only 0 to 31 legal */
+ for(int j=0; j<info.partitions; j++){
+ info.partitionclass[j]=opb.read(4); /* only 0 to 15 legal */
+ if(maxclass<info.partitionclass[j])
+ maxclass=info.partitionclass[j];
+ }
+
+ /* read partition classes */
+ for(int j=0; j<maxclass+1; j++){
+ info.class_dim[j]=opb.read(3)+1; /* 1 to 8 */
+ info.class_subs[j]=opb.read(2); /* 0,1,2,3 bits */
+ if(info.class_subs[j]<0){
+ info.free();
+ return (null);
+ }
+ if(info.class_subs[j]!=0){
+ info.class_book[j]=opb.read(8);
+ }
+ if(info.class_book[j]<0||info.class_book[j]>=vi.books){
+ info.free();
+ return (null);
+ }
+ for(int k=0; k<(1<<info.class_subs[j]); k++){
+ info.class_subbook[j][k]=opb.read(8)-1;
+ if(info.class_subbook[j][k]<-1||info.class_subbook[j][k]>=vi.books){
+ info.free();
+ return (null);
+ }
+ }
+ }
+
+ /* read the post list */
+ info.mult=opb.read(2)+1; /* only 1,2,3,4 legal now */
+ rangebits=opb.read(4);
+
+ for(int j=0, k=0; j<info.partitions; j++){
+ count+=info.class_dim[info.partitionclass[j]];
+ for(; k<count; k++){
+ int t=info.postlist[k+2]=opb.read(rangebits);
+ if(t<0||t>=(1<<rangebits)){
+ info.free();
+ return (null);
+ }
+ }
+ }
+ info.postlist[0]=0;
+ info.postlist[1]=1<<rangebits;
+
+ return (info);
+ }
+
+ Object look(DspState vd, InfoMode mi, Object i){
+ int _n=0;
+
+ int[] sortpointer=new int[VIF_POSIT+2];
+
+ // Info vi=vd.vi;
+
+ InfoFloor1 info=(InfoFloor1)i;
+ LookFloor1 look=new LookFloor1();
+ look.vi=info;
+ look.n=info.postlist[1];
+
+ /* we drop each position value in-between already decoded values,
+ and use linear interpolation to predict each new value past the
+ edges. The positions are read in the order of the position
+ list... we precompute the bounding positions in the lookup. Of
+ course, the neighbors can change (if a position is declined), but
+ this is an initial mapping */
+
+ for(int j=0; j<info.partitions; j++){
+ _n+=info.class_dim[info.partitionclass[j]];
+ }
+ _n+=2;
+ look.posts=_n;
+
+ /* also store a sorted position index */
+ for(int j=0; j<_n; j++){
+ sortpointer[j]=j;
+ }
+ // qsort(sortpointer,n,sizeof(int),icomp); // !!
+
+ int foo;
+ for(int j=0; j<_n-1; j++){
+ for(int k=j; k<_n; k++){
+ if(info.postlist[sortpointer[j]]>info.postlist[sortpointer[k]]){
+ foo=sortpointer[k];
+ sortpointer[k]=sortpointer[j];
+ sortpointer[j]=foo;
+ }
+ }
+ }
+
+ /* points from sort order back to range number */
+ for(int j=0; j<_n; j++){
+ look.forward_index[j]=sortpointer[j];
+ }
+ /* points from range order to sorted position */
+ for(int j=0; j<_n; j++){
+ look.reverse_index[look.forward_index[j]]=j;
+ }
+ /* we actually need the post values too */
+ for(int j=0; j<_n; j++){
+ look.sorted_index[j]=info.postlist[look.forward_index[j]];
+ }
+
+ /* quantize values to multiplier spec */
+ switch(info.mult){
+ case 1: /* 1024 -> 256 */
+ look.quant_q=256;
+ break;
+ case 2: /* 1024 -> 128 */
+ look.quant_q=128;
+ break;
+ case 3: /* 1024 -> 86 */
+ look.quant_q=86;
+ break;
+ case 4: /* 1024 -> 64 */
+ look.quant_q=64;
+ break;
+ default:
+ look.quant_q=-1;
+ }
+
+ /* discover our neighbors for decode where we don't use fit flags
+ (that would push the neighbors outward) */
+ for(int j=0; j<_n-2; j++){
+ int lo=0;
+ int hi=1;
+ int lx=0;
+ int hx=look.n;
+ int currentx=info.postlist[j+2];
+ for(int k=0; k<j+2; k++){
+ int x=info.postlist[k];
+ if(x>lx&&x<currentx){
+ lo=k;
+ lx=x;
+ }
+ if(x<hx&&x>currentx){
+ hi=k;
+ hx=x;
+ }
+ }
+ look.loneighbor[j]=lo;
+ look.hineighbor[j]=hi;
+ }
+
+ return look;
+ }
+
+ void free_info(Object i){
+ }
+
+ void free_look(Object i){
+ }
+
+ void free_state(Object vs){
+ }
+
+ int forward(Block vb, Object i, float[] in, float[] out, Object vs){
+ return 0;
+ }
+
+ Object inverse1(Block vb, Object ii, Object memo){
+ LookFloor1 look=(LookFloor1)ii;
+ InfoFloor1 info=look.vi;
+ CodeBook[] books=vb.vd.fullbooks;
+
+ /* unpack wrapped/predicted values from stream */
+ if(vb.opb.read(1)==1){
+ int[] fit_value=null;
+ if(memo instanceof int[]){
+ fit_value=(int[])memo;
+ }
+ if(fit_value==null||fit_value.length<look.posts){
+ fit_value=new int[look.posts];
+ }
+ else{
+ for(int i=0; i<fit_value.length; i++)
+ fit_value[i]=0;
+ }
+
+ fit_value[0]=vb.opb.read(Util.ilog(look.quant_q-1));
+ fit_value[1]=vb.opb.read(Util.ilog(look.quant_q-1));
+
+ /* partition by partition */
+ for(int i=0, j=2; i<info.partitions; i++){
+ int clss=info.partitionclass[i];
+ int cdim=info.class_dim[clss];
+ int csubbits=info.class_subs[clss];
+ int csub=1<<csubbits;
+ int cval=0;
+
+ /* decode the partition's first stage cascade value */
+ if(csubbits!=0){
+ cval=books[info.class_book[clss]].decode(vb.opb);
+
+ if(cval==-1){
+ return (null);
+ }
+ }
+
+ for(int k=0; k<cdim; k++){
+ int book=info.class_subbook[clss][cval&(csub-1)];
+ cval>>>=csubbits;
+ if(book>=0){
+ if((fit_value[j+k]=books[book].decode(vb.opb))==-1){
+ return (null);
+ }
+ }
+ else{
+ fit_value[j+k]=0;
+ }
+ }
+ j+=cdim;
+ }
+
+ /* unwrap positive values and reconsitute via linear interpolation */
+ for(int i=2; i<look.posts; i++){
+ int predicted=render_point(info.postlist[look.loneighbor[i-2]],
+ info.postlist[look.hineighbor[i-2]],
+ fit_value[look.loneighbor[i-2]], fit_value[look.hineighbor[i-2]],
+ info.postlist[i]);
+ int hiroom=look.quant_q-predicted;
+ int loroom=predicted;
+ int room=(hiroom<loroom ? hiroom : loroom)<<1;
+ int val=fit_value[i];
+
+ if(val!=0){
+ if(val>=room){
+ if(hiroom>loroom){
+ val=val-loroom;
+ }
+ else{
+ val=-1-(val-hiroom);
+ }
+ }
+ else{
+ if((val&1)!=0){
+ val=-((val+1)>>>1);
+ }
+ else{
+ val>>=1;
+ }
+ }
+
+ fit_value[i]=val+predicted;
+ fit_value[look.loneighbor[i-2]]&=0x7fff;
+ fit_value[look.hineighbor[i-2]]&=0x7fff;
+ }
+ else{
+ fit_value[i]=predicted|0x8000;
+ }
+ }
+ return (fit_value);
+ }
+
+ return (null);
+ }
+
+ private static int render_point(int x0, int x1, int y0, int y1, int x){
+ y0&=0x7fff; /* mask off flag */
+ y1&=0x7fff;
+
+ {
+ int dy=y1-y0;
+ int adx=x1-x0;
+ int ady=Math.abs(dy);
+ int err=ady*(x-x0);
+
+ int off=(int)(err/adx);
+ if(dy<0)
+ return (y0-off);
+ return (y0+off);
+ }
+ }
+
+ int inverse2(Block vb, Object i, Object memo, float[] out){
+ LookFloor1 look=(LookFloor1)i;
+ InfoFloor1 info=look.vi;
+ int n=vb.vd.vi.blocksizes[vb.mode]/2;
+
+ if(memo!=null){
+ /* render the lines */
+ int[] fit_value=(int[])memo;
+ int hx=0;
+ int lx=0;
+ int ly=fit_value[0]*info.mult;
+ for(int j=1; j<look.posts; j++){
+ int current=look.forward_index[j];
+ int hy=fit_value[current]&0x7fff;
+ if(hy==fit_value[current]){
+ hy*=info.mult;
+ hx=info.postlist[current];
+
+ render_line(lx, hx, ly, hy, out);
+
+ lx=hx;
+ ly=hy;
+ }
+ }
+ for(int j=hx; j<n; j++){
+ out[j]*=out[j-1]; /* be certain */
+ }
+ return (1);
+ }
+ for(int j=0; j<n; j++){
+ out[j]=0.f;
+ }
+ return (0);
+ }
+
+ private static float[] FLOOR_fromdB_LOOKUP= {1.0649863e-07F, 1.1341951e-07F,
+ 1.2079015e-07F, 1.2863978e-07F, 1.3699951e-07F, 1.4590251e-07F,
+ 1.5538408e-07F, 1.6548181e-07F, 1.7623575e-07F, 1.8768855e-07F,
+ 1.9988561e-07F, 2.128753e-07F, 2.2670913e-07F, 2.4144197e-07F,
+ 2.5713223e-07F, 2.7384213e-07F, 2.9163793e-07F, 3.1059021e-07F,
+ 3.3077411e-07F, 3.5226968e-07F, 3.7516214e-07F, 3.9954229e-07F,
+ 4.2550680e-07F, 4.5315863e-07F, 4.8260743e-07F, 5.1396998e-07F,
+ 5.4737065e-07F, 5.8294187e-07F, 6.2082472e-07F, 6.6116941e-07F,
+ 7.0413592e-07F, 7.4989464e-07F, 7.9862701e-07F, 8.5052630e-07F,
+ 9.0579828e-07F, 9.6466216e-07F, 1.0273513e-06F, 1.0941144e-06F,
+ 1.1652161e-06F, 1.2409384e-06F, 1.3215816e-06F, 1.4074654e-06F,
+ 1.4989305e-06F, 1.5963394e-06F, 1.7000785e-06F, 1.8105592e-06F,
+ 1.9282195e-06F, 2.0535261e-06F, 2.1869758e-06F, 2.3290978e-06F,
+ 2.4804557e-06F, 2.6416497e-06F, 2.8133190e-06F, 2.9961443e-06F,
+ 3.1908506e-06F, 3.3982101e-06F, 3.6190449e-06F, 3.8542308e-06F,
+ 4.1047004e-06F, 4.3714470e-06F, 4.6555282e-06F, 4.9580707e-06F,
+ 5.2802740e-06F, 5.6234160e-06F, 5.9888572e-06F, 6.3780469e-06F,
+ 6.7925283e-06F, 7.2339451e-06F, 7.7040476e-06F, 8.2047000e-06F,
+ 8.7378876e-06F, 9.3057248e-06F, 9.9104632e-06F, 1.0554501e-05F,
+ 1.1240392e-05F, 1.1970856e-05F, 1.2748789e-05F, 1.3577278e-05F,
+ 1.4459606e-05F, 1.5399272e-05F, 1.6400004e-05F, 1.7465768e-05F,
+ 1.8600792e-05F, 1.9809576e-05F, 2.1096914e-05F, 2.2467911e-05F,
+ 2.3928002e-05F, 2.5482978e-05F, 2.7139006e-05F, 2.8902651e-05F,
+ 3.0780908e-05F, 3.2781225e-05F, 3.4911534e-05F, 3.7180282e-05F,
+ 3.9596466e-05F, 4.2169667e-05F, 4.4910090e-05F, 4.7828601e-05F,
+ 5.0936773e-05F, 5.4246931e-05F, 5.7772202e-05F, 6.1526565e-05F,
+ 6.5524908e-05F, 6.9783085e-05F, 7.4317983e-05F, 7.9147585e-05F,
+ 8.4291040e-05F, 8.9768747e-05F, 9.5602426e-05F, 0.00010181521F,
+ 0.00010843174F, 0.00011547824F, 0.00012298267F, 0.00013097477F,
+ 0.00013948625F, 0.00014855085F, 0.00015820453F, 0.00016848555F,
+ 0.00017943469F, 0.00019109536F, 0.00020351382F, 0.00021673929F,
+ 0.00023082423F, 0.00024582449F, 0.00026179955F, 0.00027881276F,
+ 0.00029693158F, 0.00031622787F, 0.00033677814F, 0.00035866388F,
+ 0.00038197188F, 0.00040679456F, 0.00043323036F, 0.00046138411F,
+ 0.00049136745F, 0.00052329927F, 0.00055730621F, 0.00059352311F,
+ 0.00063209358F, 0.00067317058F, 0.00071691700F, 0.00076350630F,
+ 0.00081312324F, 0.00086596457F, 0.00092223983F, 0.00098217216F,
+ 0.0010459992F, 0.0011139742F, 0.0011863665F, 0.0012634633F,
+ 0.0013455702F, 0.0014330129F, 0.0015261382F, 0.0016253153F,
+ 0.0017309374F, 0.0018434235F, 0.0019632195F, 0.0020908006F,
+ 0.0022266726F, 0.0023713743F, 0.0025254795F, 0.0026895994F,
+ 0.0028643847F, 0.0030505286F, 0.0032487691F, 0.0034598925F,
+ 0.0036847358F, 0.0039241906F, 0.0041792066F, 0.0044507950F,
+ 0.0047400328F, 0.0050480668F, 0.0053761186F, 0.0057254891F,
+ 0.0060975636F, 0.0064938176F, 0.0069158225F, 0.0073652516F,
+ 0.0078438871F, 0.0083536271F, 0.0088964928F, 0.009474637F, 0.010090352F,
+ 0.010746080F, 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F,
+ 0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F, 0.018938423F,
+ 0.020169149F, 0.021479854F, 0.022875735F, 0.024362330F, 0.025945531F,
+ 0.027631618F, 0.029427276F, 0.031339626F, 0.033376252F, 0.035545228F,
+ 0.037855157F, 0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F,
+ 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F, 0.066714279F,
+ 0.071049749F, 0.075666962F, 0.080584227F, 0.085821044F, 0.091398179F,
+ 0.097337747F, 0.10366330F, 0.11039993F, 0.11757434F, 0.12521498F,
+ 0.13335215F, 0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F,
+ 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, 0.23501402F,
+ 0.25028656F, 0.26655159F, 0.28387361F, 0.30232132F, 0.32196786F,
+ 0.34289114F, 0.36517414F, 0.38890521F, 0.41417847F, 0.44109412F,
+ 0.46975890F, 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F,
+ 0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F, 0.82788260F,
+ 0.88168307F, 0.9389798F, 1.F};
+
+ private static void render_line(int x0, int x1, int y0, int y1, float[] d){
+ int dy=y1-y0;
+ int adx=x1-x0;
+ int ady=Math.abs(dy);
+ int base=dy/adx;
+ int sy=(dy<0 ? base-1 : base+1);
+ int x=x0;
+ int y=y0;
+ int err=0;
+
+ ady-=Math.abs(base*adx);
+
+ d[x]*=FLOOR_fromdB_LOOKUP[y];
+ while(++x<x1){
+ err=err+ady;
+ if(err>=adx){
+ err-=adx;
+ y+=sy;
+ }
+ else{
+ y+=base;
+ }
+ d[x]*=FLOOR_fromdB_LOOKUP[y];
+ }
+ }
+
+ class InfoFloor1{
+ static final int VIF_POSIT=63;
+ static final int VIF_CLASS=16;
+ static final int VIF_PARTS=31;
+
+ int partitions; /* 0 to 31 */
+ int[] partitionclass=new int[VIF_PARTS]; /* 0 to 15 */
+
+ int[] class_dim=new int[VIF_CLASS]; /* 1 to 8 */
+ int[] class_subs=new int[VIF_CLASS]; /* 0,1,2,3 (bits: 1<<n poss) */
+ int[] class_book=new int[VIF_CLASS]; /* subs ^ dim entries */
+ int[][] class_subbook=new int[VIF_CLASS][]; /* [VIF_CLASS][subs] */
+
+ int mult; /* 1 2 3 or 4 */
+ int[] postlist=new int[VIF_POSIT+2]; /* first two implicit */
+
+ /* encode side analysis parameters */
+ float maxover;
+ float maxunder;
+ float maxerr;
+
+ int twofitminsize;
+ int twofitminused;
+ int twofitweight;
+ float twofitatten;
+ int unusedminsize;
+ int unusedmin_n;
+
+ int n;
+
+ InfoFloor1(){
+ for(int i=0; i<class_subbook.length; i++){
+ class_subbook[i]=new int[8];
+ }
+ }
+
+ void free(){
+ partitionclass=null;
+ class_dim=null;
+ class_subs=null;
+ class_book=null;
+ class_subbook=null;
+ postlist=null;
+ }
+
+ Object copy_info(){
+ InfoFloor1 info=this;
+ InfoFloor1 ret=new InfoFloor1();
+
+ ret.partitions=info.partitions;
+ System
+ .arraycopy(info.partitionclass, 0, ret.partitionclass, 0, VIF_PARTS);
+ System.arraycopy(info.class_dim, 0, ret.class_dim, 0, VIF_CLASS);
+ System.arraycopy(info.class_subs, 0, ret.class_subs, 0, VIF_CLASS);
+ System.arraycopy(info.class_book, 0, ret.class_book, 0, VIF_CLASS);
+
+ for(int j=0; j<VIF_CLASS; j++){
+ System.arraycopy(info.class_subbook[j], 0, ret.class_subbook[j], 0, 8);
+ }
+
+ ret.mult=info.mult;
+ System.arraycopy(info.postlist, 0, ret.postlist, 0, VIF_POSIT+2);
+
+ ret.maxover=info.maxover;
+ ret.maxunder=info.maxunder;
+ ret.maxerr=info.maxerr;
+
+ ret.twofitminsize=info.twofitminsize;
+ ret.twofitminused=info.twofitminused;
+ ret.twofitweight=info.twofitweight;
+ ret.twofitatten=info.twofitatten;
+ ret.unusedminsize=info.unusedminsize;
+ ret.unusedmin_n=info.unusedmin_n;
+
+ ret.n=info.n;
+
+ return (ret);
+ }
+
+ }
+
+ class LookFloor1{
+ static final int VIF_POSIT=63;
+
+ int[] sorted_index=new int[VIF_POSIT+2];
+ int[] forward_index=new int[VIF_POSIT+2];
+ int[] reverse_index=new int[VIF_POSIT+2];
+ int[] hineighbor=new int[VIF_POSIT];
+ int[] loneighbor=new int[VIF_POSIT];
+ int posts;
+
+ int n;
+ int quant_q;
+ InfoFloor1 vi;
+
+ int phrasebits;
+ int postbits;
+ int frames;
+
+ void free(){
+ sorted_index=null;
+ forward_index=null;
+ reverse_index=null;
+ hineighbor=null;
+ loneighbor=null;
+ }
+ }
+
+ class Lsfit_acc{
+ long x0;
+ long x1;
+
+ long xa;
+ long ya;
+ long x2a;
+ long y2a;
+ long xya;
+ long n;
+ long an;
+ long un;
+ long edgey0;
+ long edgey1;
+ }
+
+ class EchstateFloor1{
+ int[] codewords;
+ float[] curve;
+ long frameno;
+ long codes;
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncFloor.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncFloor.java
new file mode 100644
index 0000000..6a0b12c
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncFloor.java
@@ -0,0 +1,52 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+abstract class FuncFloor{
+
+ public static FuncFloor[] floor_P= {new Floor0(), new Floor1()};
+
+ abstract void pack(Object i, Buffer opb);
+
+ abstract Object unpack(Info vi, Buffer opb);
+
+ abstract Object look(DspState vd, InfoMode mi, Object i);
+
+ abstract void free_info(Object i);
+
+ abstract void free_look(Object i);
+
+ abstract void free_state(Object vs);
+
+ abstract int forward(Block vb, Object i, float[] in, float[] out, Object vs);
+
+ abstract Object inverse1(Block vb, Object i, Object memo);
+
+ abstract int inverse2(Block vb, Object i, Object memo, float[] out);
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncMapping.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncMapping.java
new file mode 100644
index 0000000..50c1028
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncMapping.java
@@ -0,0 +1,45 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+abstract class FuncMapping{
+ public static FuncMapping[] mapping_P= {new Mapping0()};
+
+ abstract void pack(Info info, Object imap, Buffer buffer);
+
+ abstract Object unpack(Info info, Buffer buffer);
+
+ abstract Object look(DspState vd, InfoMode vm, Object m);
+
+ abstract void free_info(Object imap);
+
+ abstract void free_look(Object imap);
+
+ abstract int inverse(Block vd, Object lm);
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncResidue.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncResidue.java
new file mode 100644
index 0000000..e381975
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncResidue.java
@@ -0,0 +1,46 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+abstract class FuncResidue{
+ public static FuncResidue[] residue_P= {new Residue0(), new Residue1(),
+ new Residue2()};
+
+ abstract void pack(Object vr, Buffer opb);
+
+ abstract Object unpack(Info vi, Buffer opb);
+
+ abstract Object look(DspState vd, InfoMode vm, Object vr);
+
+ abstract void free_info(Object i);
+
+ abstract void free_look(Object i);
+
+ abstract int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch);
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncTime.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncTime.java
new file mode 100644
index 0000000..37b868b
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/FuncTime.java
@@ -0,0 +1,45 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+abstract class FuncTime{
+ public static FuncTime[] time_P= {new Time0()};
+
+ abstract void pack(Object i, Buffer opb);
+
+ abstract Object unpack(Info vi, Buffer opb);
+
+ abstract Object look(DspState vd, InfoMode vm, Object i);
+
+ abstract void free_info(Object i);
+
+ abstract void free_look(Object i);
+
+ abstract int inverse(Block vb, Object i, float[] in, float[] out);
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Info.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Info.java
new file mode 100644
index 0000000..86fa9b2
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Info.java
@@ -0,0 +1,472 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+/*
+ * Julien Gouesse: I just replaced new Integer(int) by Integer.valueOf(int)
+ */
+public class Info{
+ private static final int OV_EBADPACKET=-136;
+ private static final int OV_ENOTAUDIO=-135;
+
+ private static byte[] _vorbis="vorbis".getBytes();
+ private static final int VI_TIMEB=1;
+ // private static final int VI_FLOORB=1;
+ private static final int VI_FLOORB=2;
+ // private static final int VI_RESB=1;
+ private static final int VI_RESB=3;
+ private static final int VI_MAPB=1;
+ private static final int VI_WINDOWB=1;
+
+ public int version;
+ public int channels;
+ public int rate;
+
+ // The below bitrate declarations are *hints*.
+ // Combinations of the three values carry the following implications:
+ //
+ // all three set to the same value:
+ // implies a fixed rate bitstream
+ // only nominal set:
+ // implies a VBR stream that averages the nominal bitrate. No hard
+ // upper/lower limit
+ // upper and or lower set:
+ // implies a VBR bitstream that obeys the bitrate limits. nominal
+ // may also be set to give a nominal rate.
+ // none set:
+ // the coder does not care to speculate.
+
+ int bitrate_upper;
+ int bitrate_nominal;
+ int bitrate_lower;
+
+ // Vorbis supports only short and long blocks, but allows the
+ // encoder to choose the sizes
+
+ int[] blocksizes=new int[2];
+
+ // modes are the primary means of supporting on-the-fly different
+ // blocksizes, different channel mappings (LR or mid-side),
+ // different residue backends, etc. Each mode consists of a
+ // blocksize flag and a mapping (along with the mapping setup
+
+ int modes;
+ int maps;
+ int times;
+ int floors;
+ int residues;
+ int books;
+ int psys; // encode only
+
+ InfoMode[] mode_param=null;
+
+ int[] map_type=null;
+ Object[] map_param=null;
+
+ int[] time_type=null;
+ Object[] time_param=null;
+
+ int[] floor_type=null;
+ Object[] floor_param=null;
+
+ int[] residue_type=null;
+ Object[] residue_param=null;
+
+ StaticCodeBook[] book_param=null;
+
+ PsyInfo[] psy_param=new PsyInfo[64]; // encode only
+
+ // for block long/sort tuning; encode only
+ int envelopesa;
+ float preecho_thresh;
+ float preecho_clamp;
+
+ // used by synthesis, which has a full, alloced vi
+ public void init(){
+ rate=0;
+ }
+
+ public void clear(){
+ for(int i=0; i<modes; i++){
+ mode_param[i]=null;
+ }
+ mode_param=null;
+
+ for(int i=0; i<maps; i++){ // unpack does the range checking
+ FuncMapping.mapping_P[map_type[i]].free_info(map_param[i]);
+ }
+ map_param=null;
+
+ for(int i=0; i<times; i++){ // unpack does the range checking
+ FuncTime.time_P[time_type[i]].free_info(time_param[i]);
+ }
+ time_param=null;
+
+ for(int i=0; i<floors; i++){ // unpack does the range checking
+ FuncFloor.floor_P[floor_type[i]].free_info(floor_param[i]);
+ }
+ floor_param=null;
+
+ for(int i=0; i<residues; i++){ // unpack does the range checking
+ FuncResidue.residue_P[residue_type[i]].free_info(residue_param[i]);
+ }
+ residue_param=null;
+
+ // the static codebooks *are* freed if you call info_clear, because
+ // decode side does alloc a 'static' codebook. Calling clear on the
+ // full codebook does not clear the static codebook (that's our
+ // responsibility)
+ for(int i=0; i<books; i++){
+ // just in case the decoder pre-cleared to save space
+ if(book_param[i]!=null){
+ book_param[i].clear();
+ book_param[i]=null;
+ }
+ }
+ //if(vi->book_param)free(vi->book_param);
+ book_param=null;
+
+ for(int i=0; i<psys; i++){
+ psy_param[i].free();
+ }
+
+ }
+
+ // Header packing/unpacking
+ int unpack_info(Buffer opb){
+ version=opb.read(32);
+ if(version!=0)
+ return (-1);
+
+ channels=opb.read(8);
+ rate=opb.read(32);
+
+ bitrate_upper=opb.read(32);
+ bitrate_nominal=opb.read(32);
+ bitrate_lower=opb.read(32);
+
+ blocksizes[0]=1<<opb.read(4);
+ blocksizes[1]=1<<opb.read(4);
+
+ if((rate<1)||(channels<1)||(blocksizes[0]<8)||(blocksizes[1]<blocksizes[0])
+ ||(opb.read(1)!=1)){
+ clear();
+ return (-1);
+ }
+ return (0);
+ }
+
+ // all of the real encoding details are here. The modes, books,
+ // everything
+ int unpack_books(Buffer opb){
+
+ books=opb.read(8)+1;
+
+ if(book_param==null||book_param.length!=books)
+ book_param=new StaticCodeBook[books];
+ for(int i=0; i<books; i++){
+ book_param[i]=new StaticCodeBook();
+ if(book_param[i].unpack(opb)!=0){
+ clear();
+ return (-1);
+ }
+ }
+
+ // time backend settings
+ times=opb.read(6)+1;
+ if(time_type==null||time_type.length!=times)
+ time_type=new int[times];
+ if(time_param==null||time_param.length!=times)
+ time_param=new Object[times];
+ for(int i=0; i<times; i++){
+ time_type[i]=opb.read(16);
+ if(time_type[i]<0||time_type[i]>=VI_TIMEB){
+ clear();
+ return (-1);
+ }
+ time_param[i]=FuncTime.time_P[time_type[i]].unpack(this, opb);
+ if(time_param[i]==null){
+ clear();
+ return (-1);
+ }
+ }
+
+ // floor backend settings
+ floors=opb.read(6)+1;
+ if(floor_type==null||floor_type.length!=floors)
+ floor_type=new int[floors];
+ if(floor_param==null||floor_param.length!=floors)
+ floor_param=new Object[floors];
+
+ for(int i=0; i<floors; i++){
+ floor_type[i]=opb.read(16);
+ if(floor_type[i]<0||floor_type[i]>=VI_FLOORB){
+ clear();
+ return (-1);
+ }
+
+ floor_param[i]=FuncFloor.floor_P[floor_type[i]].unpack(this, opb);
+ if(floor_param[i]==null){
+ clear();
+ return (-1);
+ }
+ }
+
+ // residue backend settings
+ residues=opb.read(6)+1;
+
+ if(residue_type==null||residue_type.length!=residues)
+ residue_type=new int[residues];
+
+ if(residue_param==null||residue_param.length!=residues)
+ residue_param=new Object[residues];
+
+ for(int i=0; i<residues; i++){
+ residue_type[i]=opb.read(16);
+ if(residue_type[i]<0||residue_type[i]>=VI_RESB){
+ clear();
+ return (-1);
+ }
+ residue_param[i]=FuncResidue.residue_P[residue_type[i]].unpack(this, opb);
+ if(residue_param[i]==null){
+ clear();
+ return (-1);
+ }
+ }
+
+ // map backend settings
+ maps=opb.read(6)+1;
+ if(map_type==null||map_type.length!=maps)
+ map_type=new int[maps];
+ if(map_param==null||map_param.length!=maps)
+ map_param=new Object[maps];
+ for(int i=0; i<maps; i++){
+ map_type[i]=opb.read(16);
+ if(map_type[i]<0||map_type[i]>=VI_MAPB){
+ clear();
+ return (-1);
+ }
+ map_param[i]=FuncMapping.mapping_P[map_type[i]].unpack(this, opb);
+ if(map_param[i]==null){
+ clear();
+ return (-1);
+ }
+ }
+
+ // mode settings
+ modes=opb.read(6)+1;
+ if(mode_param==null||mode_param.length!=modes)
+ mode_param=new InfoMode[modes];
+ for(int i=0; i<modes; i++){
+ mode_param[i]=new InfoMode();
+ mode_param[i].blockflag=opb.read(1);
+ mode_param[i].windowtype=opb.read(16);
+ mode_param[i].transformtype=opb.read(16);
+ mode_param[i].mapping=opb.read(8);
+
+ if((mode_param[i].windowtype>=VI_WINDOWB)
+ ||(mode_param[i].transformtype>=VI_WINDOWB)
+ ||(mode_param[i].mapping>=maps)){
+ clear();
+ return (-1);
+ }
+ }
+
+ if(opb.read(1)!=1){
+ clear();
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ // The Vorbis header is in three packets; the initial small packet in
+ // the first page that identifies basic parameters, a second packet
+ // with bitstream comments and a third packet that holds the
+ // codebook.
+
+ public int synthesis_headerin(Comment vc, Packet op){
+ Buffer opb=new Buffer();
+
+ if(op!=null){
+ opb.readinit(op.packet_base, op.packet, op.bytes);
+
+ // Which of the three types of header is this?
+ // Also verify header-ness, vorbis
+ {
+ byte[] buffer=new byte[6];
+ int packtype=opb.read(8);
+ opb.read(buffer, 6);
+ if(buffer[0]!='v'||buffer[1]!='o'||buffer[2]!='r'||buffer[3]!='b'
+ ||buffer[4]!='i'||buffer[5]!='s'){
+ // not a vorbis header
+ return (-1);
+ }
+ switch(packtype){
+ case 0x01: // least significant *bit* is read first
+ if(op.b_o_s==0){
+ // Not the initial packet
+ return (-1);
+ }
+ if(rate!=0){
+ // previously initialized info header
+ return (-1);
+ }
+ return (unpack_info(opb));
+ case 0x03: // least significant *bit* is read first
+ if(rate==0){
+ // um... we didn't get the initial header
+ return (-1);
+ }
+ return (vc.unpack(opb));
+ case 0x05: // least significant *bit* is read first
+ if(rate==0||vc.vendor==null){
+ // um... we didn;t get the initial header or comments yet
+ return (-1);
+ }
+ return (unpack_books(opb));
+ default:
+ // Not a valid vorbis header type
+ //return(-1);
+ break;
+ }
+ }
+ }
+ return (-1);
+ }
+
+ // pack side
+ int pack_info(Buffer opb){
+ // preamble
+ opb.write(0x01, 8);
+ opb.write(_vorbis);
+
+ // basic information about the stream
+ opb.write(0x00, 32);
+ opb.write(channels, 8);
+ opb.write(rate, 32);
+
+ opb.write(bitrate_upper, 32);
+ opb.write(bitrate_nominal, 32);
+ opb.write(bitrate_lower, 32);
+
+ opb.write(Util.ilog2(blocksizes[0]), 4);
+ opb.write(Util.ilog2(blocksizes[1]), 4);
+ opb.write(1, 1);
+ return (0);
+ }
+
+ int pack_books(Buffer opb){
+ opb.write(0x05, 8);
+ opb.write(_vorbis);
+
+ // books
+ opb.write(books-1, 8);
+ for(int i=0; i<books; i++){
+ if(book_param[i].pack(opb)!=0){
+ //goto err_out;
+ return (-1);
+ }
+ }
+
+ // times
+ opb.write(times-1, 6);
+ for(int i=0; i<times; i++){
+ opb.write(time_type[i], 16);
+ FuncTime.time_P[time_type[i]].pack(this.time_param[i], opb);
+ }
+
+ // floors
+ opb.write(floors-1, 6);
+ for(int i=0; i<floors; i++){
+ opb.write(floor_type[i], 16);
+ FuncFloor.floor_P[floor_type[i]].pack(floor_param[i], opb);
+ }
+
+ // residues
+ opb.write(residues-1, 6);
+ for(int i=0; i<residues; i++){
+ opb.write(residue_type[i], 16);
+ FuncResidue.residue_P[residue_type[i]].pack(residue_param[i], opb);
+ }
+
+ // maps
+ opb.write(maps-1, 6);
+ for(int i=0; i<maps; i++){
+ opb.write(map_type[i], 16);
+ FuncMapping.mapping_P[map_type[i]].pack(this, map_param[i], opb);
+ }
+
+ // modes
+ opb.write(modes-1, 6);
+ for(int i=0; i<modes; i++){
+ opb.write(mode_param[i].blockflag, 1);
+ opb.write(mode_param[i].windowtype, 16);
+ opb.write(mode_param[i].transformtype, 16);
+ opb.write(mode_param[i].mapping, 8);
+ }
+ opb.write(1, 1);
+ return (0);
+ }
+
+ public int blocksize(Packet op){
+ //codec_setup_info
+ Buffer opb=new Buffer();
+
+ int mode;
+
+ opb.readinit(op.packet_base, op.packet, op.bytes);
+
+ /* Check the packet type */
+ if(opb.read(1)!=0){
+ /* Oops. This is not an audio data packet */
+ return (OV_ENOTAUDIO);
+ }
+ {
+ int modebits=0;
+ int v=modes;
+ while(v>1){
+ modebits++;
+ v>>>=1;
+ }
+
+ /* read our mode and pre/post windowsize */
+ mode=opb.read(modebits);
+ }
+ if(mode==-1)
+ return (OV_EBADPACKET);
+ return (blocksizes[mode_param[mode].blockflag]);
+ }
+
+ public String toString(){
+ return "version:"+Integer.valueOf(version)+", channels:"+Integer.valueOf(channels)
+ +", rate:"+Integer.valueOf(rate)+", bitrate:"+Integer.valueOf(bitrate_upper)
+ +","+Integer.valueOf(bitrate_nominal)+","+Integer.valueOf(bitrate_lower);
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/InfoMode.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/InfoMode.java
new file mode 100644
index 0000000..e864174
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/InfoMode.java
@@ -0,0 +1,34 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+class InfoMode{
+ int blockflag;
+ int windowtype;
+ int transformtype;
+ int mapping;
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/JOrbisException.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/JOrbisException.java
new file mode 100644
index 0000000..98c95a4
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/JOrbisException.java
@@ -0,0 +1,40 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+public class JOrbisException extends Exception{
+
+ private static final long serialVersionUID=1L;
+
+ public JOrbisException(){
+ super();
+ }
+
+ public JOrbisException(String s){
+ super("JOrbis: "+s);
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Lookup.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Lookup.java
new file mode 100644
index 0000000..0d15266
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Lookup.java
@@ -0,0 +1,152 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+class Lookup{
+ static final int COS_LOOKUP_SZ=128;
+ static final float[] COS_LOOKUP= {+1.0000000000000f, +0.9996988186962f,
+ +0.9987954562052f, +0.9972904566787f, +0.9951847266722f,
+ +0.9924795345987f, +0.9891765099648f, +0.9852776423889f,
+ +0.9807852804032f, +0.9757021300385f, +0.9700312531945f,
+ +0.9637760657954f, +0.9569403357322f, +0.9495281805930f,
+ +0.9415440651830f, +0.9329927988347f, +0.9238795325113f,
+ +0.9142097557035f, +0.9039892931234f, +0.8932243011955f,
+ +0.8819212643484f, +0.8700869911087f, +0.8577286100003f,
+ +0.8448535652497f, +0.8314696123025f, +0.8175848131516f,
+ +0.8032075314806f, +0.7883464276266f, +0.7730104533627f,
+ +0.7572088465065f, +0.7409511253550f, +0.7242470829515f,
+ +0.7071067811865f, +0.6895405447371f, +0.6715589548470f,
+ +0.6531728429538f, +0.6343932841636f, +0.6152315905806f,
+ +0.5956993044924f, +0.5758081914178f, +0.5555702330196f,
+ +0.5349976198871f, +0.5141027441932f, +0.4928981922298f,
+ +0.4713967368260f, +0.4496113296546f, +0.4275550934303f,
+ +0.4052413140050f, +0.3826834323651f, +0.3598950365350f,
+ +0.3368898533922f, +0.3136817403989f, +0.2902846772545f,
+ +0.2667127574749f, +0.2429801799033f, +0.2191012401569f,
+ +0.1950903220161f, +0.1709618887603f, +0.1467304744554f,
+ +0.1224106751992f, +0.0980171403296f, +0.0735645635997f,
+ +0.0490676743274f, +0.0245412285229f, +0.0000000000000f,
+ -0.0245412285229f, -0.0490676743274f, -0.0735645635997f,
+ -0.0980171403296f, -0.1224106751992f, -0.1467304744554f,
+ -0.1709618887603f, -0.1950903220161f, -0.2191012401569f,
+ -0.2429801799033f, -0.2667127574749f, -0.2902846772545f,
+ -0.3136817403989f, -0.3368898533922f, -0.3598950365350f,
+ -0.3826834323651f, -0.4052413140050f, -0.4275550934303f,
+ -0.4496113296546f, -0.4713967368260f, -0.4928981922298f,
+ -0.5141027441932f, -0.5349976198871f, -0.5555702330196f,
+ -0.5758081914178f, -0.5956993044924f, -0.6152315905806f,
+ -0.6343932841636f, -0.6531728429538f, -0.6715589548470f,
+ -0.6895405447371f, -0.7071067811865f, -0.7242470829515f,
+ -0.7409511253550f, -0.7572088465065f, -0.7730104533627f,
+ -0.7883464276266f, -0.8032075314806f, -0.8175848131516f,
+ -0.8314696123025f, -0.8448535652497f, -0.8577286100003f,
+ -0.8700869911087f, -0.8819212643484f, -0.8932243011955f,
+ -0.9039892931234f, -0.9142097557035f, -0.9238795325113f,
+ -0.9329927988347f, -0.9415440651830f, -0.9495281805930f,
+ -0.9569403357322f, -0.9637760657954f, -0.9700312531945f,
+ -0.9757021300385f, -0.9807852804032f, -0.9852776423889f,
+ -0.9891765099648f, -0.9924795345987f, -0.9951847266722f,
+ -0.9972904566787f, -0.9987954562052f, -0.9996988186962f,
+ -1.0000000000000f,};
+
+ /* interpolated lookup based cos function, domain 0 to PI only */
+ static float coslook(float a){
+ double d=a*(.31830989*(float)COS_LOOKUP_SZ);
+ int i=(int)d;
+ return COS_LOOKUP[i]+((float)(d-i))*(COS_LOOKUP[i+1]-COS_LOOKUP[i]);
+ }
+
+ static final int INVSQ_LOOKUP_SZ=32;
+ static final float[] INVSQ_LOOKUP= {1.414213562373f, 1.392621247646f,
+ 1.371988681140f, 1.352246807566f, 1.333333333333f, 1.315191898443f,
+ 1.297771369046f, 1.281025230441f, 1.264911064067f, 1.249390095109f,
+ 1.234426799697f, 1.219988562661f, 1.206045378311f, 1.192569588000f,
+ 1.179535649239f, 1.166919931983f, 1.154700538379f, 1.142857142857f,
+ 1.131370849898f, 1.120224067222f, 1.109400392450f, 1.098884511590f,
+ 1.088662107904f, 1.078719779941f, 1.069044967650f, 1.059625885652f,
+ 1.050451462878f, 1.041511287847f, 1.032795558989f, 1.024295039463f,
+ 1.016001016002f, 1.007905261358f, 1.000000000000f,};
+
+ /* interpolated 1./sqrt(p) where .5 <= p < 1. */
+ static float invsqlook(float a){
+ double d=a*(2.f*(float)INVSQ_LOOKUP_SZ)-(float)INVSQ_LOOKUP_SZ;
+ int i=(int)d;
+ return INVSQ_LOOKUP[i]+((float)(d-i))*(INVSQ_LOOKUP[i+1]-INVSQ_LOOKUP[i]);
+ }
+
+ static final int INVSQ2EXP_LOOKUP_MIN=-32;
+ static final int INVSQ2EXP_LOOKUP_MAX=32;
+ static final float[] INVSQ2EXP_LOOKUP= {65536.f, 46340.95001f, 32768.f,
+ 23170.47501f, 16384.f, 11585.2375f, 8192.f, 5792.618751f, 4096.f,
+ 2896.309376f, 2048.f, 1448.154688f, 1024.f, 724.0773439f, 512.f,
+ 362.038672f, 256.f, 181.019336f, 128.f, 90.50966799f, 64.f, 45.254834f,
+ 32.f, 22.627417f, 16.f, 11.3137085f, 8.f, 5.656854249f, 4.f,
+ 2.828427125f, 2.f, 1.414213562f, 1.f, 0.7071067812f, 0.5f, 0.3535533906f,
+ 0.25f, 0.1767766953f, 0.125f, 0.08838834765f, 0.0625f, 0.04419417382f,
+ 0.03125f, 0.02209708691f, 0.015625f, 0.01104854346f, 0.0078125f,
+ 0.005524271728f, 0.00390625f, 0.002762135864f, 0.001953125f,
+ 0.001381067932f, 0.0009765625f, 0.000690533966f, 0.00048828125f,
+ 0.000345266983f, 0.000244140625f, 0.0001726334915f, 0.0001220703125f,
+ 8.631674575e-05f, 6.103515625e-05f, 4.315837288e-05f, 3.051757812e-05f,
+ 2.157918644e-05f, 1.525878906e-05f,};
+
+ /* interpolated 1./sqrt(p) where .5 <= p < 1. */
+ static float invsq2explook(int a){
+ return INVSQ2EXP_LOOKUP[a-INVSQ2EXP_LOOKUP_MIN];
+ }
+
+ static final int FROMdB_LOOKUP_SZ=35;
+ static final int FROMdB2_LOOKUP_SZ=32;
+ static final int FROMdB_SHIFT=5;
+ static final int FROMdB2_SHIFT=3;
+ static final int FROMdB2_MASK=31;
+ static final float[] FROMdB_LOOKUP= {1.f, 0.6309573445f, 0.3981071706f,
+ 0.2511886432f, 0.1584893192f, 0.1f, 0.06309573445f, 0.03981071706f,
+ 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, 0.003981071706f,
+ 0.002511886432f, 0.001584893192f, 0.001f, 0.0006309573445f,
+ 0.0003981071706f, 0.0002511886432f, 0.0001584893192f, 0.0001f,
+ 6.309573445e-05f, 3.981071706e-05f, 2.511886432e-05f, 1.584893192e-05f,
+ 1e-05f, 6.309573445e-06f, 3.981071706e-06f, 2.511886432e-06f,
+ 1.584893192e-06f, 1e-06f, 6.309573445e-07f, 3.981071706e-07f,
+ 2.511886432e-07f, 1.584893192e-07f,};
+ static final float[] FROMdB2_LOOKUP= {0.9928302478f, 0.9786445908f,
+ 0.9646616199f, 0.9508784391f, 0.9372921937f, 0.92390007f, 0.9106992942f,
+ 0.8976871324f, 0.8848608897f, 0.8722179097f, 0.8597555737f,
+ 0.8474713009f, 0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f,
+ 0.7886330981f, 0.7773650302f, 0.7662579617f, 0.755309592f, 0.7445176537f,
+ 0.7338799116f, 0.7233941627f, 0.7130582353f, 0.7028699885f,
+ 0.6928273125f, 0.6829281272f, 0.6731703824f, 0.6635520573f,
+ 0.6540711597f, 0.6447257262f, 0.6355138211f,};
+
+ /* interpolated lookup based fromdB function, domain -140dB to 0dB only */
+ static float fromdBlook(float a){
+ int i=(int)(a*((float)(-(1<<FROMdB2_SHIFT))));
+ return (i<0) ? 1.f : ((i>=(FROMdB_LOOKUP_SZ<<FROMdB_SHIFT)) ? 0.f
+ : FROMdB_LOOKUP[i>>>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]);
+ }
+
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Lpc.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Lpc.java
new file mode 100644
index 0000000..aef5e99
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Lpc.java
@@ -0,0 +1,188 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+class Lpc{
+ // en/decode lookups
+ Drft fft=new Drft();;
+
+ int ln;
+ int m;
+
+ // Autocorrelation LPC coeff generation algorithm invented by
+ // N. Levinson in 1947, modified by J. Durbin in 1959.
+
+ // Input : n elements of time doamin data
+ // Output: m lpc coefficients, excitation energy
+
+ static float lpc_from_data(float[] data, float[] lpc, int n, int m){
+ float[] aut=new float[m+1];
+ float error;
+ int i, j;
+
+ // autocorrelation, p+1 lag coefficients
+
+ j=m+1;
+ while(j--!=0){
+ float d=0;
+ for(i=j; i<n; i++)
+ d+=data[i]*data[i-j];
+ aut[j]=d;
+ }
+
+ // Generate lpc coefficients from autocorr values
+
+ error=aut[0];
+ /*
+ if(error==0){
+ for(int k=0; k<m; k++) lpc[k]=0.0f;
+ return 0;
+ }
+ */
+
+ for(i=0; i<m; i++){
+ float r=-aut[i+1];
+
+ if(error==0){
+ for(int k=0; k<m; k++)
+ lpc[k]=0.0f;
+ return 0;
+ }
+
+ // Sum up this iteration's reflection coefficient; note that in
+ // Vorbis we don't save it. If anyone wants to recycle this code
+ // and needs reflection coefficients, save the results of 'r' from
+ // each iteration.
+
+ for(j=0; j<i; j++)
+ r-=lpc[j]*aut[i-j];
+ r/=error;
+
+ // Update LPC coefficients and total error
+
+ lpc[i]=r;
+ for(j=0; j<i/2; j++){
+ float tmp=lpc[j];
+ lpc[j]+=r*lpc[i-1-j];
+ lpc[i-1-j]+=r*tmp;
+ }
+ if(i%2!=0)
+ lpc[j]+=lpc[j]*r;
+
+ error*=1.0-r*r;
+ }
+
+ // we need the error value to know how big an impulse to hit the
+ // filter with later
+
+ return error;
+ }
+
+ // Input : n element envelope spectral curve
+ // Output: m lpc coefficients, excitation energy
+
+ float lpc_from_curve(float[] curve, float[] lpc){
+ int n=ln;
+ float[] work=new float[n+n];
+ float fscale=(float)(.5/n);
+ int i, j;
+
+ // input is a real curve. make it complex-real
+ // This mixes phase, but the LPC generation doesn't care.
+ for(i=0; i<n; i++){
+ work[i*2]=curve[i]*fscale;
+ work[i*2+1]=0;
+ }
+ work[n*2-1]=curve[n-1]*fscale;
+
+ n*=2;
+ fft.backward(work);
+
+ // The autocorrelation will not be circular. Shift, else we lose
+ // most of the power in the edges.
+
+ for(i=0, j=n/2; i<n/2;){
+ float temp=work[i];
+ work[i++]=work[j];
+ work[j++]=temp;
+ }
+
+ return (lpc_from_data(work, lpc, n, m));
+ }
+
+ void init(int mapped, int m){
+ ln=mapped;
+ this.m=m;
+
+ // we cheat decoding the LPC spectrum via FFTs
+ fft.init(mapped*2);
+ }
+
+ void clear(){
+ fft.clear();
+ }
+
+ static float FAST_HYPOT(float a, float b){
+ return (float)Math.sqrt((a)*(a)+(b)*(b));
+ }
+
+ // One can do this the long way by generating the transfer function in
+ // the time domain and taking the forward FFT of the result. The
+ // results from direct calculation are cleaner and faster.
+ //
+ // This version does a linear curve generation and then later
+ // interpolates the log curve from the linear curve.
+
+ void lpc_to_curve(float[] curve, float[] lpc, float amp){
+
+ for(int i=0; i<ln*2; i++)
+ curve[i]=0.0f;
+
+ if(amp==0)
+ return;
+
+ for(int i=0; i<m; i++){
+ curve[i*2+1]=lpc[i]/(4*amp);
+ curve[i*2+2]=-lpc[i]/(4*amp);
+ }
+
+ fft.backward(curve);
+
+ {
+ int l2=ln*2;
+ float unit=(float)(1./amp);
+ curve[0]=(float)(1./(curve[0]*2+unit));
+ for(int i=1; i<ln; i++){
+ float real=(curve[i]+curve[l2-i]);
+ float imag=(curve[i]-curve[l2-i]);
+
+ float a=real+unit;
+ curve[i]=(float)(1.0/FAST_HYPOT(a, imag));
+ }
+ }
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Lsp.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Lsp.java
new file mode 100644
index 0000000..e3b08fe
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Lsp.java
@@ -0,0 +1,107 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+/*
+ function: LSP (also called LSF) conversion routines
+
+ The LSP generation code is taken (with minimal modification) from
+ "On the Computation of the LSP Frequencies" by Joseph Rothweiler
+ <[email protected]>, available at:
+
+ http://www2.xtdl.com/~rothwlr/lsfpaper/lsfpage.html
+ ********************************************************************/
+
+class Lsp{
+
+ static final float M_PI=(float)(3.1415926539);
+
+ static void lsp_to_curve(float[] curve, int[] map, int n, int ln,
+ float[] lsp, int m, float amp, float ampoffset){
+ int i;
+ float wdel=M_PI/ln;
+ for(i=0; i<m; i++)
+ lsp[i]=Lookup.coslook(lsp[i]);
+ int m2=(m/2)*2;
+
+ i=0;
+ while(i<n){
+ int k=map[i];
+ float p=.7071067812f;
+ float q=.7071067812f;
+ float w=Lookup.coslook(wdel*k);
+
+ for(int j=0; j<m2; j+=2){
+ q*=lsp[j]-w;
+ p*=lsp[j+1]-w;
+ }
+
+ if((m&1)!=0){
+ /* odd order filter; slightly assymetric */
+ /* the last coefficient */
+ q*=lsp[m-1]-w;
+ q*=q;
+ p*=p*(1.f-w*w);
+ }
+ else{
+ /* even order filter; still symmetric */
+ q*=q*(1.f+w);
+ p*=p*(1.f-w);
+ }
+
+ // q=frexp(p+q,&qexp);
+ q=p+q;
+ int hx=Float.floatToIntBits(q);
+ int ix=0x7fffffff&hx;
+ int qexp=0;
+
+ if(ix>=0x7f800000||(ix==0)){
+ // 0,inf,nan
+ }
+ else{
+ if(ix<0x00800000){ // subnormal
+ q*=3.3554432000e+07; // 0x4c000000
+ hx=Float.floatToIntBits(q);
+ ix=0x7fffffff&hx;
+ qexp=-25;
+ }
+ qexp+=((ix>>>23)-126);
+ hx=(hx&0x807fffff)|0x3f000000;
+ q=Float.intBitsToFloat(hx);
+ }
+
+ q=Lookup.fromdBlook(amp*Lookup.invsqlook(q)*Lookup.invsq2explook(qexp+m)
+ -ampoffset);
+
+ do{
+ curve[i++]*=q;
+ }
+ while(i<n&&map[i]==k);
+
+ }
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Mapping0.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Mapping0.java
new file mode 100644
index 0000000..d1bc10b
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Mapping0.java
@@ -0,0 +1,375 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+class Mapping0 extends FuncMapping{
+ static int seq=0;
+
+ void free_info(Object imap){
+ };
+
+ void free_look(Object imap){
+ }
+
+ Object look(DspState vd, InfoMode vm, Object m){
+ //System.err.println("Mapping0.look");
+ Info vi=vd.vi;
+ LookMapping0 look=new LookMapping0();
+ InfoMapping0 info=look.map=(InfoMapping0)m;
+ look.mode=vm;
+
+ look.time_look=new Object[info.submaps];
+ look.floor_look=new Object[info.submaps];
+ look.residue_look=new Object[info.submaps];
+
+ look.time_func=new FuncTime[info.submaps];
+ look.floor_func=new FuncFloor[info.submaps];
+ look.residue_func=new FuncResidue[info.submaps];
+
+ for(int i=0; i<info.submaps; i++){
+ int timenum=info.timesubmap[i];
+ int floornum=info.floorsubmap[i];
+ int resnum=info.residuesubmap[i];
+
+ look.time_func[i]=FuncTime.time_P[vi.time_type[timenum]];
+ look.time_look[i]=look.time_func[i].look(vd, vm, vi.time_param[timenum]);
+ look.floor_func[i]=FuncFloor.floor_P[vi.floor_type[floornum]];
+ look.floor_look[i]=look.floor_func[i].look(vd, vm,
+ vi.floor_param[floornum]);
+ look.residue_func[i]=FuncResidue.residue_P[vi.residue_type[resnum]];
+ look.residue_look[i]=look.residue_func[i].look(vd, vm,
+ vi.residue_param[resnum]);
+
+ }
+
+ if(vi.psys!=0&&vd.analysisp!=0){
+ // ??
+ }
+
+ look.ch=vi.channels;
+
+ return (look);
+ }
+
+ void pack(Info vi, Object imap, Buffer opb){
+ InfoMapping0 info=(InfoMapping0)imap;
+
+ /* another 'we meant to do it this way' hack... up to beta 4, we
+ packed 4 binary zeros here to signify one submapping in use. We
+ now redefine that to mean four bitflags that indicate use of
+ deeper features; bit0:submappings, bit1:coupling,
+ bit2,3:reserved. This is backward compatable with all actual uses
+ of the beta code. */
+
+ if(info.submaps>1){
+ opb.write(1, 1);
+ opb.write(info.submaps-1, 4);
+ }
+ else{
+ opb.write(0, 1);
+ }
+
+ if(info.coupling_steps>0){
+ opb.write(1, 1);
+ opb.write(info.coupling_steps-1, 8);
+ for(int i=0; i<info.coupling_steps; i++){
+ opb.write(info.coupling_mag[i], Util.ilog2(vi.channels));
+ opb.write(info.coupling_ang[i], Util.ilog2(vi.channels));
+ }
+ }
+ else{
+ opb.write(0, 1);
+ }
+
+ opb.write(0, 2); /* 2,3:reserved */
+
+ /* we don't write the channel submappings if we only have one... */
+ if(info.submaps>1){
+ for(int i=0; i<vi.channels; i++)
+ opb.write(info.chmuxlist[i], 4);
+ }
+ for(int i=0; i<info.submaps; i++){
+ opb.write(info.timesubmap[i], 8);
+ opb.write(info.floorsubmap[i], 8);
+ opb.write(info.residuesubmap[i], 8);
+ }
+ }
+
+ // also responsible for range checking
+ Object unpack(Info vi, Buffer opb){
+ InfoMapping0 info=new InfoMapping0();
+
+ if(opb.read(1)!=0){
+ info.submaps=opb.read(4)+1;
+ }
+ else{
+ info.submaps=1;
+ }
+
+ if(opb.read(1)!=0){
+ info.coupling_steps=opb.read(8)+1;
+
+ for(int i=0; i<info.coupling_steps; i++){
+ int testM=info.coupling_mag[i]=opb.read(Util.ilog2(vi.channels));
+ int testA=info.coupling_ang[i]=opb.read(Util.ilog2(vi.channels));
+
+ if(testM<0||testA<0||testM==testA||testM>=vi.channels
+ ||testA>=vi.channels){
+ //goto err_out;
+ info.free();
+ return (null);
+ }
+ }
+ }
+
+ if(opb.read(2)>0){ /* 2,3:reserved */
+ info.free();
+ return (null);
+ }
+
+ if(info.submaps>1){
+ for(int i=0; i<vi.channels; i++){
+ info.chmuxlist[i]=opb.read(4);
+ if(info.chmuxlist[i]>=info.submaps){
+ info.free();
+ return (null);
+ }
+ }
+ }
+
+ for(int i=0; i<info.submaps; i++){
+ info.timesubmap[i]=opb.read(8);
+ if(info.timesubmap[i]>=vi.times){
+ info.free();
+ return (null);
+ }
+ info.floorsubmap[i]=opb.read(8);
+ if(info.floorsubmap[i]>=vi.floors){
+ info.free();
+ return (null);
+ }
+ info.residuesubmap[i]=opb.read(8);
+ if(info.residuesubmap[i]>=vi.residues){
+ info.free();
+ return (null);
+ }
+ }
+ return info;
+ }
+
+ float[][] pcmbundle=null;
+ int[] zerobundle=null;
+ int[] nonzero=null;
+ Object[] floormemo=null;
+
+ synchronized int inverse(Block vb, Object l){
+ DspState vd=vb.vd;
+ Info vi=vd.vi;
+ LookMapping0 look=(LookMapping0)l;
+ InfoMapping0 info=look.map;
+ InfoMode mode=look.mode;
+ int n=vb.pcmend=vi.blocksizes[vb.W];
+
+ float[] window=vd.window[vb.W][vb.lW][vb.nW][mode.windowtype];
+ if(pcmbundle==null||pcmbundle.length<vi.channels){
+ pcmbundle=new float[vi.channels][];
+ nonzero=new int[vi.channels];
+ zerobundle=new int[vi.channels];
+ floormemo=new Object[vi.channels];
+ }
+
+ // time domain information decode (note that applying the
+ // information would have to happen later; we'll probably add a
+ // function entry to the harness for that later
+ // NOT IMPLEMENTED
+
+ // recover the spectral envelope; store it in the PCM vector for now
+ for(int i=0; i<vi.channels; i++){
+ float[] pcm=vb.pcm[i];
+ int submap=info.chmuxlist[i];
+
+ floormemo[i]=look.floor_func[submap].inverse1(vb,
+ look.floor_look[submap], floormemo[i]);
+ if(floormemo[i]!=null){
+ nonzero[i]=1;
+ }
+ else{
+ nonzero[i]=0;
+ }
+ for(int j=0; j<n/2; j++){
+ pcm[j]=0;
+ }
+
+ }
+
+ for(int i=0; i<info.coupling_steps; i++){
+ if(nonzero[info.coupling_mag[i]]!=0||nonzero[info.coupling_ang[i]]!=0){
+ nonzero[info.coupling_mag[i]]=1;
+ nonzero[info.coupling_ang[i]]=1;
+ }
+ }
+
+ // recover the residue, apply directly to the spectral envelope
+
+ for(int i=0; i<info.submaps; i++){
+ int ch_in_bundle=0;
+ for(int j=0; j<vi.channels; j++){
+ if(info.chmuxlist[j]==i){
+ if(nonzero[j]!=0){
+ zerobundle[ch_in_bundle]=1;
+ }
+ else{
+ zerobundle[ch_in_bundle]=0;
+ }
+ pcmbundle[ch_in_bundle++]=vb.pcm[j];
+ }
+ }
+
+ look.residue_func[i].inverse(vb, look.residue_look[i], pcmbundle,
+ zerobundle, ch_in_bundle);
+ }
+
+ for(int i=info.coupling_steps-1; i>=0; i--){
+ float[] pcmM=vb.pcm[info.coupling_mag[i]];
+ float[] pcmA=vb.pcm[info.coupling_ang[i]];
+
+ for(int j=0; j<n/2; j++){
+ float mag=pcmM[j];
+ float ang=pcmA[j];
+
+ if(mag>0){
+ if(ang>0){
+ pcmM[j]=mag;
+ pcmA[j]=mag-ang;
+ }
+ else{
+ pcmA[j]=mag;
+ pcmM[j]=mag+ang;
+ }
+ }
+ else{
+ if(ang>0){
+ pcmM[j]=mag;
+ pcmA[j]=mag+ang;
+ }
+ else{
+ pcmA[j]=mag;
+ pcmM[j]=mag-ang;
+ }
+ }
+ }
+ }
+
+ // /* compute and apply spectral envelope */
+
+ for(int i=0; i<vi.channels; i++){
+ float[] pcm=vb.pcm[i];
+ int submap=info.chmuxlist[i];
+ look.floor_func[submap].inverse2(vb, look.floor_look[submap],
+ floormemo[i], pcm);
+ }
+
+ // transform the PCM data; takes PCM vector, vb; modifies PCM vector
+ // only MDCT right now....
+
+ for(int i=0; i<vi.channels; i++){
+ float[] pcm=vb.pcm[i];
+ //_analysis_output("out",seq+i,pcm,n/2,0,0);
+ ((Mdct)vd.transform[vb.W][0]).backward(pcm, pcm);
+ }
+
+ // now apply the decoded pre-window time information
+ // NOT IMPLEMENTED
+
+ // window the data
+ for(int i=0; i<vi.channels; i++){
+ float[] pcm=vb.pcm[i];
+ if(nonzero[i]!=0){
+ for(int j=0; j<n; j++){
+ pcm[j]*=window[j];
+ }
+ }
+ else{
+ for(int j=0; j<n; j++){
+ pcm[j]=0.f;
+ }
+ }
+ }
+
+ // now apply the decoded post-window time information
+ // NOT IMPLEMENTED
+ // all done!
+ return (0);
+ }
+
+ class InfoMapping0{
+ int submaps; // <= 16
+ int[] chmuxlist=new int[256]; // up to 256 channels in a Vorbis stream
+
+ int[] timesubmap=new int[16]; // [mux]
+ int[] floorsubmap=new int[16]; // [mux] submap to floors
+ int[] residuesubmap=new int[16];// [mux] submap to residue
+ int[] psysubmap=new int[16]; // [mux]; encode only
+
+ int coupling_steps;
+ int[] coupling_mag=new int[256];
+ int[] coupling_ang=new int[256];
+
+ void free(){
+ chmuxlist=null;
+ timesubmap=null;
+ floorsubmap=null;
+ residuesubmap=null;
+ psysubmap=null;
+
+ coupling_mag=null;
+ coupling_ang=null;
+ }
+ }
+
+ class LookMapping0{
+ InfoMode mode;
+ InfoMapping0 map;
+ Object[] time_look;
+ Object[] floor_look;
+ Object[] floor_state;
+ Object[] residue_look;
+ PsyLook[] psy_look;
+
+ FuncTime[] time_func;
+ FuncFloor[] floor_func;
+ FuncResidue[] residue_func;
+
+ int ch;
+ float[][] decay;
+ int lastframe; // if a different mode is called, we need to
+ // invalidate decay and floor state
+ }
+
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Mdct.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Mdct.java
new file mode 100644
index 0000000..e7657eb
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Mdct.java
@@ -0,0 +1,250 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+class Mdct{
+
+ int n;
+ int log2n;
+
+ float[] trig;
+ int[] bitrev;
+
+ float scale;
+
+ void init(int n){
+ bitrev=new int[n/4];
+ trig=new float[n+n/4];
+
+ log2n=(int)Math.rint(Math.log(n)/Math.log(2));
+ this.n=n;
+
+ int AE=0;
+ int AO=1;
+ int BE=AE+n/2;
+ int BO=BE+1;
+ int CE=BE+n/2;
+ int CO=CE+1;
+ // trig lookups...
+ for(int i=0; i<n/4; i++){
+ trig[AE+i*2]=(float)Math.cos((Math.PI/n)*(4*i));
+ trig[AO+i*2]=(float)-Math.sin((Math.PI/n)*(4*i));
+ trig[BE+i*2]=(float)Math.cos((Math.PI/(2*n))*(2*i+1));
+ trig[BO+i*2]=(float)Math.sin((Math.PI/(2*n))*(2*i+1));
+ }
+ for(int i=0; i<n/8; i++){
+ trig[CE+i*2]=(float)Math.cos((Math.PI/n)*(4*i+2));
+ trig[CO+i*2]=(float)-Math.sin((Math.PI/n)*(4*i+2));
+ }
+
+ {
+ int mask=(1<<(log2n-1))-1;
+ int msb=1<<(log2n-2);
+ for(int i=0; i<n/8; i++){
+ int acc=0;
+ for(int j=0; msb>>>j!=0; j++)
+ if(((msb>>>j)&i)!=0)
+ acc|=1<<j;
+ bitrev[i*2]=((~acc)&mask);
+ // bitrev[i*2]=((~acc)&mask)-1;
+ bitrev[i*2+1]=acc;
+ }
+ }
+ scale=4.f/n;
+ }
+
+ void clear(){
+ }
+
+ void forward(float[] in, float[] out){
+ }
+
+ float[] _x=new float[1024];
+ float[] _w=new float[1024];
+
+ synchronized void backward(float[] in, float[] out){
+ if(_x.length<n/2){
+ _x=new float[n/2];
+ }
+ if(_w.length<n/2){
+ _w=new float[n/2];
+ }
+ float[] x=_x;
+ float[] w=_w;
+ int n2=n>>>1;
+ int n4=n>>>2;
+ int n8=n>>>3;
+
+ // rotate + step 1
+ {
+ int inO=1;
+ int xO=0;
+ int A=n2;
+
+ int i;
+ for(i=0; i<n8; i++){
+ A-=2;
+ x[xO++]=-in[inO+2]*trig[A+1]-in[inO]*trig[A];
+ x[xO++]=in[inO]*trig[A+1]-in[inO+2]*trig[A];
+ inO+=4;
+ }
+
+ inO=n2-4;
+
+ for(i=0; i<n8; i++){
+ A-=2;
+ x[xO++]=in[inO]*trig[A+1]+in[inO+2]*trig[A];
+ x[xO++]=in[inO]*trig[A]-in[inO+2]*trig[A+1];
+ inO-=4;
+ }
+ }
+
+ float[] xxx=mdct_kernel(x, w, n, n2, n4, n8);
+ int xx=0;
+
+ // step 8
+
+ {
+ int B=n2;
+ int o1=n4, o2=o1-1;
+ int o3=n4+n2, o4=o3-1;
+
+ for(int i=0; i<n4; i++){
+ float temp1=(xxx[xx]*trig[B+1]-xxx[xx+1]*trig[B]);
+ float temp2=-(xxx[xx]*trig[B]+xxx[xx+1]*trig[B+1]);
+
+ out[o1]=-temp1;
+ out[o2]=temp1;
+ out[o3]=temp2;
+ out[o4]=temp2;
+
+ o1++;
+ o2--;
+ o3++;
+ o4--;
+ xx+=2;
+ B+=2;
+ }
+ }
+ }
+
+ private float[] mdct_kernel(float[] x, float[] w, int n, int n2, int n4,
+ int n8){
+ // step 2
+
+ int xA=n4;
+ int xB=0;
+ int w2=n4;
+ int A=n2;
+
+ for(int i=0; i<n4;){
+ float x0=x[xA]-x[xB];
+ float x1;
+ w[w2+i]=x[xA++]+x[xB++];
+
+ x1=x[xA]-x[xB];
+ A-=4;
+
+ w[i++]=x0*trig[A]+x1*trig[A+1];
+ w[i]=x1*trig[A]-x0*trig[A+1];
+
+ w[w2+i]=x[xA++]+x[xB++];
+ i++;
+ }
+
+ // step 3
+
+ {
+ for(int i=0; i<log2n-3; i++){
+ int k0=n>>>(i+2);
+ int k1=1<<(i+3);
+ int wbase=n2-2;
+
+ A=0;
+ float[] temp;
+
+ for(int r=0; r<(k0>>>2); r++){
+ int w1=wbase;
+ w2=w1-(k0>>1);
+ float AEv=trig[A], wA;
+ float AOv=trig[A+1], wB;
+ wbase-=2;
+
+ k0++;
+ for(int s=0; s<(2<<i); s++){
+ wB=w[w1]-w[w2];
+ x[w1]=w[w1]+w[w2];
+
+ wA=w[++w1]-w[++w2];
+ x[w1]=w[w1]+w[w2];
+
+ x[w2]=wA*AEv-wB*AOv;
+ x[w2-1]=wB*AEv+wA*AOv;
+
+ w1-=k0;
+ w2-=k0;
+ }
+ k0--;
+ A+=k1;
+ }
+
+ temp=w;
+ w=x;
+ x=temp;
+ }
+ }
+
+ // step 4, 5, 6, 7
+ {
+ int C=n;
+ int bit=0;
+ int x1=0;
+ int x2=n2-1;
+
+ for(int i=0; i<n8; i++){
+ int t1=bitrev[bit++];
+ int t2=bitrev[bit++];
+
+ float wA=w[t1]-w[t2+1];
+ float wB=w[t1-1]+w[t2];
+ float wC=w[t1]+w[t2+1];
+ float wD=w[t1-1]-w[t2];
+
+ float wACE=wA*trig[C];
+ float wBCE=wB*trig[C++];
+ float wACO=wA*trig[C];
+ float wBCO=wB*trig[C++];
+
+ x[x1++]=(wC+wACO+wBCE)*.5f;
+ x[x2--]=(-wD+wBCO-wACE)*.5f;
+ x[x1++]=(wD+wBCO-wACE)*.5f;
+ x[x2--]=(wC-wACO-wBCE)*.5f;
+ }
+ }
+ return (x);
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/PsyInfo.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/PsyInfo.java
new file mode 100644
index 0000000..63b250d
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/PsyInfo.java
@@ -0,0 +1,74 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+// psychoacoustic setup
+class PsyInfo{
+ int athp;
+ int decayp;
+ int smoothp;
+ int noisefitp;
+ int noisefit_subblock;
+ float noisefit_threshdB;
+
+ float ath_att;
+
+ int tonemaskp;
+ float[] toneatt_125Hz=new float[5];
+ float[] toneatt_250Hz=new float[5];
+ float[] toneatt_500Hz=new float[5];
+ float[] toneatt_1000Hz=new float[5];
+ float[] toneatt_2000Hz=new float[5];
+ float[] toneatt_4000Hz=new float[5];
+ float[] toneatt_8000Hz=new float[5];
+
+ int peakattp;
+ float[] peakatt_125Hz=new float[5];
+ float[] peakatt_250Hz=new float[5];
+ float[] peakatt_500Hz=new float[5];
+ float[] peakatt_1000Hz=new float[5];
+ float[] peakatt_2000Hz=new float[5];
+ float[] peakatt_4000Hz=new float[5];
+ float[] peakatt_8000Hz=new float[5];
+
+ int noisemaskp;
+ float[] noiseatt_125Hz=new float[5];
+ float[] noiseatt_250Hz=new float[5];
+ float[] noiseatt_500Hz=new float[5];
+ float[] noiseatt_1000Hz=new float[5];
+ float[] noiseatt_2000Hz=new float[5];
+ float[] noiseatt_4000Hz=new float[5];
+ float[] noiseatt_8000Hz=new float[5];
+
+ float max_curve_dB;
+
+ float attack_coeff;
+ float decay_coeff;
+
+ void free(){
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/PsyLook.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/PsyLook.java
new file mode 100644
index 0000000..ff30571
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/PsyLook.java
@@ -0,0 +1,42 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+class PsyLook{
+ int n;
+ PsyInfo vi;
+
+ float[][][] tonecurves;
+ float[][] peakatt;
+ float[][][] noisecurves;
+
+ float[] ath;
+ int[] octave;
+
+ void init(PsyInfo vi, int n, int rate){
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue0.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue0.java
new file mode 100644
index 0000000..46ca59a
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue0.java
@@ -0,0 +1,330 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+class Residue0 extends FuncResidue{
+ void pack(Object vr, Buffer opb){
+ InfoResidue0 info=(InfoResidue0)vr;
+ int acc=0;
+ opb.write(info.begin, 24);
+ opb.write(info.end, 24);
+
+ opb.write(info.grouping-1, 24); /* residue vectors to group and
+ code with a partitioned book */
+ opb.write(info.partitions-1, 6); /* possible partition choices */
+ opb.write(info.groupbook, 8); /* group huffman book */
+
+ /* secondstages is a bitmask; as encoding progresses pass by pass, a
+ bitmask of one indicates this partition class has bits to write
+ this pass */
+ for(int j=0; j<info.partitions; j++){
+ int i=info.secondstages[j];
+ if(Util.ilog(i)>3){
+ /* yes, this is a minor hack due to not thinking ahead */
+ opb.write(i, 3);
+ opb.write(1, 1);
+ opb.write(i>>>3, 5);
+ }
+ else{
+ opb.write(i, 4); /* trailing zero */
+ }
+ acc+=Util.icount(i);
+ }
+ for(int j=0; j<acc; j++){
+ opb.write(info.booklist[j], 8);
+ }
+ }
+
+ Object unpack(Info vi, Buffer opb){
+ int acc=0;
+ InfoResidue0 info=new InfoResidue0();
+ info.begin=opb.read(24);
+ info.end=opb.read(24);
+ info.grouping=opb.read(24)+1;
+ info.partitions=opb.read(6)+1;
+ info.groupbook=opb.read(8);
+
+ for(int j=0; j<info.partitions; j++){
+ int cascade=opb.read(3);
+ if(opb.read(1)!=0){
+ cascade|=(opb.read(5)<<3);
+ }
+ info.secondstages[j]=cascade;
+ acc+=Util.icount(cascade);
+ }
+
+ for(int j=0; j<acc; j++){
+ info.booklist[j]=opb.read(8);
+ }
+
+ if(info.groupbook>=vi.books){
+ free_info(info);
+ return (null);
+ }
+
+ for(int j=0; j<acc; j++){
+ if(info.booklist[j]>=vi.books){
+ free_info(info);
+ return (null);
+ }
+ }
+ return (info);
+ }
+
+ Object look(DspState vd, InfoMode vm, Object vr){
+ InfoResidue0 info=(InfoResidue0)vr;
+ LookResidue0 look=new LookResidue0();
+ int acc=0;
+ int dim;
+ int maxstage=0;
+ look.info=info;
+ look.map=vm.mapping;
+
+ look.parts=info.partitions;
+ look.fullbooks=vd.fullbooks;
+ look.phrasebook=vd.fullbooks[info.groupbook];
+
+ dim=look.phrasebook.dim;
+
+ look.partbooks=new int[look.parts][];
+
+ for(int j=0; j<look.parts; j++){
+ int i=info.secondstages[j];
+ int stages=Util.ilog(i);
+ if(stages!=0){
+ if(stages>maxstage)
+ maxstage=stages;
+ look.partbooks[j]=new int[stages];
+ for(int k=0; k<stages; k++){
+ if((i&(1<<k))!=0){
+ look.partbooks[j][k]=info.booklist[acc++];
+ }
+ }
+ }
+ }
+
+ look.partvals=(int)Math.rint(Math.pow(look.parts, dim));
+ look.stages=maxstage;
+ look.decodemap=new int[look.partvals][];
+ for(int j=0; j<look.partvals; j++){
+ int val=j;
+ int mult=look.partvals/look.parts;
+ look.decodemap[j]=new int[dim];
+
+ for(int k=0; k<dim; k++){
+ int deco=val/mult;
+ val-=deco*mult;
+ mult/=look.parts;
+ look.decodemap[j][k]=deco;
+ }
+ }
+ return (look);
+ }
+
+ void free_info(Object i){
+ }
+
+ void free_look(Object i){
+ }
+
+ private static int[][][] _01inverse_partword=new int[2][][]; // _01inverse is synchronized for
+
+ // re-using partword
+ synchronized static int _01inverse(Block vb, Object vl, float[][] in, int ch,
+ int decodepart){
+ int i, j, k, l, s;
+ LookResidue0 look=(LookResidue0)vl;
+ InfoResidue0 info=look.info;
+
+ // move all this setup out later
+ int samples_per_partition=info.grouping;
+ int partitions_per_word=look.phrasebook.dim;
+ int n=info.end-info.begin;
+
+ int partvals=n/samples_per_partition;
+ int partwords=(partvals+partitions_per_word-1)/partitions_per_word;
+
+ if(_01inverse_partword.length<ch){
+ _01inverse_partword=new int[ch][][];
+ }
+
+ for(j=0; j<ch; j++){
+ if(_01inverse_partword[j]==null||_01inverse_partword[j].length<partwords){
+ _01inverse_partword[j]=new int[partwords][];
+ }
+ }
+
+ for(s=0; s<look.stages; s++){
+ // each loop decodes on partition codeword containing
+ // partitions_pre_word partitions
+ for(i=0, l=0; i<partvals; l++){
+ if(s==0){
+ // fetch the partition word for each channel
+ for(j=0; j<ch; j++){
+ int temp=look.phrasebook.decode(vb.opb);
+ if(temp==-1){
+ return (0);
+ }
+ _01inverse_partword[j][l]=look.decodemap[temp];
+ if(_01inverse_partword[j][l]==null){
+ return (0);
+ }
+ }
+ }
+
+ // now we decode residual values for the partitions
+ for(k=0; k<partitions_per_word&&i<partvals; k++, i++)
+ for(j=0; j<ch; j++){
+ int offset=info.begin+i*samples_per_partition;
+ int index=_01inverse_partword[j][l][k];
+ if((info.secondstages[index]&(1<<s))!=0){
+ CodeBook stagebook=look.fullbooks[look.partbooks[index][s]];
+ if(stagebook!=null){
+ if(decodepart==0){
+ if(stagebook.decodevs_add(in[j], offset, vb.opb,
+ samples_per_partition)==-1){
+ return (0);
+ }
+ }
+ else if(decodepart==1){
+ if(stagebook.decodev_add(in[j], offset, vb.opb,
+ samples_per_partition)==-1){
+ return (0);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return (0);
+ }
+
+ static int[][] _2inverse_partword=null;
+
+ synchronized static int _2inverse(Block vb, Object vl, float[][] in, int ch){
+ int i, k, l, s;
+ LookResidue0 look=(LookResidue0)vl;
+ InfoResidue0 info=look.info;
+
+ // move all this setup out later
+ int samples_per_partition=info.grouping;
+ int partitions_per_word=look.phrasebook.dim;
+ int n=info.end-info.begin;
+
+ int partvals=n/samples_per_partition;
+ int partwords=(partvals+partitions_per_word-1)/partitions_per_word;
+
+ if(_2inverse_partword==null||_2inverse_partword.length<partwords){
+ _2inverse_partword=new int[partwords][];
+ }
+ for(s=0; s<look.stages; s++){
+ for(i=0, l=0; i<partvals; l++){
+ if(s==0){
+ // fetch the partition word for each channel
+ int temp=look.phrasebook.decode(vb.opb);
+ if(temp==-1){
+ return (0);
+ }
+ _2inverse_partword[l]=look.decodemap[temp];
+ if(_2inverse_partword[l]==null){
+ return (0);
+ }
+ }
+
+ // now we decode residual values for the partitions
+ for(k=0; k<partitions_per_word&&i<partvals; k++, i++){
+ int offset=info.begin+i*samples_per_partition;
+ int index=_2inverse_partword[l][k];
+ if((info.secondstages[index]&(1<<s))!=0){
+ CodeBook stagebook=look.fullbooks[look.partbooks[index][s]];
+ if(stagebook!=null){
+ if(stagebook.decodevv_add(in, offset, ch, vb.opb,
+ samples_per_partition)==-1){
+ return (0);
+ }
+ }
+ }
+ }
+ }
+ }
+ return (0);
+ }
+
+ int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch){
+ int used=0;
+ for(int i=0; i<ch; i++){
+ if(nonzero[i]!=0){
+ in[used++]=in[i];
+ }
+ }
+ if(used!=0)
+ return (_01inverse(vb, vl, in, used, 0));
+ else
+ return (0);
+ }
+
+ class LookResidue0{
+ InfoResidue0 info;
+ int map;
+
+ int parts;
+ int stages;
+ CodeBook[] fullbooks;
+ CodeBook phrasebook;
+ int[][] partbooks;
+
+ int partvals;
+ int[][] decodemap;
+
+ int postbits;
+ int phrasebits;
+ int frames;
+ }
+
+ class InfoResidue0{
+ // block-partitioned VQ coded straight residue
+ int begin;
+ int end;
+
+ // first stage (lossless partitioning)
+ int grouping; // group n vectors per partition
+ int partitions; // possible codebooks for a partition
+ int groupbook; // huffbook for partitioning
+ int[] secondstages=new int[64]; // expanded out to pointers in lookup
+ int[] booklist=new int[256]; // list of second stage books
+
+ // encode-only heuristic settings
+ float[] entmax=new float[64]; // book entropy threshholds
+ float[] ampmax=new float[64]; // book amp threshholds
+ int[] subgrp=new int[64]; // book heuristic subgroup size
+ int[] blimit=new int[64]; // subgroup position limits
+ }
+
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue1.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue1.java
new file mode 100644
index 0000000..294e131
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue1.java
@@ -0,0 +1,45 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+class Residue1 extends Residue0{
+
+ int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch){
+ int used=0;
+ for(int i=0; i<ch; i++){
+ if(nonzero[i]!=0){
+ in[used++]=in[i];
+ }
+ }
+ if(used!=0){
+ return (_01inverse(vb, vl, in, used, 1));
+ }
+ else{
+ return 0;
+ }
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue2.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue2.java
new file mode 100644
index 0000000..613cc7d
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Residue2.java
@@ -0,0 +1,41 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+class Residue2 extends Residue0{
+
+ int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch){
+ int i=0;
+ for(i=0; i<ch; i++)
+ if(nonzero[i]!=0)
+ break;
+ if(i==ch)
+ return (0); /* no nonzero vectors */
+
+ return (_2inverse(vb, vl, in, ch));
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/StaticCodeBook.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/StaticCodeBook.java
new file mode 100644
index 0000000..fea49c7
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/StaticCodeBook.java
@@ -0,0 +1,443 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+class StaticCodeBook{
+ int dim; // codebook dimensions (elements per vector)
+ int entries; // codebook entries
+ int[] lengthlist; // codeword lengths in bits
+
+ // mapping
+ int maptype; // 0=none
+ // 1=implicitly populated values from map column
+ // 2=listed arbitrary values
+
+ // The below does a linear, single monotonic sequence mapping.
+ int q_min; // packed 32 bit float; quant value 0 maps to minval
+ int q_delta; // packed 32 bit float; val 1 - val 0 == delta
+ int q_quant; // bits: 0 < quant <= 16
+ int q_sequencep; // bitflag
+
+ // additional information for log (dB) mapping; the linear mapping
+ // is assumed to actually be values in dB. encodebias is used to
+ // assign an error weight to 0 dB. We have two additional flags:
+ // zeroflag indicates if entry zero is to represent -Inf dB; negflag
+ // indicates if we're to represent negative linear values in a
+ // mirror of the positive mapping.
+
+ int[] quantlist; // map == 1: (int)(entries/dim) element column map
+ // map == 2: list of dim*entries quantized entry vals
+
+ StaticCodeBook(){
+ }
+
+ int pack(Buffer opb){
+ int i;
+ boolean ordered=false;
+
+ opb.write(0x564342, 24);
+ opb.write(dim, 16);
+ opb.write(entries, 24);
+
+ // pack the codewords. There are two packings; length ordered and
+ // length random. Decide between the two now.
+
+ for(i=1; i<entries; i++){
+ if(lengthlist[i]<lengthlist[i-1])
+ break;
+ }
+ if(i==entries)
+ ordered=true;
+
+ if(ordered){
+ // length ordered. We only need to say how many codewords of
+ // each length. The actual codewords are generated
+ // deterministically
+
+ int count=0;
+ opb.write(1, 1); // ordered
+ opb.write(lengthlist[0]-1, 5); // 1 to 32
+
+ for(i=1; i<entries; i++){
+ int _this=lengthlist[i];
+ int _last=lengthlist[i-1];
+ if(_this>_last){
+ for(int j=_last; j<_this; j++){
+ opb.write(i-count, Util.ilog(entries-count));
+ count=i;
+ }
+ }
+ }
+ opb.write(i-count, Util.ilog(entries-count));
+ }
+ else{
+ // length random. Again, we don't code the codeword itself, just
+ // the length. This time, though, we have to encode each length
+ opb.write(0, 1); // unordered
+
+ // algortihmic mapping has use for 'unused entries', which we tag
+ // here. The algorithmic mapping happens as usual, but the unused
+ // entry has no codeword.
+ for(i=0; i<entries; i++){
+ if(lengthlist[i]==0)
+ break;
+ }
+
+ if(i==entries){
+ opb.write(0, 1); // no unused entries
+ for(i=0; i<entries; i++){
+ opb.write(lengthlist[i]-1, 5);
+ }
+ }
+ else{
+ opb.write(1, 1); // we have unused entries; thus we tag
+ for(i=0; i<entries; i++){
+ if(lengthlist[i]==0){
+ opb.write(0, 1);
+ }
+ else{
+ opb.write(1, 1);
+ opb.write(lengthlist[i]-1, 5);
+ }
+ }
+ }
+ }
+
+ // is the entry number the desired return value, or do we have a
+ // mapping? If we have a mapping, what type?
+ opb.write(maptype, 4);
+ switch(maptype){
+ case 0:
+ // no mapping
+ break;
+ case 1:
+ case 2:
+ // implicitly populated value mapping
+ // explicitly populated value mapping
+ if(quantlist==null){
+ // no quantlist? error
+ return (-1);
+ }
+
+ // values that define the dequantization
+ opb.write(q_min, 32);
+ opb.write(q_delta, 32);
+ opb.write(q_quant-1, 4);
+ opb.write(q_sequencep, 1);
+
+ {
+ int quantvals=0;
+ switch(maptype){
+ case 1:
+ // a single column of (c->entries/c->dim) quantized values for
+ // building a full value list algorithmically (square lattice)
+ quantvals=maptype1_quantvals();
+ break;
+ case 2:
+ // every value (c->entries*c->dim total) specified explicitly
+ quantvals=entries*dim;
+ break;
+ }
+
+ // quantized values
+ for(i=0; i<quantvals; i++){
+ opb.write(Math.abs(quantlist[i]), q_quant);
+ }
+ }
+ break;
+ default:
+ // error case; we don't have any other map types now
+ return (-1);
+ }
+ return (0);
+ }
+
+ // unpacks a codebook from the packet buffer into the codebook struct,
+ // readies the codebook auxiliary structures for decode
+ int unpack(Buffer opb){
+ int i;
+ //memset(s,0,sizeof(static_codebook));
+
+ // make sure alignment is correct
+ if(opb.read(24)!=0x564342){
+ // goto _eofout;
+ clear();
+ return (-1);
+ }
+
+ // first the basic parameters
+ dim=opb.read(16);
+ entries=opb.read(24);
+ if(entries==-1){
+ // goto _eofout;
+ clear();
+ return (-1);
+ }
+
+ // codeword ordering.... length ordered or unordered?
+ switch(opb.read(1)){
+ case 0:
+ // unordered
+ lengthlist=new int[entries];
+
+ // allocated but unused entries?
+ if(opb.read(1)!=0){
+ // yes, unused entries
+
+ for(i=0; i<entries; i++){
+ if(opb.read(1)!=0){
+ int num=opb.read(5);
+ if(num==-1){
+ // goto _eofout;
+ clear();
+ return (-1);
+ }
+ lengthlist[i]=num+1;
+ }
+ else{
+ lengthlist[i]=0;
+ }
+ }
+ }
+ else{
+ // all entries used; no tagging
+ for(i=0; i<entries; i++){
+ int num=opb.read(5);
+ if(num==-1){
+ // goto _eofout;
+ clear();
+ return (-1);
+ }
+ lengthlist[i]=num+1;
+ }
+ }
+ break;
+ case 1:
+ // ordered
+ {
+ int length=opb.read(5)+1;
+ lengthlist=new int[entries];
+
+ for(i=0; i<entries;){
+ int num=opb.read(Util.ilog(entries-i));
+ if(num==-1){
+ // goto _eofout;
+ clear();
+ return (-1);
+ }
+ for(int j=0; j<num; j++, i++){
+ lengthlist[i]=length;
+ }
+ length++;
+ }
+ }
+ break;
+ default:
+ // EOF
+ return (-1);
+ }
+
+ // Do we have a mapping to unpack?
+ switch((maptype=opb.read(4))){
+ case 0:
+ // no mapping
+ break;
+ case 1:
+ case 2:
+ // implicitly populated value mapping
+ // explicitly populated value mapping
+ q_min=opb.read(32);
+ q_delta=opb.read(32);
+ q_quant=opb.read(4)+1;
+ q_sequencep=opb.read(1);
+
+ {
+ int quantvals=0;
+ switch(maptype){
+ case 1:
+ quantvals=maptype1_quantvals();
+ break;
+ case 2:
+ quantvals=entries*dim;
+ break;
+ }
+
+ // quantized values
+ quantlist=new int[quantvals];
+ for(i=0; i<quantvals; i++){
+ quantlist[i]=opb.read(q_quant);
+ }
+ if(quantlist[quantvals-1]==-1){
+ // goto _eofout;
+ clear();
+ return (-1);
+ }
+ }
+ break;
+ default:
+ // goto _eofout;
+ clear();
+ return (-1);
+ }
+ // all set
+ return (0);
+ // _errout:
+ // _eofout:
+ // vorbis_staticbook_clear(s);
+ // return(-1);
+ }
+
+ // there might be a straightforward one-line way to do the below
+ // that's portable and totally safe against roundoff, but I haven't
+ // thought of it. Therefore, we opt on the side of caution
+ private int maptype1_quantvals(){
+ int vals=(int)(Math.floor(Math.pow(entries, 1./dim)));
+
+ // the above *should* be reliable, but we'll not assume that FP is
+ // ever reliable when bitstream sync is at stake; verify via integer
+ // means that vals really is the greatest value of dim for which
+ // vals^b->bim <= b->entries
+ // treat the above as an initial guess
+ while(true){
+ int acc=1;
+ int acc1=1;
+ for(int i=0; i<dim; i++){
+ acc*=vals;
+ acc1*=vals+1;
+ }
+ if(acc<=entries&&acc1>entries){
+ return (vals);
+ }
+ else{
+ if(acc>entries){
+ vals--;
+ }
+ else{
+ vals++;
+ }
+ }
+ }
+ }
+
+ void clear(){
+ }
+
+ // unpack the quantized list of values for encode/decode
+ // we need to deal with two map types: in map type 1, the values are
+ // generated algorithmically (each column of the vector counts through
+ // the values in the quant vector). in map type 2, all the values came
+ // in in an explicit list. Both value lists must be unpacked
+ float[] unquantize(){
+
+ if(maptype==1||maptype==2){
+ int quantvals;
+ float mindel=float32_unpack(q_min);
+ float delta=float32_unpack(q_delta);
+ float[] r=new float[entries*dim];
+
+ // maptype 1 and 2 both use a quantized value vector, but
+ // different sizes
+ switch(maptype){
+ case 1:
+ // most of the time, entries%dimensions == 0, but we need to be
+ // well defined. We define that the possible vales at each
+ // scalar is values == entries/dim. If entries%dim != 0, we'll
+ // have 'too few' values (values*dim<entries), which means that
+ // we'll have 'left over' entries; left over entries use zeroed
+ // values (and are wasted). So don't generate codebooks like that
+ quantvals=maptype1_quantvals();
+ for(int j=0; j<entries; j++){
+ float last=0.f;
+ int indexdiv=1;
+ for(int k=0; k<dim; k++){
+ int index=(j/indexdiv)%quantvals;
+ float val=quantlist[index];
+ val=Math.abs(val)*delta+mindel+last;
+ if(q_sequencep!=0)
+ last=val;
+ r[j*dim+k]=val;
+ indexdiv*=quantvals;
+ }
+ }
+ break;
+ case 2:
+ for(int j=0; j<entries; j++){
+ float last=0.f;
+ for(int k=0; k<dim; k++){
+ float val=quantlist[j*dim+k];
+ //if((j*dim+k)==0){System.err.println(" | 0 -> "+val+" | ");}
+ val=Math.abs(val)*delta+mindel+last;
+ if(q_sequencep!=0)
+ last=val;
+ r[j*dim+k]=val;
+ //if((j*dim+k)==0){System.err.println(" $ r[0] -> "+r[0]+" | ");}
+ }
+ }
+ //System.err.println("\nr[0]="+r[0]);
+ }
+ return (r);
+ }
+ return (null);
+ }
+
+ // 32 bit float (not IEEE; nonnormalized mantissa +
+ // biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm
+ // Why not IEEE? It's just not that important here.
+
+ static final int VQ_FEXP=10;
+ static final int VQ_FMAN=21;
+ static final int VQ_FEXP_BIAS=768; // bias toward values smaller than 1.
+
+ // doesn't currently guard under/overflow
+ static long float32_pack(float val){
+ int sign=0;
+ int exp;
+ int mant;
+ if(val<0){
+ sign=0x80000000;
+ val=-val;
+ }
+ exp=(int)Math.floor(Math.log(val)/Math.log(2));
+ mant=(int)Math.rint(Math.pow(val, (VQ_FMAN-1)-exp));
+ exp=(exp+VQ_FEXP_BIAS)<<VQ_FMAN;
+ return (sign|exp|mant);
+ }
+
+ static float float32_unpack(int val){
+ float mant=val&0x1fffff;
+ float exp=(val&0x7fe00000)>>>VQ_FMAN;
+ if((val&0x80000000)!=0)
+ mant=-mant;
+ return (ldexp(mant, ((int)exp)-(VQ_FMAN-1)-VQ_FEXP_BIAS));
+ }
+
+ static float ldexp(float foo, int e){
+ return (float)(foo*Math.pow(2, e));
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Time0.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Time0.java
new file mode 100644
index 0000000..fef6549
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Time0.java
@@ -0,0 +1,52 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+class Time0 extends FuncTime{
+ void pack(Object i, Buffer opb){
+ }
+
+ Object unpack(Info vi, Buffer opb){
+ return "";
+ }
+
+ Object look(DspState vd, InfoMode mi, Object i){
+ return "";
+ }
+
+ void free_info(Object i){
+ }
+
+ void free_look(Object i){
+ }
+
+ int inverse(Block vb, Object i, float[] in, float[] out){
+ return 0;
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/Util.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Util.java
new file mode 100644
index 0000000..983c568
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/Util.java
@@ -0,0 +1,30 @@
+package com.jcraft.jorbis;
+
+class Util{
+ static int ilog(int v){
+ int ret=0;
+ while(v!=0){
+ ret++;
+ v>>>=1;
+ }
+ return (ret);
+ }
+
+ static int ilog2(int v){
+ int ret=0;
+ while(v>1){
+ ret++;
+ v>>>=1;
+ }
+ return (ret);
+ }
+
+ static int icount(int v){
+ int ret=0;
+ while(v!=0){
+ ret+=(v&1);
+ v>>>=1;
+ }
+ return (ret);
+ }
+}
diff --git a/ardor3d-audio/src/main/java/com/jcraft/jorbis/VorbisFile.java b/ardor3d-audio/src/main/java/com/jcraft/jorbis/VorbisFile.java
new file mode 100644
index 0000000..f2e7336
--- /dev/null
+++ b/ardor3d-audio/src/main/java/com/jcraft/jorbis/VorbisFile.java
@@ -0,0 +1,1397 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<[email protected]>
+ *
+ * Many thanks to
+ * Monty <[email protected]> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+public class VorbisFile{
+ static final int CHUNKSIZE=8500;
+ static final int SEEK_SET=0;
+ static final int SEEK_CUR=1;
+ static final int SEEK_END=2;
+
+ static final int OV_FALSE=-1;
+ static final int OV_EOF=-2;
+ static final int OV_HOLE=-3;
+
+ static final int OV_EREAD=-128;
+ static final int OV_EFAULT=-129;
+ static final int OV_EIMPL=-130;
+ static final int OV_EINVAL=-131;
+ static final int OV_ENOTVORBIS=-132;
+ static final int OV_EBADHEADER=-133;
+ static final int OV_EVERSION=-134;
+ static final int OV_ENOTAUDIO=-135;
+ static final int OV_EBADPACKET=-136;
+ static final int OV_EBADLINK=-137;
+ static final int OV_ENOSEEK=-138;
+
+ InputStream datasource;
+ boolean seekable=false;
+ long offset;
+ long end;
+
+ SyncState oy=new SyncState();
+
+ int links;
+ long[] offsets;
+ long[] dataoffsets;
+ int[] serialnos;
+ long[] pcmlengths;
+ Info[] vi;
+ Comment[] vc;
+
+ // Decoding working state local storage
+ long pcm_offset;
+ boolean decode_ready=false;
+
+ int current_serialno;
+ int current_link;
+
+ float bittrack;
+ float samptrack;
+
+ StreamState os=new StreamState(); // take physical pages, weld into a logical
+ // stream of packets
+ DspState vd=new DspState(); // central working state for
+ // the packet->PCM decoder
+ Block vb=new Block(vd); // local working space for packet->PCM decode
+
+ //ov_callbacks callbacks;
+
+ public VorbisFile(String file) throws JOrbisException{
+ super();
+ InputStream is=null;
+ try{
+ is=new SeekableInputStream(file);
+ int ret=open(is, null, 0);
+ if(ret==-1){
+ throw new JOrbisException("VorbisFile: open return -1");
+ }
+ }
+ catch(Exception e){
+ throw new JOrbisException("VorbisFile: "+e.toString());
+ }
+ finally{
+ if(is!=null){
+ try{
+ is.close();
+ }
+ catch(IOException e){
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public VorbisFile(InputStream is, byte[] initial, int ibytes)
+ throws JOrbisException{
+ super();
+ int ret=open(is, initial, ibytes);
+ if(ret==-1){
+ }
+ }
+
+ private int get_data(){
+ int index=oy.buffer(CHUNKSIZE);
+ byte[] buffer=oy.data;
+ int bytes=0;
+ try{
+ bytes=datasource.read(buffer, index, CHUNKSIZE);
+ }
+ catch(Exception e){
+ return OV_EREAD;
+ }
+ oy.wrote(bytes);
+ if(bytes==-1){
+ bytes=0;
+ }
+ return bytes;
+ }
+
+ private void seek_helper(long offst){
+ fseek(datasource, offst, SEEK_SET);
+ this.offset=offst;
+ oy.reset();
+ }
+
+ private int get_next_page(Page page, long boundary){
+ if(boundary>0)
+ boundary+=offset;
+ while(true){
+ int more;
+ if(boundary>0&&offset>=boundary)
+ return OV_FALSE;
+ more=oy.pageseek(page);
+ if(more<0){
+ offset-=more;
+ }
+ else{
+ if(more==0){
+ if(boundary==0)
+ return OV_FALSE;
+ int ret=get_data();
+ if(ret==0)
+ return OV_EOF;
+ if(ret<0)
+ return OV_EREAD;
+ }
+ else{
+ int ret=(int)offset; //!!!
+ offset+=more;
+ return ret;
+ }
+ }
+ }
+ }
+
+ private int get_prev_page(Page page) throws JOrbisException{
+ long begin=offset; //!!!
+ int ret;
+ int offst=-1;
+ while(offst==-1){
+ begin-=CHUNKSIZE;
+ if(begin<0)
+ begin=0;
+ seek_helper(begin);
+ while(offset<begin+CHUNKSIZE){
+ ret=get_next_page(page, begin+CHUNKSIZE-offset);
+ if(ret==OV_EREAD){
+ return OV_EREAD;
+ }
+ if(ret<0){
+ if(offst==-1)
+ throw new JOrbisException();
+ break;
+ }
+ else{
+ offst=ret;
+ }
+ }
+ }
+ seek_helper(offst); //!!!
+ ret=get_next_page(page, CHUNKSIZE);
+ if(ret<0){
+ return OV_EFAULT;
+ }
+ return offst;
+ }
+
+ int bisect_forward_serialno(long begin, long searched, long end,
+ int currentno, int m){
+ long endsearched=end;
+ long next=end;
+ Page page=new Page();
+ int ret;
+
+ while(searched<endsearched){
+ long bisect;
+ if(endsearched-searched<CHUNKSIZE){
+ bisect=searched;
+ }
+ else{
+ bisect=(searched+endsearched)/2;
+ }
+
+ seek_helper(bisect);
+ ret=get_next_page(page, -1);
+ if(ret==OV_EREAD)
+ return OV_EREAD;
+ if(ret<0||page.serialno()!=currentno){
+ endsearched=bisect;
+ if(ret>=0)
+ next=ret;
+ }
+ else{
+ searched=ret+page.header_len+page.body_len;
+ }
+ }
+ seek_helper(next);
+ ret=get_next_page(page, -1);
+ if(ret==OV_EREAD)
+ return OV_EREAD;
+
+ if(searched>=end||ret==-1){
+ links=m+1;
+ offsets=new long[m+2];
+ offsets[m+1]=searched;
+ }
+ else{
+ ret=bisect_forward_serialno(next, offset, end, page.serialno(), m+1);
+ if(ret==OV_EREAD)
+ return OV_EREAD;
+ }
+ offsets[m]=begin;
+ return 0;
+ }
+
+ // uses the local ogg_stream storage in vf; this is important for
+ // non-streaming input sources
+ int fetch_headers(Info vi, Comment vc, int[] serialno, Page og_ptr){
+ Page og=new Page();
+ Packet op=new Packet();
+ int ret;
+
+ if(og_ptr==null){
+ ret=get_next_page(og, CHUNKSIZE);
+ if(ret==OV_EREAD)
+ return OV_EREAD;
+ if(ret<0)
+ return OV_ENOTVORBIS;
+ og_ptr=og;
+ }
+
+ if(serialno!=null)
+ serialno[0]=og_ptr.serialno();
+
+ os.init(og_ptr.serialno());
+
+ // extract the initial header from the first page and verify that the
+ // Ogg bitstream is in fact Vorbis data
+
+ vi.init();
+ vc.init();
+
+ int i=0;
+ while(i<3){
+ os.pagein(og_ptr);
+ while(i<3){
+ int result=os.packetout(op);
+ if(result==0)
+ break;
+ if(result==-1){
+ vi.clear();
+ vc.clear();
+ os.clear();
+ return -1;
+ }
+ if(vi.synthesis_headerin(vc, op)!=0){
+ vi.clear();
+ vc.clear();
+ os.clear();
+ return -1;
+ }
+ i++;
+ }
+ if(i<3)
+ if(get_next_page(og_ptr, 1)<0){
+ vi.clear();
+ vc.clear();
+ os.clear();
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ // last step of the OggVorbis_File initialization; get all the
+ // vorbis_info structs and PCM positions. Only called by the seekable
+ // initialization (local stream storage is hacked slightly; pay
+ // attention to how that's done)
+ void prefetch_all_headers(Info first_i, Comment first_c, int dataoffset)
+ throws JOrbisException{
+ Page og=new Page();
+ int ret;
+
+ vi=new Info[links];
+ vc=new Comment[links];
+ dataoffsets=new long[links];
+ pcmlengths=new long[links];
+ serialnos=new int[links];
+
+ for(int i=0; i<links; i++){
+ if(first_i!=null&&first_c!=null&&i==0){
+ // we already grabbed the initial header earlier. This just
+ // saves the waste of grabbing it again
+ vi[i]=first_i;
+ vc[i]=first_c;
+ dataoffsets[i]=dataoffset;
+ }
+ else{
+ // seek to the location of the initial header
+ seek_helper(offsets[i]); //!!!
+ vi[i]=new Info();
+ vc[i]=new Comment();
+ if(fetch_headers(vi[i], vc[i], null, null)==-1){
+ dataoffsets[i]=-1;
+ }
+ else{
+ dataoffsets[i]=offset;
+ os.clear();
+ }
+ }
+
+ // get the serial number and PCM length of this link. To do this,
+ // get the last page of the stream
+ {
+ long end=offsets[i+1]; //!!!
+ seek_helper(end);
+
+ while(true){
+ ret=get_prev_page(og);
+ if(ret==-1){
+ // this should not be possible
+ vi[i].clear();
+ vc[i].clear();
+ break;
+ }
+ if(og.granulepos()!=-1){
+ serialnos[i]=og.serialno();
+ pcmlengths[i]=og.granulepos();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private int make_decode_ready(){
+ if(decode_ready)
+ System.exit(1);
+ vd.synthesis_init(vi[0]);
+ vb.init(vd);
+ decode_ready=true;
+ return (0);
+ }
+
+ int open_seekable() throws JOrbisException{
+ Info initial_i=new Info();
+ Comment initial_c=new Comment();
+ int serialno;
+ long end;
+ int ret;
+ int dataoffset;
+ Page og=new Page();
+ // is this even vorbis...?
+ int[] foo=new int[1];
+ ret=fetch_headers(initial_i, initial_c, foo, null);
+ serialno=foo[0];
+ dataoffset=(int)offset; //!!
+ os.clear();
+ if(ret==-1)
+ return (-1);
+ if(ret<0)
+ return (ret);
+ // we can seek, so set out learning all about this file
+ seekable=true;
+ fseek(datasource, 0, SEEK_END);
+ offset=ftell(datasource);
+ end=offset;
+ // We get the offset for the last page of the physical bitstream.
+ // Most OggVorbis files will contain a single logical bitstream
+ end=get_prev_page(og);
+ // moer than one logical bitstream?
+ if(og.serialno()!=serialno){
+ // Chained bitstream. Bisect-search each logical bitstream
+ // section. Do so based on serial number only
+ if(bisect_forward_serialno(0, 0, end+1, serialno, 0)<0){
+ clear();
+ return OV_EREAD;
+ }
+ }
+ else{
+ // Only one logical bitstream
+ if(bisect_forward_serialno(0, end, end+1, serialno, 0)<0){
+ clear();
+ return OV_EREAD;
+ }
+ }
+ prefetch_all_headers(initial_i, initial_c, dataoffset);
+ return 0;
+ }
+
+ int open_nonseekable(){
+ // we cannot seek. Set up a 'single' (current) logical bitstream entry
+ links=1;
+ vi=new Info[links];
+ vi[0]=new Info(); // ??
+ vc=new Comment[links];
+ vc[0]=new Comment(); // ?? bug?
+
+ // Try to fetch the headers, maintaining all the storage
+ int[] foo=new int[1];
+ if(fetch_headers(vi[0], vc[0], foo, null)==-1)
+ return (-1);
+ current_serialno=foo[0];
+ make_decode_ready();
+ return 0;
+ }
+
+ // clear out the current logical bitstream decoder
+ void decode_clear(){
+ os.clear();
+ vd.clear();
+ vb.clear();
+ decode_ready=false;
+ bittrack=0.f;
+ samptrack=0.f;
+ }
+
+ // fetch and process a packet. Handles the case where we're at a
+ // bitstream boundary and dumps the decoding machine. If the decoding
+ // machine is unloaded, it loads it. It also keeps pcm_offset up to
+ // date (seek and read both use this. seek uses a special hack with
+ // readp).
+ //
+ // return: -1) hole in the data (lost packet)
+ // 0) need more date (only if readp==0)/eof
+ // 1) got a packet
+
+ int process_packet(int readp){
+ Page og=new Page();
+
+ // handle one packet. Try to fetch it from current stream state
+ // extract packets from page
+ while(true){
+ // process a packet if we can. If the machine isn't loaded,
+ // neither is a page
+ if(decode_ready){
+ Packet op=new Packet();
+ int result=os.packetout(op);
+ long granulepos;
+ // if(result==-1)return(-1); // hole in the data. For now, swallow
+ // and go. We'll need to add a real
+ // error code in a bit.
+ if(result>0){
+ // got a packet. process it
+ granulepos=op.granulepos;
+ if(vb.synthesis(op)==0){ // lazy check for lazy
+ // header handling. The
+ // header packets aren't
+ // audio, so if/when we
+ // submit them,
+ // vorbis_synthesis will
+ // reject them
+ // suck in the synthesis data and track bitrate
+ {
+ int oldsamples=vd.synthesis_pcmout(null, null);
+ vd.synthesis_blockin(vb);
+ samptrack+=vd.synthesis_pcmout(null, null)-oldsamples;
+ bittrack+=op.bytes*8;
+ }
+
+ // update the pcm offset.
+ if(granulepos!=-1&&op.e_o_s==0){
+ int link=(seekable ? current_link : 0);
+ int samples;
+ // this packet has a pcm_offset on it (the last packet
+ // completed on a page carries the offset) After processing
+ // (above), we know the pcm position of the *last* sample
+ // ready to be returned. Find the offset of the *first*
+ //
+ // As an aside, this trick is inaccurate if we begin
+ // reading anew right at the last page; the end-of-stream
+ // granulepos declares the last frame in the stream, and the
+ // last packet of the last page may be a partial frame.
+ // So, we need a previous granulepos from an in-sequence page
+ // to have a reference point. Thus the !op.e_o_s clause above
+
+ samples=vd.synthesis_pcmout(null, null);
+ granulepos-=samples;
+ for(int i=0; i<link; i++){
+ granulepos+=pcmlengths[i];
+ }
+ pcm_offset=granulepos;
+ }
+ return (1);
+ }
+ }
+ }
+
+ if(readp==0)
+ return (0);
+ if(get_next_page(og, -1)<0)
+ return (0); // eof. leave unitialized
+
+ // bitrate tracking; add the header's bytes here, the body bytes
+ // are done by packet above
+ bittrack+=og.header_len*8;
+
+ // has our decoding just traversed a bitstream boundary?
+ if(decode_ready){
+ if(current_serialno!=og.serialno()){
+ decode_clear();
+ }
+ }
+
+ // Do we need to load a new machine before submitting the page?
+ // This is different in the seekable and non-seekable cases.
+ //
+ // In the seekable case, we already have all the header
+ // information loaded and cached; we just initialize the machine
+ // with it and continue on our merry way.
+ //
+ // In the non-seekable (streaming) case, we'll only be at a
+ // boundary if we just left the previous logical bitstream and
+ // we're now nominally at the header of the next bitstream
+
+ if(!decode_ready){
+ int i;
+ if(seekable){
+ current_serialno=og.serialno();
+
+ // match the serialno to bitstream section. We use this rather than
+ // offset positions to avoid problems near logical bitstream
+ // boundaries
+ for(i=0; i<links; i++){
+ if(serialnos[i]==current_serialno)
+ break;
+ }
+ if(i==links)
+ return (-1); // sign of a bogus stream. error out,
+ // leave machine uninitialized
+ current_link=i;
+
+ os.init(current_serialno);
+ os.reset();
+
+ }
+ else{
+ // we're streaming
+ // fetch the three header packets, build the info struct
+ int foo[]=new int[1];
+ int ret=fetch_headers(vi[0], vc[0], foo, og);
+ current_serialno=foo[0];
+ if(ret!=0)
+ return ret;
+ current_link++;
+ i=0;
+ }
+ make_decode_ready();
+ }
+ os.pagein(og);
+ }
+ }
+
+ // The helpers are over; it's all toplevel interface from here on out
+ // clear out the OggVorbis_File struct
+ int clear(){
+ vb.clear();
+ vd.clear();
+ os.clear();
+
+ if(vi!=null&&links!=0){
+ for(int i=0; i<links; i++){
+ vi[i].clear();
+ vc[i].clear();
+ }
+ vi=null;
+ vc=null;
+ }
+ if(dataoffsets!=null)
+ dataoffsets=null;
+ if(pcmlengths!=null)
+ pcmlengths=null;
+ if(serialnos!=null)
+ serialnos=null;
+ if(offsets!=null)
+ offsets=null;
+ oy.clear();
+
+ return (0);
+ }
+
+ static int fseek(InputStream fis, long off, int whence){
+ if(fis instanceof SeekableInputStream){
+ SeekableInputStream sis=(SeekableInputStream)fis;
+ try{
+ if(whence==SEEK_SET){
+ sis.seek(off);
+ }
+ else if(whence==SEEK_END){
+ sis.seek(sis.getLength()-off);
+ }
+ else{
+ }
+ }
+ catch(Exception e){
+ }
+ return 0;
+ }
+ try{
+ if(whence==0){
+ fis.reset();
+ }
+ fis.skip(off);
+ }
+ catch(Exception e){
+ return -1;
+ }
+ return 0;
+ }
+
+ static long ftell(InputStream fis){
+ try{
+ if(fis instanceof SeekableInputStream){
+ SeekableInputStream sis=(SeekableInputStream)fis;
+ return (sis.tell());
+ }
+ }
+ catch(Exception e){
+ }
+ return 0;
+ }
+
+ // inspects the OggVorbis file and finds/documents all the logical
+ // bitstreams contained in it. Tries to be tolerant of logical
+ // bitstream sections that are truncated/woogie.
+ //
+ // return: -1) error
+ // 0) OK
+
+ int open(InputStream is, byte[] initial, int ibytes) throws JOrbisException{
+ return open_callbacks(is, initial, ibytes);
+ }
+
+ int open_callbacks(InputStream is, byte[] initial, int ibytes//, callbacks callbacks
+ ) throws JOrbisException{
+ int ret;
+ datasource=is;
+
+ oy.init();
+
+ // perhaps some data was previously read into a buffer for testing
+ // against other stream types. Allow initialization from this
+ // previously read data (as we may be reading from a non-seekable
+ // stream)
+ if(initial!=null){
+ int index=oy.buffer(ibytes);
+ System.arraycopy(initial, 0, oy.data, index, ibytes);
+ oy.wrote(ibytes);
+ }
+ // can we seek? Stevens suggests the seek test was portable
+ if(is instanceof SeekableInputStream){
+ ret=open_seekable();
+ }
+ else{
+ ret=open_nonseekable();
+ }
+ if(ret!=0){
+ datasource=null;
+ clear();
+ }
+ return ret;
+ }
+
+ // How many logical bitstreams in this physical bitstream?
+ public int streams(){
+ return links;
+ }
+
+ // Is the FILE * associated with vf seekable?
+ public boolean seekable(){
+ return seekable;
+ }
+
+ // returns the bitrate for a given logical bitstream or the entire
+ // physical bitstream. If the file is open for random access, it will
+ // find the *actual* average bitrate. If the file is streaming, it
+ // returns the nominal bitrate (if set) else the average of the
+ // upper/lower bounds (if set) else -1 (unset).
+ //
+ // If you want the actual bitrate field settings, get them from the
+ // vorbis_info structs
+
+ public int bitrate(int i){
+ if(i>=links)
+ return (-1);
+ if(!seekable&&i!=0)
+ return (bitrate(0));
+ if(i<0){
+ long bits=0;
+ for(int j=0; j<links; j++){
+ bits+=(offsets[j+1]-dataoffsets[j])*8;
+ }
+ return ((int)Math.rint(bits/time_total(-1)));
+ }
+ else{
+ if(seekable){
+ // return the actual bitrate
+ return ((int)Math.rint((offsets[i+1]-dataoffsets[i])*8/time_total(i)));
+ }
+ else{
+ // return nominal if set
+ if(vi[i].bitrate_nominal>0){
+ return vi[i].bitrate_nominal;
+ }
+ else{
+ if(vi[i].bitrate_upper>0){
+ if(vi[i].bitrate_lower>0){
+ return (vi[i].bitrate_upper+vi[i].bitrate_lower)/2;
+ }
+ else{
+ return vi[i].bitrate_upper;
+ }
+ }
+ return (-1);
+ }
+ }
+ }
+ }
+
+ // returns the actual bitrate since last call. returns -1 if no
+ // additional data to offer since last call (or at beginning of stream)
+ public int bitrate_instant(){
+ int _link=(seekable ? current_link : 0);
+ if(samptrack==0)
+ return (-1);
+ int ret=(int)(bittrack/samptrack*vi[_link].rate+.5);
+ bittrack=0.f;
+ samptrack=0.f;
+ return (ret);
+ }
+
+ public int serialnumber(int i){
+ if(i>=links)
+ return (-1);
+ if(!seekable&&i>=0)
+ return (serialnumber(-1));
+ if(i<0){
+ return (current_serialno);
+ }
+ else{
+ return (serialnos[i]);
+ }
+ }
+
+ // returns: total raw (compressed) length of content if i==-1
+ // raw (compressed) length of that logical bitstream for i==0 to n
+ // -1 if the stream is not seekable (we can't know the length)
+
+ public long raw_total(int i){
+ if(!seekable||i>=links)
+ return (-1);
+ if(i<0){
+ long acc=0; // bug?
+ for(int j=0; j<links; j++){
+ acc+=raw_total(j);
+ }
+ return (acc);
+ }
+ else{
+ return (offsets[i+1]-offsets[i]);
+ }
+ }
+
+ // returns: total PCM length (samples) of content if i==-1
+ // PCM length (samples) of that logical bitstream for i==0 to n
+ // -1 if the stream is not seekable (we can't know the length)
+ public long pcm_total(int i){
+ if(!seekable||i>=links)
+ return (-1);
+ if(i<0){
+ long acc=0;
+ for(int j=0; j<links; j++){
+ acc+=pcm_total(j);
+ }
+ return (acc);
+ }
+ else{
+ return (pcmlengths[i]);
+ }
+ }
+
+ // returns: total seconds of content if i==-1
+ // seconds in that logical bitstream for i==0 to n
+ // -1 if the stream is not seekable (we can't know the length)
+ public float time_total(int i){
+ if(!seekable||i>=links)
+ return (-1);
+ if(i<0){
+ float acc=0;
+ for(int j=0; j<links; j++){
+ acc+=time_total(j);
+ }
+ return (acc);
+ }
+ else{
+ return ((float)(pcmlengths[i])/vi[i].rate);
+ }
+ }
+
+ // seek to an offset relative to the *compressed* data. This also
+ // immediately sucks in and decodes pages to update the PCM cursor. It
+ // will cross a logical bitstream boundary, but only if it can't get
+ // any packets out of the tail of the bitstream we seek to (so no
+ // surprises).
+ //
+ // returns zero on success, nonzero on failure
+
+ public int raw_seek(int pos){
+ if(!seekable)
+ return (-1); // don't dump machine if we can't seek
+ if(pos<0||pos>offsets[links]){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+
+ // clear out decoding machine state
+ pcm_offset=-1;
+ decode_clear();
+
+ // seek
+ seek_helper(pos);
+
+ // we need to make sure the pcm_offset is set. We use the
+ // _fetch_packet helper to process one packet with readp set, then
+ // call it until it returns '0' with readp not set (the last packet
+ // from a page has the 'granulepos' field set, and that's how the
+ // helper updates the offset
+
+ switch(process_packet(1)){
+ case 0:
+ // oh, eof. There are no packets remaining. Set the pcm offset to
+ // the end of file
+ pcm_offset=pcm_total(-1);
+ return (0);
+ case -1:
+ // error! missing data or invalid bitstream structure
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ default:
+ // all OK
+ break;
+ }
+ while(true){
+ switch(process_packet(0)){
+ case 0:
+ // the offset is set. If it's a bogus bitstream with no offset
+ // information, it's not but that's not our fault. We still run
+ // gracefully, we're just missing the offset
+ return (0);
+ case -1:
+ // error! missing data or invalid bitstream structure
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ default:
+ // continue processing packets
+ break;
+ }
+ }
+
+ // seek_error:
+ // dump the machine so we're in a known state
+ //pcm_offset=-1;
+ //decode_clear();
+ //return -1;
+ }
+
+ // seek to a sample offset relative to the decompressed pcm stream
+ // returns zero on success, nonzero on failure
+
+ public int pcm_seek(long pos){
+ int link=-1;
+ long total=pcm_total(-1);
+
+ if(!seekable)
+ return (-1); // don't dump machine if we can't seek
+ if(pos<0||pos>total){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+
+ // which bitstream section does this pcm offset occur in?
+ for(link=links-1; link>=0; link--){
+ total-=pcmlengths[link];
+ if(pos>=total)
+ break;
+ }
+
+ // search within the logical bitstream for the page with the highest
+ // pcm_pos preceeding (or equal to) pos. There is a danger here;
+ // missing pages or incorrect frame number information in the
+ // bitstream could make our task impossible. Account for that (it
+ // would be an error condition)
+ {
+ long target=pos-total;
+ long end=offsets[link+1];
+ long begin=offsets[link];
+ int best=(int)begin;
+
+ Page og=new Page();
+ while(begin<end){
+ long bisect;
+ int ret;
+
+ if(end-begin<CHUNKSIZE){
+ bisect=begin;
+ }
+ else{
+ bisect=(end+begin)/2;
+ }
+
+ seek_helper(bisect);
+ ret=get_next_page(og, end-bisect);
+
+ if(ret==-1){
+ end=bisect;
+ }
+ else{
+ long granulepos=og.granulepos();
+ if(granulepos<target){
+ best=ret; // raw offset of packet with granulepos
+ begin=offset; // raw offset of next packet
+ }
+ else{
+ end=bisect;
+ }
+ }
+ }
+ // found our page. seek to it (call raw_seek).
+ if(raw_seek(best)!=0){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+ }
+
+ // verify result
+ if(pcm_offset>=pos){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+ if(pos>pcm_total(-1)){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+
+ // discard samples until we reach the desired position. Crossing a
+ // logical bitstream boundary with abandon is OK.
+ while(pcm_offset<pos){
+ int target=(int)(pos-pcm_offset);
+ float[][][] _pcm=new float[1][][];
+ int[] _index=new int[getInfo(-1).channels];
+ int samples=vd.synthesis_pcmout(_pcm, _index);
+
+ if(samples>target)
+ samples=target;
+ vd.synthesis_read(samples);
+ pcm_offset+=samples;
+
+ if(samples<target)
+ if(process_packet(1)==0){
+ pcm_offset=pcm_total(-1); // eof
+ }
+ }
+ return 0;
+
+ // seek_error:
+ // dump machine so we're in a known state
+ //pcm_offset=-1;
+ //decode_clear();
+ //return -1;
+ }
+
+ // seek to a playback time relative to the decompressed pcm stream
+ // returns zero on success, nonzero on failure
+ int time_seek(float seconds){
+ // translate time to PCM position and call pcm_seek
+
+ int link=-1;
+ long pcm_total=pcm_total(-1);
+ float time_total=time_total(-1);
+
+ if(!seekable)
+ return (-1); // don't dump machine if we can't seek
+ if(seconds<0||seconds>time_total){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+
+ // which bitstream section does this time offset occur in?
+ for(link=links-1; link>=0; link--){
+ pcm_total-=pcmlengths[link];
+ time_total-=time_total(link);
+ if(seconds>=time_total)
+ break;
+ }
+
+ // enough information to convert time offset to pcm offset
+ {
+ long target=(long)(pcm_total+(seconds-time_total)*vi[link].rate);
+ return (pcm_seek(target));
+ }
+
+ //seek_error:
+ // dump machine so we're in a known state
+ //pcm_offset=-1;
+ //decode_clear();
+ //return -1;
+ }
+
+ // tell the current stream offset cursor. Note that seek followed by
+ // tell will likely not give the set offset due to caching
+ public long raw_tell(){
+ return (offset);
+ }
+
+ // return PCM offset (sample) of next PCM sample to be read
+ public long pcm_tell(){
+ return (pcm_offset);
+ }
+
+ // return time offset (seconds) of next PCM sample to be read
+ public float time_tell(){
+ // translate time to PCM position and call pcm_seek
+
+ int link=-1;
+ long pcm_total=0;
+ float time_total=0.f;
+
+ if(seekable){
+ pcm_total=pcm_total(-1);
+ time_total=time_total(-1);
+
+ // which bitstream section does this time offset occur in?
+ for(link=links-1; link>=0; link--){
+ pcm_total-=pcmlengths[link];
+ time_total-=time_total(link);
+ if(pcm_offset>=pcm_total)
+ break;
+ }
+ }
+
+ return ((float)time_total+(float)(pcm_offset-pcm_total)/vi[link].rate);
+ }
+
+ // link: -1) return the vorbis_info struct for the bitstream section
+ // currently being decoded
+ // 0-n) to request information for a specific bitstream section
+ //
+ // In the case of a non-seekable bitstream, any call returns the
+ // current bitstream. NULL in the case that the machine is not
+ // initialized
+
+ public Info getInfo(int link){
+ if(seekable){
+ if(link<0){
+ if(decode_ready){
+ return vi[current_link];
+ }
+ else{
+ return null;
+ }
+ }
+ else{
+ if(link>=links){
+ return null;
+ }
+ else{
+ return vi[link];
+ }
+ }
+ }
+ else{
+ if(decode_ready){
+ return vi[0];
+ }
+ else{
+ return null;
+ }
+ }
+ }
+
+ public Comment getComment(int link){
+ if(seekable){
+ if(link<0){
+ if(decode_ready){
+ return vc[current_link];
+ }
+ else{
+ return null;
+ }
+ }
+ else{
+ if(link>=links){
+ return null;
+ }
+ else{
+ return vc[link];
+ }
+ }
+ }
+ else{
+ if(decode_ready){
+ return vc[0];
+ }
+ else{
+ return null;
+ }
+ }
+ }
+
+ int host_is_big_endian(){
+ return 1;
+ // short pattern = 0xbabe;
+ // unsigned char *bytewise = (unsigned char *)&pattern;
+ // if (bytewise[0] == 0xba) return 1;
+ // assert(bytewise[0] == 0xbe);
+ // return 0;
+ }
+
+ // up to this point, everything could more or less hide the multiple
+ // logical bitstream nature of chaining from the toplevel application
+ // if the toplevel application didn't particularly care. However, at
+ // the point that we actually read audio back, the multiple-section
+ // nature must surface: Multiple bitstream sections do not necessarily
+ // have to have the same number of channels or sampling rate.
+ //
+ // read returns the sequential logical bitstream number currently
+ // being decoded along with the PCM data in order that the toplevel
+ // application can take action on channel/sample rate changes. This
+ // number will be incremented even for streamed (non-seekable) streams
+ // (for seekable streams, it represents the actual logical bitstream
+ // index within the physical bitstream. Note that the accessor
+ // functions above are aware of this dichotomy).
+ //
+ // input values: buffer) a buffer to hold packed PCM data for return
+ // length) the byte length requested to be placed into buffer
+ // bigendianp) should the data be packed LSB first (0) or
+ // MSB first (1)
+ // word) word size for output. currently 1 (byte) or
+ // 2 (16 bit short)
+ //
+ // return values: -1) error/hole in data
+ // 0) EOF
+ // n) number of bytes of PCM actually returned. The
+ // below works on a packet-by-packet basis, so the
+ // return length is not related to the 'length' passed
+ // in, just guaranteed to fit.
+ //
+ // *section) set to the logical bitstream number
+
+ int read(byte[] buffer, int length, int bigendianp, int word, int sgned,
+ int[] bitstream){
+ int host_endian=host_is_big_endian();
+ int index=0;
+
+ while(true){
+ if(decode_ready){
+ float[][] pcm;
+ float[][][] _pcm=new float[1][][];
+ int[] _index=new int[getInfo(-1).channels];
+ int samples=vd.synthesis_pcmout(_pcm, _index);
+ pcm=_pcm[0];
+ if(samples!=0){
+ // yay! proceed to pack data into the byte buffer
+ int channels=getInfo(-1).channels;
+ int bytespersample=word*channels;
+ if(samples>length/bytespersample)
+ samples=length/bytespersample;
+
+ // a tight loop to pack each size
+ {
+ int val;
+ if(word==1){
+ int off=(sgned!=0 ? 0 : 128);
+ for(int j=0; j<samples; j++){
+ for(int i=0; i<channels; i++){
+ val=(int)(pcm[i][_index[i]+j]*128.+0.5);
+ if(val>127)
+ val=127;
+ else if(val<-128)
+ val=-128;
+ buffer[index++]=(byte)(val+off);
+ }
+ }
+ }
+ else{
+ int off=(sgned!=0 ? 0 : 32768);
+
+ if(host_endian==bigendianp){
+ if(sgned!=0){
+ for(int i=0; i<channels; i++){ // It's faster in this order
+ int src=_index[i];
+ int dest=i;
+ for(int j=0; j<samples; j++){
+ val=(int)(pcm[i][src+j]*32768.+0.5);
+ if(val>32767)
+ val=32767;
+ else if(val<-32768)
+ val=-32768;
+ buffer[dest]=(byte)(val>>>8);
+ buffer[dest+1]=(byte)(val);
+ dest+=channels*2;
+ }
+ }
+ }
+ else{
+ for(int i=0; i<channels; i++){
+ float[] src=pcm[i];
+ int dest=i;
+ for(int j=0; j<samples; j++){
+ val=(int)(src[j]*32768.+0.5);
+ if(val>32767)
+ val=32767;
+ else if(val<-32768)
+ val=-32768;
+ buffer[dest]=(byte)((val+off)>>>8);
+ buffer[dest+1]=(byte)(val+off);
+ dest+=channels*2;
+ }
+ }
+ }
+ }
+ else if(bigendianp!=0){
+ for(int j=0; j<samples; j++){
+ for(int i=0; i<channels; i++){
+ val=(int)(pcm[i][j]*32768.+0.5);
+ if(val>32767)
+ val=32767;
+ else if(val<-32768)
+ val=-32768;
+ val+=off;
+ buffer[index++]=(byte)(val>>>8);
+ buffer[index++]=(byte)val;
+ }
+ }
+ }
+ else{
+ //int val;
+ for(int j=0; j<samples; j++){
+ for(int i=0; i<channels; i++){
+ val=(int)(pcm[i][j]*32768.+0.5);
+ if(val>32767)
+ val=32767;
+ else if(val<-32768)
+ val=-32768;
+ val+=off;
+ buffer[index++]=(byte)val;
+ buffer[index++]=(byte)(val>>>8);
+ }
+ }
+ }
+ }
+ }
+
+ vd.synthesis_read(samples);
+ pcm_offset+=samples;
+ if(bitstream!=null)
+ bitstream[0]=current_link;
+ return (samples*bytespersample);
+ }
+ }
+
+ // suck in another packet
+ switch(process_packet(1)){
+ case 0:
+ return (0);
+ case -1:
+ return -1;
+ default:
+ break;
+ }
+ }
+ }
+
+ public Info[] getInfo(){
+ return vi;
+ }
+
+ public Comment[] getComment(){
+ return vc;
+ }
+
+ public void close() throws java.io.IOException{
+ datasource.close();
+ }
+
+ class SeekableInputStream extends InputStream{
+ java.io.RandomAccessFile raf=null;
+ final String mode="r";
+
+ SeekableInputStream(String file) throws java.io.IOException{
+ raf=new java.io.RandomAccessFile(file, mode);
+ }
+
+ public int read() throws java.io.IOException{
+ return raf.read();
+ }
+
+ public int read(byte[] buf) throws java.io.IOException{
+ return raf.read(buf);
+ }
+
+ public int read(byte[] buf, int s, int len) throws java.io.IOException{
+ return raf.read(buf, s, len);
+ }
+
+ public long skip(long n) throws java.io.IOException{
+ return (long)(raf.skipBytes((int)n));
+ }
+
+ public long getLength() throws java.io.IOException{
+ return raf.length();
+ }
+
+ public long tell() throws java.io.IOException{
+ return raf.getFilePointer();
+ }
+
+ public int available() throws java.io.IOException{
+ return (raf.length()==raf.getFilePointer()) ? 0 : 1;
+ }
+
+ public void close() throws java.io.IOException{
+ raf.close();
+ }
+
+ public synchronized void mark(int m){
+ }
+
+ public synchronized void reset() throws java.io.IOException{
+ }
+
+ public boolean markSupported(){
+ return false;
+ }
+
+ public void seek(long pos) throws java.io.IOException{
+ raf.seek(pos);
+ }
+ }
+
+}
diff --git a/settings.gradle b/settings.gradle
index 4b20feb..afa7147 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -18,6 +18,7 @@ include ':ardor3d-examples'
include ':ardor3d-performance'
include ':distribution'
include ':ardor3d-craft'
+include ':ardor3d-audio'
project(':ardor3d-savable').projectDir = "$rootDir/ardor3d-savable" as File
project(':ardor3d-math').projectDir = "$rootDir/ardor3d-math" as File
@@ -37,4 +38,5 @@ project(':ardor3d-terrain').projectDir = "$rootDir/ardor3d-terrain" as File
project(':ardor3d-examples').projectDir = "$rootDir/ardor3d-examples" as File
project(':ardor3d-performance').projectDir = "$rootDir/ardor3d-performance" as File
project(':distribution').projectDir = "$rootDir/ardor3d-distribution" as File
-project(':ardor3d-craft').projectDir = "$rootDir/ardor3d-craft" as File \ No newline at end of file
+project(':ardor3d-craft').projectDir = "$rootDir/ardor3d-craft" as File
+project(':ardor3d-audio').projectDir = "$rootDir/ardor3d-audio" as File