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); } }