2012-08-05 10:23:38 +00:00
|
|
|
package paulscode.sound.codecs;
|
|
|
|
|
|
|
|
import java.io.DataInputStream;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.net.URL;
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.ByteOrder;
|
|
|
|
import java.nio.ShortBuffer;
|
2014-01-18 05:55:48 +00:00
|
|
|
|
2012-08-05 10:23:38 +00:00
|
|
|
import javax.sound.sampled.AudioFormat;
|
|
|
|
|
|
|
|
import paulscode.sound.ICodec;
|
|
|
|
import paulscode.sound.SoundBuffer;
|
|
|
|
import paulscode.sound.SoundSystemConfig;
|
|
|
|
import paulscode.sound.SoundSystemLogger;
|
|
|
|
import ibxm.FastTracker2;
|
|
|
|
import ibxm.IBXM;
|
|
|
|
import ibxm.Module;
|
|
|
|
import ibxm.ProTracker;
|
|
|
|
import ibxm.ScreamTracker3;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The CodecIBXM class provides an ICodec interface for reading from MOD/S3M/XM
|
|
|
|
* files via the IBXM library.
|
|
|
|
*<b><i> SoundSystem CodecIBXM 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 IBXM library available from
|
|
|
|
* http://www.geocities.com/sunet2000/
|
|
|
|
*</b><br><br>
|
|
|
|
*<br><b>
|
|
|
|
* IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD
|
|
|
|
* License.
|
|
|
|
*<br><br>
|
|
|
|
* All rights reserved.
|
|
|
|
*<br><br>
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*<br><br>
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer. Redistributions in binary
|
|
|
|
* form must reproduce the above copyright notice, this list of conditions and
|
|
|
|
* the following disclaimer in the documentation and/or other materials
|
|
|
|
* provided with the distribution. Neither the name of mumart nor the names of
|
|
|
|
* its contributors may be used to endorse or promote products derived from
|
|
|
|
* this software without specific prior written permission.
|
|
|
|
* <br><br>
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
* <br><br><br></b>
|
|
|
|
*/
|
|
|
|
public class CodecIBXM 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;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Format the converted audio will be in.
|
|
|
|
*/
|
|
|
|
private AudioFormat myAudioFormat = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if the using library requires data read by this codec to be
|
|
|
|
* reverse-ordered before returning it from methods read() and readAll().
|
|
|
|
*/
|
|
|
|
private boolean reverseBytes = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* IBXM decoder.
|
|
|
|
*/
|
|
|
|
private IBXM ibxm;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Module instance to be played.
|
|
|
|
*/
|
|
|
|
private Module module;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Duration of the audio (in frames).
|
|
|
|
*/
|
|
|
|
private int songDuration;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Audio read position (in frames).
|
|
|
|
*/
|
|
|
|
private int playPosition;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Processes status messages, warnings, and error messages.
|
|
|
|
*/
|
|
|
|
private SoundSystemLogger logger;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor: Grabs a handle to the logger.
|
|
|
|
*/
|
|
|
|
public CodecIBXM()
|
|
|
|
{
|
|
|
|
logger = SoundSystemConfig.getLogger();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tells this codec when it will need to reverse the byte order of
|
|
|
|
* the data before returning it in the read() and readAll() methods. The
|
|
|
|
* IBXM library produces 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
|
|
|
|
* will call the reverseByteOrder() method.
|
|
|
|
* @param b True if the calling audio library requires byte-reversal.
|
|
|
|
*/
|
2014-01-18 05:55:48 +00:00
|
|
|
@Override
|
2012-08-05 10:23:38 +00:00
|
|
|
public void reverseByteOrder( boolean b )
|
|
|
|
{
|
|
|
|
reverseBytes = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepares an audio stream to read from. If another stream is already opened,
|
|
|
|
* it will be closed and a new audio stream opened in its place.
|
|
|
|
* @param url URL to an audio file to stream from.
|
|
|
|
* @return False if an error occurred or if end of stream was reached.
|
|
|
|
*/
|
2014-01-18 05:55:48 +00:00
|
|
|
@Override
|
2012-08-05 10:23:38 +00:00
|
|
|
public boolean initialize( URL url )
|
|
|
|
{
|
|
|
|
initialized( SET, false );
|
|
|
|
cleanup();
|
|
|
|
|
|
|
|
if( url == null )
|
|
|
|
{
|
|
|
|
errorMessage( "url null in method 'initialize'" );
|
|
|
|
cleanup();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
InputStream is = null;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
is = url.openStream();
|
|
|
|
}
|
|
|
|
catch( IOException ioe )
|
|
|
|
{
|
|
|
|
errorMessage( "Unable to open stream in method 'initialize'" );
|
|
|
|
printStackTrace( ioe );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ibxm == null )
|
|
|
|
ibxm = new IBXM( 48000 );
|
|
|
|
if( myAudioFormat == null )
|
|
|
|
myAudioFormat = new AudioFormat( 48000, 16, 2, true, true );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
setModule( loadModule( is ) );
|
|
|
|
}
|
|
|
|
catch( IllegalArgumentException iae )
|
|
|
|
{
|
|
|
|
errorMessage( "Illegal argument in method 'initialize'" );
|
|
|
|
printStackTrace( iae );
|
|
|
|
if( is != null )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
is.close();
|
|
|
|
}
|
|
|
|
catch( IOException ioe )
|
|
|
|
{}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
catch( IOException ioe )
|
|
|
|
{
|
|
|
|
errorMessage( "Error loading module in method 'initialize'" );
|
|
|
|
printStackTrace( ioe );
|
|
|
|
if( is != null )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
is.close();
|
|
|
|
}
|
|
|
|
catch( IOException ioe2 )
|
|
|
|
{}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( is != null )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
is.close();
|
|
|
|
}
|
|
|
|
catch( IOException ioe )
|
|
|
|
{}
|
|
|
|
}
|
|
|
|
|
|
|
|
endOfStream( SET, false );
|
|
|
|
initialized( SET, true );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns false if the stream is busy initializing.
|
|
|
|
* @return True if steam is initialized.
|
|
|
|
*/
|
2014-01-18 05:55:48 +00:00
|
|
|
@Override
|
2012-08-05 10:23:38 +00:00
|
|
|
public boolean initialized()
|
|
|
|
{
|
|
|
|
return initialized( GET, XXX );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads in one stream buffer worth of audio data. See
|
|
|
|
* {@link paulscode.sound.SoundSystemConfig SoundSystemConfig} for more
|
|
|
|
* information about accessing and changing default settings.
|
|
|
|
* @return The audio data wrapped into a SoundBuffer context.
|
|
|
|
*/
|
2014-01-18 05:55:48 +00:00
|
|
|
@Override
|
2012-08-05 10:23:38 +00:00
|
|
|
public SoundBuffer read()
|
|
|
|
{
|
|
|
|
if( endOfStream( GET, XXX ) )
|
|
|
|
return null;
|
|
|
|
|
|
|
|
if( module == null )
|
|
|
|
{
|
|
|
|
errorMessage( "Module null in method 'read'" );
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check to make sure there is an audio format:
|
|
|
|
if( myAudioFormat == null )
|
|
|
|
{
|
|
|
|
errorMessage( "Audio Format null in method 'read'" );
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bufferFrameSize = (int) SoundSystemConfig.getStreamingBufferSize()
|
|
|
|
/ 4;
|
|
|
|
|
|
|
|
int frames = songDuration - playPosition;
|
|
|
|
if( frames > bufferFrameSize )
|
|
|
|
frames = bufferFrameSize;
|
|
|
|
|
|
|
|
if( frames <= 0 )
|
|
|
|
{
|
|
|
|
endOfStream( SET, true );
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
byte[] outputBuffer = new byte[ frames * 4 ];
|
|
|
|
|
|
|
|
ibxm.get_audio( outputBuffer, frames );
|
|
|
|
|
|
|
|
playPosition += frames;
|
|
|
|
if( playPosition >= songDuration )
|
|
|
|
{
|
|
|
|
endOfStream( SET, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reverse the byte order if necessary:
|
|
|
|
if( reverseBytes )
|
|
|
|
reverseBytes( outputBuffer, 0, frames * 4 );
|
|
|
|
|
|
|
|
// Wrap the data into a SoundBuffer:
|
|
|
|
SoundBuffer buffer = new SoundBuffer( outputBuffer, myAudioFormat );
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads in all the audio data from the stream (up to the default
|
|
|
|
* "maximum file size". See
|
|
|
|
* {@link paulscode.sound.SoundSystemConfig SoundSystemConfig} for more
|
|
|
|
* information about accessing and changing default settings.
|
|
|
|
* @return the audio data wrapped into a SoundBuffer context.
|
|
|
|
*/
|
2014-01-18 05:55:48 +00:00
|
|
|
@Override
|
2012-08-05 10:23:38 +00:00
|
|
|
public SoundBuffer readAll()
|
|
|
|
{
|
|
|
|
if( module == null )
|
|
|
|
{
|
|
|
|
errorMessage( "Module null in method 'readAll'" );
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check to make sure there is an audio format:
|
|
|
|
if( myAudioFormat == null )
|
|
|
|
{
|
|
|
|
errorMessage( "Audio Format null in method 'readAll'" );
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bufferFrameSize = (int) SoundSystemConfig.getFileChunkSize()
|
|
|
|
/ 4;
|
|
|
|
|
|
|
|
byte[] outputBuffer = new byte[ bufferFrameSize * 4 ];
|
|
|
|
|
|
|
|
// Buffer to contain the audio data:
|
|
|
|
byte[] fullBuffer = null;
|
|
|
|
// frames of audio data:
|
|
|
|
int frames;
|
|
|
|
// bytes of audio data:
|
|
|
|
int totalBytes = 0;
|
|
|
|
|
|
|
|
while( (!endOfStream(GET, XXX)) &&
|
|
|
|
(totalBytes < SoundSystemConfig.getMaxFileSize()) )
|
|
|
|
{
|
|
|
|
frames = songDuration - playPosition;
|
|
|
|
if( frames > bufferFrameSize )
|
|
|
|
frames = bufferFrameSize;
|
|
|
|
ibxm.get_audio( outputBuffer, frames );
|
|
|
|
totalBytes += (frames * 4);
|
|
|
|
|
|
|
|
fullBuffer = appendByteArrays( fullBuffer, outputBuffer,
|
|
|
|
frames * 4 );
|
|
|
|
|
|
|
|
playPosition += frames;
|
|
|
|
if( playPosition >= songDuration )
|
|
|
|
{
|
|
|
|
endOfStream( SET, true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reverse the byte order if necessary:
|
|
|
|
if( reverseBytes )
|
|
|
|
reverseBytes( fullBuffer, 0, totalBytes );
|
|
|
|
|
|
|
|
// Wrap the data into a SoundBuffer:
|
|
|
|
SoundBuffer buffer = new SoundBuffer( fullBuffer, myAudioFormat );
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns false if there is still more data available to be read in.
|
|
|
|
* @return True if end of stream was reached.
|
|
|
|
*/
|
2014-01-18 05:55:48 +00:00
|
|
|
@Override
|
2012-08-05 10:23:38 +00:00
|
|
|
public boolean endOfStream()
|
|
|
|
{
|
|
|
|
return endOfStream( GET, XXX );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closes the audio stream and remove references to all instantiated objects.
|
|
|
|
*/
|
2014-01-18 05:55:48 +00:00
|
|
|
@Override
|
2012-08-05 10:23:38 +00:00
|
|
|
public void cleanup()
|
|
|
|
{
|
|
|
|
// if( ibxm != null )
|
|
|
|
// ibxm.seek( 0 );
|
|
|
|
playPosition = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the audio format of the data being returned by the read() and
|
|
|
|
* readAll() methods.
|
|
|
|
* @return Information wrapped into an AudioFormat context.
|
|
|
|
*/
|
2014-01-18 05:55:48 +00:00
|
|
|
@Override
|
2012-08-05 10:23:38 +00:00
|
|
|
public AudioFormat getAudioFormat()
|
|
|
|
{
|
|
|
|
return myAudioFormat;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decodes the data in the specified InputStream into an instance of
|
|
|
|
* ibxm.Module.
|
|
|
|
* @param input an InputStream containing the module file to be decoded.
|
|
|
|
* @throws IllegalArgumentException if the data is not recognised as a module file.
|
|
|
|
*/
|
|
|
|
private static Module loadModule( InputStream input )
|
|
|
|
throws IllegalArgumentException, IOException
|
|
|
|
{
|
|
|
|
DataInputStream data_input_stream = new DataInputStream( input );
|
|
|
|
|
|
|
|
// Check if data is in XM format:
|
|
|
|
byte[] xm_header = new byte[ 60 ];
|
|
|
|
data_input_stream.readFully( xm_header );
|
|
|
|
if( FastTracker2.is_xm( xm_header ) )
|
|
|
|
return FastTracker2.load_xm( xm_header, data_input_stream );
|
|
|
|
|
|
|
|
// Check if data is in ScreamTracker 3 format:
|
|
|
|
byte[] s3m_header = new byte[ 96 ];
|
|
|
|
System.arraycopy( xm_header, 0, s3m_header, 0, 60 );
|
|
|
|
data_input_stream.readFully( s3m_header, 60, 36 );
|
|
|
|
if( ScreamTracker3.is_s3m( s3m_header ) )
|
|
|
|
return ScreamTracker3.load_s3m( s3m_header, data_input_stream );
|
|
|
|
|
|
|
|
// Check if data is in ProTracker format:
|
|
|
|
byte[] mod_header = new byte[ 1084 ];
|
|
|
|
System.arraycopy( s3m_header, 0, mod_header, 0, 96 );
|
|
|
|
data_input_stream.readFully( mod_header, 96, 988 );
|
|
|
|
return ProTracker.load_mod( mod_header, data_input_stream );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the Module instance to be played.
|
|
|
|
*/
|
|
|
|
private void setModule( Module m )
|
|
|
|
{
|
|
|
|
if( m != null )
|
|
|
|
module = m;
|
|
|
|
ibxm.set_module( module );
|
|
|
|
songDuration = ibxm.calculate_song_duration();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reverse-orders all bytes contained in the specified array.
|
|
|
|
* @param buffer Array containing audio data.
|
|
|
|
*/
|
|
|
|
public static void reverseBytes( byte[] buffer )
|
|
|
|
{
|
|
|
|
reverseBytes( buffer, 0, buffer.length );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reverse-orders the specified range of bytes contained in the specified array.
|
|
|
|
* @param buffer Array containing audio data.
|
|
|
|
* @param offset Array index to begin.
|
|
|
|
* @param size number of bytes to reverse-order.
|
|
|
|
*/
|
|
|
|
public static void reverseBytes( byte[] buffer, int offset, int size )
|
|
|
|
{
|
|
|
|
|
|
|
|
byte b;
|
|
|
|
for( int i = offset; i < ( offset + size ); i += 2 )
|
|
|
|
{
|
|
|
|
b = buffer[i];
|
|
|
|
buffer[i] = buffer[i + 1];
|
|
|
|
buffer[i + 1] = b;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts sound bytes to little-endian format.
|
|
|
|
* @param audio_bytes The original wave data
|
|
|
|
* @param two_bytes_data For stereo sounds.
|
|
|
|
* @return byte array containing the converted data.
|
|
|
|
*/
|
|
|
|
private static byte[] convertAudioBytes( byte[] audio_bytes,
|
|
|
|
boolean two_bytes_data )
|
|
|
|
{
|
|
|
|
ByteBuffer dest = ByteBuffer.allocateDirect( audio_bytes.length );
|
|
|
|
dest.order( ByteOrder.nativeOrder() );
|
|
|
|
ByteBuffer src = ByteBuffer.wrap( audio_bytes );
|
|
|
|
src.order( ByteOrder.LITTLE_ENDIAN );
|
|
|
|
if( two_bytes_data )
|
|
|
|
{
|
|
|
|
ShortBuffer dest_short = dest.asShortBuffer();
|
|
|
|
ShortBuffer src_short = src.asShortBuffer();
|
|
|
|
while( src_short.hasRemaining() )
|
|
|
|
{
|
|
|
|
dest_short.put(src_short.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while( src.hasRemaining() )
|
|
|
|
{
|
|
|
|
dest.put( src.get() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dest.rewind();
|
|
|
|
|
|
|
|
if( !dest.hasArray() )
|
|
|
|
{
|
|
|
|
byte[] arrayBackedBuffer = new byte[dest.capacity()];
|
|
|
|
dest.get( arrayBackedBuffer );
|
|
|
|
dest.clear();
|
|
|
|
|
|
|
|
return arrayBackedBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dest.array();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 length How many bytes to append from the second array.
|
|
|
|
* @return Byte array containing information from both arrays.
|
|
|
|
*/
|
|
|
|
private static byte[] appendByteArrays( byte[] arrayOne, byte[] arrayTwo,
|
|
|
|
int length )
|
|
|
|
{
|
|
|
|
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[ length ];
|
|
|
|
// fill the new array with the contents of arrayTwo:
|
|
|
|
System.arraycopy( arrayTwo, 0, newArray, 0, 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 + 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,
|
|
|
|
length );
|
|
|
|
arrayOne = null;
|
|
|
|
arrayTwo = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return newArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prints an error message.
|
|
|
|
* @param message Message to print.
|
|
|
|
*/
|
|
|
|
private void errorMessage( String message )
|
|
|
|
{
|
|
|
|
logger.errorMessage( "CodecWav", 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 );
|
|
|
|
}
|
|
|
|
}
|