tcr/java/src/client/audio/CodecJOrbis.java
2025-05-04 20:27:55 +02:00

478 lines
16 KiB
Java

package client.audio;
import java.io.IOException;
import java.io.InputStream;
import client.audio.jogg.Packet;
import client.audio.jogg.Page;
import client.audio.jogg.StreamState;
import client.audio.jogg.SyncState;
import client.audio.jorbis.Block;
import client.audio.jorbis.Comment;
import client.audio.jorbis.DspState;
import client.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);
}
}