478 lines
16 KiB
Java
478 lines
16 KiB
Java
![]() |
package game.audio;
|
||
|
|
||
|
import java.io.IOException;
|
||
|
import java.io.InputStream;
|
||
|
import game.audio.jogg.Packet;
|
||
|
import game.audio.jogg.Page;
|
||
|
import game.audio.jogg.StreamState;
|
||
|
import game.audio.jogg.SyncState;
|
||
|
import game.audio.jorbis.Block;
|
||
|
import game.audio.jorbis.Comment;
|
||
|
import game.audio.jorbis.DspState;
|
||
|
import game.audio.jorbis.Info;
|
||
|
|
||
|
public class CodecJOrbis
|
||
|
{
|
||
|
private boolean endOfStream = false;
|
||
|
private byte[] buffer = null;
|
||
|
private int bufferSize;
|
||
|
private int count = 0;
|
||
|
private int index = 0;
|
||
|
private short[] convertedBuffer = null;
|
||
|
private float[][][] pcmInfo;
|
||
|
private int[] pcmIndex;
|
||
|
private Packet joggPacket = new Packet();
|
||
|
private Page joggPage = new Page();
|
||
|
private StreamState joggStreamState = new StreamState();
|
||
|
private SyncState joggSyncState = new SyncState();
|
||
|
private DspState jorbisDspState = new DspState();
|
||
|
private Block jorbisBlock = new Block(jorbisDspState);
|
||
|
private Comment jorbisComment = new Comment();
|
||
|
private Info jorbisInfo = new Info();
|
||
|
|
||
|
private CodecJOrbis() {
|
||
|
}
|
||
|
|
||
|
private boolean initialize( InputStream in ) throws IOException
|
||
|
{
|
||
|
// 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();
|
||
|
|
||
|
// 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();
|
||
|
|
||
|
// URLConnection urlConnection = null;
|
||
|
// 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 = in; // 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(in) )
|
||
|
{
|
||
|
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 );
|
||
|
|
||
|
if(jorbisInfo.channels != 1) {
|
||
|
errorMessage("Stream muss genau einen Kanal besitzen");
|
||
|
return false;
|
||
|
}
|
||
|
if(jorbisInfo.rate != 48000) {
|
||
|
errorMessage("Stream muss eine Samplerate von 48000 Hz besitzen");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// audioFormat = ;
|
||
|
pcmInfo = new float[1][][];
|
||
|
pcmIndex = new int[ 1 ];
|
||
|
|
||
|
// initialized( SET, true );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public static short[] readAll(InputStream in) throws IOException
|
||
|
{
|
||
|
CodecJOrbis codec = new CodecJOrbis();
|
||
|
if(!codec.initialize(in))
|
||
|
return null;
|
||
|
short[] returnBuffer = null;
|
||
|
|
||
|
while( !codec.endOfStream )
|
||
|
{
|
||
|
if( returnBuffer == null )
|
||
|
returnBuffer = codec.readBytes(in);
|
||
|
else
|
||
|
returnBuffer = appendByteArrays( returnBuffer, codec.readBytes(in) );
|
||
|
}
|
||
|
|
||
|
// if( returnBuffer == null )
|
||
|
// return null;
|
||
|
// this.cleanup();
|
||
|
return returnBuffer; // new SoundBuffer( returnBuffer, audioFormat );
|
||
|
}
|
||
|
|
||
|
private boolean readHeader(InputStream inputStream) 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;
|
||
|
}
|
||
|
|
||
|
private short[] readBytes(InputStream inputStream) throws IOException
|
||
|
{
|
||
|
// if( !initialized( GET, XXX ) )
|
||
|
// return null;
|
||
|
|
||
|
if( endOfStream )
|
||
|
return null;
|
||
|
|
||
|
if( convertedBuffer == null )
|
||
|
convertedBuffer = new short[ bufferSize ];
|
||
|
short[] 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 = 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 < bufferSize ?
|
||
|
samples : bufferSize );
|
||
|
// for( i = 0; i < jorbisInfo.channels; i++ )
|
||
|
// {
|
||
|
// ptr = 0; // i * 2;
|
||
|
mono = pcmIndex[0];
|
||
|
for( j = 0; j < bout; j++ )
|
||
|
{
|
||
|
val = (int) ( pcmf[0][mono + j] *
|
||
|
32767. );
|
||
|
if( val > 32767 )
|
||
|
val = 32767;
|
||
|
if( val < -32768 )
|
||
|
val = -32768;
|
||
|
if( val < 0 )
|
||
|
val = val | 0x8000;
|
||
|
convertedBuffer[j] = (short)val;
|
||
|
// convertedBuffer[ptr] = (byte) (val);
|
||
|
// convertedBuffer[ptr+1] =
|
||
|
// (byte) (val>>>8);
|
||
|
// ptr += 2 * (jorbisInfo.channels);
|
||
|
}
|
||
|
// }
|
||
|
jorbisDspState.synthesis_read( bout );
|
||
|
|
||
|
returnBuffer = appendByteArrays( returnBuffer,
|
||
|
convertedBuffer,
|
||
|
bout );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( joggPage.eos() != 0 )
|
||
|
endOfStream = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( !endOfStream )
|
||
|
{
|
||
|
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 = true;
|
||
|
}
|
||
|
|
||
|
return returnBuffer;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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 short[] appendByteArrays( short[] arrayOne, short[] arrayTwo,
|
||
|
int arrayTwoBytes )
|
||
|
{
|
||
|
short[] 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 short[ 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 short[ 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 short[ 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 short[] appendByteArrays( short[] arrayOne, short[] arrayTwo )
|
||
|
{
|
||
|
short[] 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 short[ 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 short[ 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 short[ 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;
|
||
|
}
|
||
|
|
||
|
private void errorMessage( String message ) throws IOException
|
||
|
{
|
||
|
throw new IOException(message);
|
||
|
}
|
||
|
}
|