From a6f20d815e5386c9a38b3218d10d8f7e1ee6d822 Mon Sep 17 00:00:00 2001 From: Sen Date: Tue, 25 Mar 2025 12:41:18 +0100 Subject: [PATCH] remove OPUS, re-add ogg/jorbis --- java/src/game/audio/CodecJOrbis.java | 477 ++++++ java/src/game/audio/jogg/Buffer.java | 294 ++++ java/src/game/audio/jogg/Packet.java | 47 + java/src/game/audio/jogg/Page.java | 135 ++ java/src/game/audio/jogg/StreamState.java | 526 +++++++ java/src/game/audio/jogg/SyncState.java | 275 ++++ java/src/game/audio/jorbis/Block.java | 128 ++ .../game/audio/jorbis/ChainingExample.java | 69 + java/src/game/audio/jorbis/CodeBook.java | 478 ++++++ java/src/game/audio/jorbis/Comment.java | 243 +++ java/src/game/audio/jorbis/DecodeExample.java | 324 ++++ java/src/game/audio/jorbis/Drft.java | 1327 ++++++++++++++++ java/src/game/audio/jorbis/DspState.java | 376 +++++ java/src/game/audio/jorbis/Floor0.java | 335 ++++ java/src/game/audio/jorbis/Floor1.java | 611 +++++++ java/src/game/audio/jorbis/FuncFloor.java | 52 + java/src/game/audio/jorbis/FuncMapping.java | 45 + java/src/game/audio/jorbis/FuncResidue.java | 46 + java/src/game/audio/jorbis/FuncTime.java | 45 + java/src/game/audio/jorbis/Info.java | 469 ++++++ java/src/game/audio/jorbis/InfoMode.java | 34 + .../game/audio/jorbis/JOrbisException.java | 40 + java/src/game/audio/jorbis/Lookup.java | 152 ++ java/src/game/audio/jorbis/Lpc.java | 188 +++ java/src/game/audio/jorbis/Lsp.java | 107 ++ java/src/game/audio/jorbis/Mapping0.java | 375 +++++ java/src/game/audio/jorbis/Mdct.java | 250 +++ java/src/game/audio/jorbis/PsyInfo.java | 74 + java/src/game/audio/jorbis/PsyLook.java | 42 + java/src/game/audio/jorbis/Residue0.java | 330 ++++ java/src/game/audio/jorbis/Residue1.java | 45 + java/src/game/audio/jorbis/Residue2.java | 41 + .../src/game/audio/jorbis/StaticCodeBook.java | 443 ++++++ java/src/game/audio/jorbis/Time0.java | 52 + java/src/game/audio/jorbis/Util.java | 30 + java/src/game/audio/jorbis/VorbisFile.java | 1398 +++++++++++++++++ java/src/game/gui/GuiInfo.java | 3 +- java/src/game/init/SoundEvent.java | 50 +- 38 files changed, 9910 insertions(+), 46 deletions(-) create mode 100644 java/src/game/audio/CodecJOrbis.java create mode 100644 java/src/game/audio/jogg/Buffer.java create mode 100644 java/src/game/audio/jogg/Packet.java create mode 100644 java/src/game/audio/jogg/Page.java create mode 100644 java/src/game/audio/jogg/StreamState.java create mode 100644 java/src/game/audio/jogg/SyncState.java create mode 100644 java/src/game/audio/jorbis/Block.java create mode 100644 java/src/game/audio/jorbis/ChainingExample.java create mode 100644 java/src/game/audio/jorbis/CodeBook.java create mode 100644 java/src/game/audio/jorbis/Comment.java create mode 100644 java/src/game/audio/jorbis/DecodeExample.java create mode 100644 java/src/game/audio/jorbis/Drft.java create mode 100644 java/src/game/audio/jorbis/DspState.java create mode 100644 java/src/game/audio/jorbis/Floor0.java create mode 100644 java/src/game/audio/jorbis/Floor1.java create mode 100644 java/src/game/audio/jorbis/FuncFloor.java create mode 100644 java/src/game/audio/jorbis/FuncMapping.java create mode 100644 java/src/game/audio/jorbis/FuncResidue.java create mode 100644 java/src/game/audio/jorbis/FuncTime.java create mode 100644 java/src/game/audio/jorbis/Info.java create mode 100644 java/src/game/audio/jorbis/InfoMode.java create mode 100644 java/src/game/audio/jorbis/JOrbisException.java create mode 100644 java/src/game/audio/jorbis/Lookup.java create mode 100644 java/src/game/audio/jorbis/Lpc.java create mode 100644 java/src/game/audio/jorbis/Lsp.java create mode 100644 java/src/game/audio/jorbis/Mapping0.java create mode 100644 java/src/game/audio/jorbis/Mdct.java create mode 100644 java/src/game/audio/jorbis/PsyInfo.java create mode 100644 java/src/game/audio/jorbis/PsyLook.java create mode 100644 java/src/game/audio/jorbis/Residue0.java create mode 100644 java/src/game/audio/jorbis/Residue1.java create mode 100644 java/src/game/audio/jorbis/Residue2.java create mode 100644 java/src/game/audio/jorbis/StaticCodeBook.java create mode 100644 java/src/game/audio/jorbis/Time0.java create mode 100644 java/src/game/audio/jorbis/Util.java create mode 100644 java/src/game/audio/jorbis/VorbisFile.java diff --git a/java/src/game/audio/CodecJOrbis.java b/java/src/game/audio/CodecJOrbis.java new file mode 100644 index 0000000..19a92cf --- /dev/null +++ b/java/src/game/audio/CodecJOrbis.java @@ -0,0 +1,477 @@ +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); + } +} diff --git a/java/src/game/audio/jogg/Buffer.java b/java/src/game/audio/jogg/Buffer.java new file mode 100644 index 0000000..761e609 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.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=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<=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/java/src/game/audio/jogg/Packet.java b/java/src/game/audio/jogg/Packet.java new file mode 100644 index 0000000..0ede86d --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.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/java/src/game/audio/jogg/Page.java b/java/src/game/audio/jogg/Page.java new file mode 100644 index 0000000..a05a403 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.jogg; + +public class Page{ + private static int[] crc_lookup=new int[256]; + static{ + for(int i=0; i>>24)&0xff)^(header_base[header+i]&0xff)]; + } + for(int i=0; i>>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/java/src/game/audio/jogg/StreamState.java b/java/src/game/audio/jogg/StreamState.java new file mode 100644 index 0000000..3339bb3 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.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; i0) + 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; i0) + 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; vals4096) + 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; i4096|| /* '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/java/src/game/audio/jogg/SyncState.java b/java/src/game/audio/jogg/SyncState.java new file mode 100644 index 0000000..58e3743 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.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; iibytes) + 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; ii0){ + // 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/java/src/game/audio/jorbis/Block.java b/java/src/game/audio/jorbis/Block.java new file mode 100644 index 0000000..a96d2e2 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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 + * + * Many thanks to + * Monty 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 game.audio.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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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; k8){ + for(i=0; i 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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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; jPCM 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=(samples32767){ + 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/java/src/game/audio/jorbis/Drft.java b/java/src/game/audio/jorbis/Drft.java new file mode 100644 index 0000000..052faea --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.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>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; ikl1){ + for(j=1; j>>1; + ipp2=ip; + ipph=(ip+1)>>>1; + if(idol1){ + state=139; + break; + } + + is=-ido-1; + t1=0; + for(j=1; j + * + * Many thanks to + * Monty 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 game.audio.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; ivi.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_returnedpcm_storage){ + // expand the storage + pcm_storage=endW+vi.blocksizes[1]; + for(int i=0; igranulepos. + // + // 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_returnedcenterW) + return (-1); + pcm_returned+=bytes; + return (0); + } + + public void clear(){ + } +} diff --git a/java/src/game/audio/jorbis/Floor0.java b/java/src/game/audio/jorbis/Floor0.java new file mode 100644 index 0000000..7a37aa7 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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=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.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<0){ // also handles the -1 out of data case + int maxval=(1<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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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=vi.books){ + info.free(); + return (null); + } + for(int k=0; k<(1<=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=(1<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; klx&&xcurrentx){ + 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>>=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=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=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< + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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/java/src/game/audio/jorbis/FuncMapping.java b/java/src/game/audio/jorbis/FuncMapping.java new file mode 100644 index 0000000..2d94876 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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/java/src/game/audio/jorbis/FuncResidue.java b/java/src/game/audio/jorbis/FuncResidue.java new file mode 100644 index 0000000..d0b7aaf --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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/java/src/game/audio/jorbis/FuncTime.java b/java/src/game/audio/jorbis/FuncTime.java new file mode 100644 index 0000000..f0ea437 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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/java/src/game/audio/jorbis/Info.java b/java/src/game/audio/jorbis/Info.java new file mode 100644 index 0000000..e37fac2 --- /dev/null +++ b/java/src/game/audio/jorbis/Info.java @@ -0,0 +1,469 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.jogg.*; + +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; ibook_param)free(vi->book_param); + book_param=null; + + for(int i=0; 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=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=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=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=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; i1){ + 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:"+version+", channels:"+channels + +", rate:"+rate+", bitrate:"+bitrate_upper + +","+bitrate_nominal+","+bitrate_lower; + } +} diff --git a/java/src/game/audio/jorbis/InfoMode.java b/java/src/game/audio/jorbis/InfoMode.java new file mode 100644 index 0000000..2125bc5 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +class InfoMode{ + int blockflag; + int windowtype; + int transformtype; + int mapping; +} diff --git a/java/src/game/audio/jorbis/JOrbisException.java b/java/src/game/audio/jorbis/JOrbisException.java new file mode 100644 index 0000000..a6a2433 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.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/java/src/game/audio/jorbis/Lookup.java b/java/src/game/audio/jorbis/Lookup.java new file mode 100644 index 0000000..b539615 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.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<=(FROMdB_LOOKUP_SZ<>>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]); + } + +} diff --git a/java/src/game/audio/jorbis/Lpc.java b/java/src/game/audio/jorbis/Lpc.java new file mode 100644 index 0000000..5a456f0 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.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 + * + * Many thanks to + * Monty 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 game.audio.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 + , 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=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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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; i1){ + 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; i1){ + for(int i=0; i=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=info.submaps){ + info.free(); + return (null); + } + } + } + + for(int i=0; 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=0; i--){ + float[] pcmM=vb.pcm[info.coupling_mag[i]]; + float[] pcmA=vb.pcm[info.coupling_ang[i]]; + + for(int j=0; j0){ + 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 + * + * Many thanks to + * Monty 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 game.audio.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>>j!=0; j++) + if(((msb>>>j)&i)!=0) + acc|=1<>>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>>(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< + * + * Many thanks to + * Monty 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 game.audio.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/java/src/game/audio/jorbis/PsyLook.java b/java/src/game/audio/jorbis/PsyLook.java new file mode 100644 index 0000000..70285ef --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.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/java/src/game/audio/jorbis/Residue0.java b/java/src/game/audio/jorbis/Residue0.java new file mode 100644 index 0000000..6ba0e90 --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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; j3){ + /* 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=vi.books){ + free_info(info); + return (null); + } + + for(int j=0; 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; jmaxstage) + maxstage=stages; + look.partbooks[j]=new int[stages]; + for(int k=0; k + * + * Many thanks to + * Monty 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 game.audio.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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +class Residue2 extends Residue0{ + + int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch){ + int i=0; + for(i=0; i + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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_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; ientries/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; ibim <= b->entries + // treat the above as an initial guess + while(true){ + int acc=1; + int acc1=1; + for(int i=0; ientries){ + 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 "+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; + 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/java/src/game/audio/jorbis/Time0.java b/java/src/game/audio/jorbis/Time0.java new file mode 100644 index 0000000..6c64b3e --- /dev/null +++ b/java/src/game/audio/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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import game.audio.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/java/src/game/audio/jorbis/Util.java b/java/src/game/audio/jorbis/Util.java new file mode 100644 index 0000000..ce82e07 --- /dev/null +++ b/java/src/game/audio/jorbis/Util.java @@ -0,0 +1,30 @@ +package game.audio.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/java/src/game/audio/jorbis/VorbisFile.java b/java/src/game/audio/jorbis/VorbisFile.java new file mode 100644 index 0000000..0d5cbf7 --- /dev/null +++ b/java/src/game/audio/jorbis/VorbisFile.java @@ -0,0 +1,1398 @@ +/* -*-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 + * + * Many thanks to + * Monty 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 game.audio.jorbis; + +import java.io.InputStream; + +import game.audio.jogg.*; + +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=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; i0){ + // 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=links) + return (-1); + if(!seekable&&i!=0) + return (bitrate(0)); + if(i<0){ + long bits=0; + for(int j=0; j0){ + 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) + return (-1); + if(i<0){ + long acc=0; + for(int j=0; j=links) + return (-1); + if(i<0){ + float acc=0; + for(int j=0; joffsets[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=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_offsettarget) + samples=target; + vd.synthesis_read(samples); + pcm_offset+=samples; + + if(samplestime_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; j127) + 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; i32767) + 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; i32767) + 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; j32767) + 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; j32767) + 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/java/src/game/gui/GuiInfo.java b/java/src/game/gui/GuiInfo.java index adacd58..4c773ef 100644 --- a/java/src/game/gui/GuiInfo.java +++ b/java/src/game/gui/GuiInfo.java @@ -17,7 +17,7 @@ public class GuiInfo extends Gui { "Update 0.2 - Läuft jetzt auch mit nur 512KB Fast-RAM!"; private static final String[] LIBRARIES = { - "LWJGL 3.3.6+1 (GLFW + OpenGL + Opus)", + "LWJGL 3.3.6+1 (GLFW + OpenGL)", "Netty 4.1.119-Final" }; private static final String[] CODE = { @@ -25,6 +25,7 @@ public class GuiInfo extends Gui { "Joonas Vali - NameGenerator", "LWJGL 2.9.4-nightly-20150209 - Project, Vector*, Matrix*", "Guava 17.0 - collect + future + Predicates", + "JOrbis 20101023 (JCraft) - jogg, jorbis, CodecJOrbis", "MC 1.8.9" }; diff --git a/java/src/game/init/SoundEvent.java b/java/src/game/init/SoundEvent.java index 5bb779d..716f5d2 100755 --- a/java/src/game/init/SoundEvent.java +++ b/java/src/game/init/SoundEvent.java @@ -4,12 +4,7 @@ import java.io.BufferedInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.ShortBuffer; - -import org.lwjgl.BufferUtils; -import org.lwjgl.util.opus.OpusFile; - +import game.audio.CodecJOrbis; import game.log.Log; import game.rng.Random; import game.util.FileUtils; @@ -139,23 +134,22 @@ public enum SoundEvent { for(int z = 0; z < entry.sounds.length; z++) { String sound = entry.sounds[z]; Log.SOUND.trace("Lade Sound %s", sound); - entry.buffers[z] = readOpus("sounds/" + sound + ".opus"); + entry.buffers[z] = readOgg("sounds/" + sound + ".ogg"); } } } - private static short[] readOpus(String filename) { + private static short[] readOgg(String filename) { InputStream in = null; - byte[] data; try { in = new BufferedInputStream(FileUtils.getResource(filename)); - data = FileUtils.readBytes(in); + return CodecJOrbis.readAll(in); } catch(FileNotFoundException e) { Log.IO.error("Fehler beim Lesen von OPUS-Datei '%s': Datei nicht gefunden", filename); return null; } - catch(IOException e) { + catch(Exception e) { Log.IO.error("Fehler beim Lesen von OPUS-Datei '%s': %s", filename, e.getMessage()); return null; } @@ -167,40 +161,6 @@ public enum SoundEvent { catch(IOException e) { } } - ByteBuffer buf = BufferUtils.createByteBuffer(data.length); - buf.put(data); - buf.flip(); - long fd = OpusFile.op_open_memory(buf, null); - if(fd == 0L) { - Log.IO.error("Fehler beim Lesen von OPUS-Datei '%s': Falsches oder fehlerhaftes Format", filename); - return null; - } - if(OpusFile.op_channel_count(fd, -1) != 1) { - Log.IO.error("Fehler beim Lesen von OPUS-Datei '%s': Falsches Anzahl von Kanälen (!= 1)", filename); - OpusFile.op_free(fd); - return null; - } - long samples = OpusFile.op_pcm_total(fd, -1); - if(samples < 0L) { - Log.IO.error("Fehler beim Lesen von OPUS-Datei '%s': E/A-Fehler", filename); - OpusFile.op_free(fd); - return null; - } - ShortBuffer pcm = BufferUtils.createShortBuffer((int)samples); - int state; - while((state = OpusFile.op_read(fd, pcm, null)) > 0L) { - pcm.position(pcm.position() + state); - } - if(state < 0L) { - Log.IO.error("Fehler beim Lesen von OPUS-Datei '%s': E/A-Fehler", filename); - OpusFile.op_free(fd); - return null; - } - OpusFile.op_free(fd); - short[] decoded = new short[(int)samples]; - pcm.rewind(); - pcm.get(decoded); - return decoded; } private SoundEvent(String ... sounds) {