/* * This is a Java port of the a52dec audio codec,a free ATSC A-52 stream decoder. * Copyright (c) 2003 Jonathan Hueber. * * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> * * a52dec is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * a52dec 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 1a39e335700bec46ae31a38e2156a898 */ package net.sourceforge.jffmpeg.codecs.audio.ac3; import java.awt.Dimension; import javax.media.Codec; import javax.media.Format; import javax.media.format.AudioFormat; import javax.media.Buffer; import net.sourceforge.jffmpeg.JMFCodec; import net.sourceforge.jffmpeg.GPLLicense; import net.sourceforge.jffmpeg.codecs.audio.ac3.data.Tables; import net.sourceforge.jffmpeg.codecs.utils.BitStream; import net.sourceforge.jffmpeg.codecs.utils.FFMpegException; /** * AC3 Codec */ public class AC3Decoder implements Codec, GPLLicense, JMFCodec { public static final boolean debug = false; int bias = 0; public static final int SYNC_BYTES = 0x0b77; public static final int HEADER_LENGTH = 7; public static final int A52_CHANNEL = 0; public static final int A52_MONO = 1; public static final int A52_STEREO = 2; public static final int A52_3F = 3; public static final int A52_2F1R = 4; public static final int A52_3F1R = 5; public static final int A52_2F2R = 6; public static final int A52_3F2R = 7; public static final int A52_CHANNEL1 = 8; public static final int A52_CHANNEL2 = 9; public static final int A52_DOLBY = 10; public static final int A52_CHANNEL_MASK = 15; public static final int A52_LFE = 16; public static final int A52_ADJUST_LEVEL = 32; private static double LEVEL_PLUS6DB = 2.0; private static double LEVEL_PLUS3DB = 1.4142135623730951; private static double LEVEL_3DB = 0.7071067811865476; private static double LEVEL_45DB = 0.5946035575013605; private static double LEVEL_6DB = 0.5; private static final int EXP_REUSE = 0; private static final int EXP_D15 = 1; private static final int EXP_D25 = 2; private static final int EXP_D45 = 3; private static final int DELTA_BIT_REUSE = 0; private static final int DELTA_BIT_NEW = 1; private static final int DELTA_BIT_NONE = 2; private static final int DELTA_BIT_RESERVED = 3; /* * Header information */ private int flags; private int sample_rate; private int bit_rate; private int frame_length; // Including header /* * Frame information */ private int fscod; private int halfrate; private int acmod; private double clev, slev; private boolean lfeon; private int language; /* Internal state */ public static final int MAX_CHANNELS = 5; public static final int MAX_BANDS = 18; public static final int EXPONENT_SIZE = 256; public static final int MAX_DELT_BA_SIZE = 50; private double level = 200; public boolean dynrnge = false; private double dynrng; private int chincpl; private boolean phsflginu; private int ncplbnd; private int cplstrtbnd; private int cplstrtmant; private int cplendmant; private int cplbndstrc; private double[][] cplco = new double[ MAX_CHANNELS ][ MAX_BANDS ]; private int rematflg; private int[] endmant = new int[ MAX_CHANNELS ]; // private int cplextstr; private byte[] cpl_expbapExp = new byte[ EXPONENT_SIZE ]; private byte[][] fbw_expbapExp = new byte[ MAX_CHANNELS ][ EXPONENT_SIZE ]; private byte[] lfe_expbapExp = new byte[ EXPONENT_SIZE ]; private byte[] cpl_expbapBap = new byte[ EXPONENT_SIZE ]; private byte[][] fbw_expbapBap = new byte[ MAX_CHANNELS ][ EXPONENT_SIZE ]; private byte[] lfe_expbapBap = new byte[ EXPONENT_SIZE ]; private int bai; private int cplbaBai; private int cplbaDeltbae; private int[] cplbaDeltba = new int[ MAX_DELT_BA_SIZE ]; private int[] baBai = new int[ MAX_CHANNELS ]; private int[] baDeltbae = new int[ MAX_CHANNELS ]; private int[][] baDeltba = new int[ MAX_CHANNELS ][ MAX_DELT_BA_SIZE ]; private int lfebaBai; private int lfebaDeltbae; //Note DeltBae is always DELTA_BIT_NONE for lfe private int[] lfebaDeltba = new int[ MAX_DELT_BA_SIZE ]; private int csnroffst; private int cplfleak; private int cplsleak; private int lfsr_state = 1; private Quantizer quant = new Quantizer(); private double[] samplesOut = new double[ (MAX_CHANNELS + 2) * 256 * 2]; private boolean downmixed; BitStream in = new BitStream(); public static final int[] halfRate = {0,0,0,0,0,0,0,0,0,1,2,3}; public static final int[] rate = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640}; public static final int[] lfeonValues = {0x10, 0x10, 0x04, 0x04, 0x04, 0x01, 0x04, 0x01}; public static final double[] clevValues = { LEVEL_3DB, LEVEL_45DB, LEVEL_6DB, LEVEL_45DB }; public static final double[] slevValues = { LEVEL_3DB, LEVEL_6DB, 0, LEVEL_6DB }; public final byte[] exp_1 = Tables.getExponentTable1(); public final byte[] exp_2 = Tables.getExponentTable2(); public final byte[] exp_3 = Tables.getExponentTable3(); public final int[] dither_lutp = Tables.getDitherLoopupTable(); public final double[] scale_factor = Tables.getScaleFactors(); public final double[] q_1_0 = Tables.getQ10Table(); public final double[] q_1_1 = Tables.getQ11Table(); public final double[] q_1_2 = Tables.getQ12Table(); public final double[] q_2_0 = Tables.getQ20Table(); public final double[] q_2_1 = Tables.getQ21Table(); public final double[] q_2_2 = Tables.getQ22Table(); public final double[] q_3 = Tables.getQ3Table(); public final double[] q_4_0 = Tables.getQ40Table(); public final double[] q_4_1 = Tables.getQ41Table(); public final double[] q_5 = Tables.getQ5Table(); SoundOutput soundOutput = new SoundOutput(); /** * Read synchronisation bytes and header */ private boolean a52_syncinfo() throws FFMpegException { /* Check for synchronisation block */ while ( in.showBits( 16 ) != SYNC_BYTES ) { in.getBits(8); // System.out.println( "Jump" ); //throw new Mpeg4Exception( "Not sync block" ); if ( in.availableBits() <= HEADER_LENGTH * 8 ) return false; } in.getBits(16); in.getBits( 16 ); //Skip int byte4 = in.getBits( 8 ); int byte5 = in.getBits( 8 ); int byte6 = in.getBits( 8 ); int half = halfRate[ byte5 >> 3 ]; int acmod = byte6 >> 5; flags = (((byte6 & 0xf8) == 0x50) ? A52_DOLBY : acmod ) | (((byte6 & lfeonValues[ acmod ]) != 0) ? A52_LFE : 0 ); int frmsizecod = byte4 & 63; if ( frmsizecod >= 38 ) throw new AC3Exception( "Unknown rate" ); bit_rate = (rate[ frmsizecod >> 1 ] * 1000) >> half; switch ( byte4 & 0xc0 ) { case 0x00: { sample_rate = 48000 >> half; frame_length = 4 * rate[ frmsizecod >> 1 ]; break; } case 0x40: { sample_rate = 44100 >> half; frame_length = 2 * ( 320 * rate[ frmsizecod >> 1 ] / 147 + (frmsizecod & 1) ); break; } case 0x80: { sample_rate = 32000 >> half; frame_length = 6 * rate[ frmsizecod >> 1 ]; break; } default: { throw new AC3Exception( "Unrecognised sample rate multiplier" ); } } if ( debug) System.out.println( "Sync - flags:" + Integer.toHexString( flags ) + " sample_rate:" + sample_rate + " bit_rate:" + bit_rate ); in.seek( in.getPos() - 56 ); //Return to start of frame return true; } /* Read a frame of data */ private void a52_frame() throws FFMpegException { if ( debug ) System.out.println( "a52_frame" + in.getPos() ); in.getBits( 16 + 16 ); fscod = in.getBits( 3 ); in.getBits( 5 ); int halfRateIndex = in.getBits( 5 ); if ( halfRateIndex >= halfRate.length ) throw new FFMpegException( "Illegal half rate" ); halfrate = halfRate[ halfRateIndex ]; in.getBits( 3 ); acmod = in.getBits( 3 ); if ( (acmod == 2) && (in.getBits(2) == 2) ) { //acmod = A52_DOLBY; /* !!!! Note this is a local variable in the C version !!!! */ } clev = 0; if ( ((acmod & 1) != 0) && (acmod != 1) ) { clev = clevValues[ in.getBits(2) ]; } slev = 0; if ( (acmod & 4) != 0) { slev = slevValues[ in.getBits(2) ]; } if ( debug ) System.out.println( "clev " + show_sample(clev) + " slev " + show_sample(slev) ); lfeon = in.getTrueFalse(); level = 2; if ( debug ) System.out.println( "level " + show_sample(level) ); downmix_init(acmod); if ( debug ) System.out.println( "bias " + show_sample(bias) ); //set level and bias level *= 2; dynrng = level; dynrnge = false; //No callback //SET DELTA_BIT_NONE boolean repeat = (acmod == 0); do { in.getBits( 5 ); if ( in.getTrueFalse() ) in.getBits( 8 ); //Compression if ( in.getTrueFalse() ) language = in.getBits( 8 ); //Language code if ( in.getTrueFalse() ) in.getBits( 7 ); //Mix level and room type repeat = !repeat; } while ( !repeat ); in.getBits( 2 ); //Copyright and original bits if ( in.getTrueFalse() ) in.getBits( 14 ); //Time code 1 if ( in.getTrueFalse() ) in.getBits( 14 ); //Time code 2 if ( in.getTrueFalse() ) { //Skip additionl data int addbsil = in.getBits( 6 ); in.seek( in.getPos() + addbsil * 8 ); } } public static final int[] nfchansValues = new int[] {2, 1, 2, 3, 3, 4, 4, 5, 1, 1, 2}; public static final int[] cplstrtbndValues = new int[] { 31, 35, 37, 39, 41, 42, 43, 44, 45, 45, 46, 46, 47, 47, 48, 48 }; public static final int[] rematrix_band = new int[] {25, 37, 61, 253}; private void a52_block() throws FFMpegException { /* Number of channels */ int nfchans = nfchansValues[ acmod ]; if ( debug && nfchans != 2 ) System.out.println( "nfchans " + nfchans + " acmod " + acmod ); /* Read block switch */ boolean[] blksw = new boolean[ 5 ]; for ( int i = 0; i < nfchans; i++ ) { blksw[i] = in.getTrueFalse(); if ( debug ) System.out.println( "blksw[" + i + "]=" + (blksw[i]? 1:0) ); } /* Read dither flags */ boolean[] dithflag = new boolean[ 5 ]; for ( int i = 0; i < nfchans; i++ ) { dithflag[i] = in.getTrueFalse(); if ( debug ) System.out.println( "dithflag[" + i + "]=" + (dithflag[i]? 1:0) ); } /* Read dynrng */ boolean repeat = (acmod == 0); do { /* Read dynrng */ if ( in.getTrueFalse() ) { int dynrngLocal = bitstream_get_2(8); if ( dynrnge ) { if ( debug ) System.out.println( "dynrnge" ); dynrng = ((( dynrngLocal & 0x1f ) | 0x20) << 13) * scale_factor[ 2 - (dynrngLocal >> 5) ] * level; } // CALL dynrngcall if ( debug ) System.out.println( "dynrng=" + dynrng ); } if ( debug ) System.out.println( "dynrngLoop" ); repeat = !repeat; } while ( !repeat ); if ( debug ) System.out.println( "clev " + show_sample(clev) + " slev " + show_sample(slev) ); /* Read cplstre */ if ( in.getTrueFalse() ) { chincpl = 0; if ( in.getTrueFalse() ) { for ( int i = 0; i < nfchans; i++ ) { chincpl |= in.getBits(1) << i; } if (debug) System.out.println( "chincpl=" + chincpl ); switch( acmod ) { case 0: case 1: { throw new AC3Exception( "Invalid mode" ); } case 2: { phsflginu = in.getTrueFalse(); break; } } int cplbegf = in.getBits( 4 ); int cplendf = in.getBits( 4 ); if ( debug ) System.out.println( "cplbegf=" + cplbegf ); if ( cplendf + 3 - cplbegf < 0 ) throw new AC3Exception( "Invalid values" ); ncplbnd = cplendf + 3 - cplbegf; cplstrtbnd = cplstrtbndValues[ cplbegf ]; cplstrtmant = cplbegf * 12 + 37; cplendmant = cplendf * 12 + 73; /* Read cplbndstrc */ cplbndstrc = 0; int ncplsubnd = ncplbnd; for ( int i = 0; i < ncplsubnd - 1; i++ ) { if ( in.getTrueFalse() ) { cplbndstrc |= 1 << i; ncplbnd--; } } } } /* Read cplinu */ if ( chincpl != 0 ) { boolean cplcoe = false; for ( int i = 0; i <nfchans; i++ ) { if ( ((chincpl >> i ) & 1) != 0 ) { if ( in.getTrueFalse() ) { cplcoe = true; int mstrcplco = 3 * in.getBits(2); for ( int j = 0; j < ncplbnd; j++ ) { int cplcoexp = in.getBits(4); int cplcomant = in.getBits(4); if ( cplcoexp == 15) { cplcomant <<= 14; } else { cplcomant = (cplcomant | 0x10) << 13; } cplco[ i ][ j ] = cplcomant * scale_factor[cplcoexp + mstrcplco]; if ( debug ) System.out.println( "i="+i+" j="+j+" cplco=" + cplco[i][j] ); } } } } if ( acmod == 2 && phsflginu && cplcoe ) { for ( int j = 0; j < ncplbnd; j++ ) { if ( in.getTrueFalse() ) { cplco[ 1 ][ j ] = - cplco[ 1 ][ j ]; } } } } /* Read rmatstr */ if ( (acmod == 2) && in.getTrueFalse() ) { rematflg = 0; int end = (chincpl != 0) ? cplstrtmant : 253; int i = 0; do { rematflg |= in.getBits(1) << i; if ( debug ) System.out.println( "rematflg["+ i + "]=" + ((rematflg>>i)&1) ); } while ( rematrix_band[ i++ ] < end ); } /* cplexpstr */ int cplexpstr = EXP_REUSE; int lfeexpstr = EXP_REUSE; if ( chincpl != 0 ) { cplexpstr = in.getBits(2); if ( debug ) System.out.print( "cplextstr=" + cplexpstr + " " ); } int[] chexpstr = new int[ 5 ]; for ( int i = 0; i < nfchans; i++ ) { chexpstr[i] = in.getBits(2); if ( debug ) System.out.print( "chextstr=" + chexpstr[i] + " " ); } if ( lfeon ) { lfeexpstr = in.getBits( 1 ); if ( debug ) System.out.print( "lfeexpstr=" + lfeexpstr + " " ); } if ( debug ) System.out.println(); for ( int i = 0; i < nfchans; i++ ) { if ( chexpstr[i] != EXP_REUSE ) { if (((chincpl >> i) & 1) != 0) { endmant[i] = cplstrtmant; } else { int chbwcod = in.getBits(6); if ( chbwcod > 60 ) throw new AC3Exception( "chbwcod too large" ); endmant[i] = chbwcod * 3 + 73; } if ( debug ) System.out.println( "endmant[" + i + "]=" + endmant[i] ); } } int do_bit_alloc = 0; if ( debug ) System.out.println( "cplendmant=" + cplendmant ); if ( debug ) System.out.println( "cplstrtmant=" + cplstrtmant ); if ( cplexpstr != EXP_REUSE ) { do_bit_alloc = 0x40; int ncplgrps = (cplendmant - cplstrtmant)/(3 << (cplexpstr - 1)); byte cplabsexp = (byte)(in.getBits( 4 ) << 1); parse_exponents( cplexpstr, ncplgrps, cplabsexp, cpl_expbapExp, cplstrtmant ); } for ( int i = 0; i < nfchans; i++ ) { if ( chexpstr[i] != EXP_REUSE ) { do_bit_alloc |= 1 << i; int grp_size = 3 << (chexpstr[i] - 1); int nchgrps = (endmant[i] + grp_size - 4)/grp_size; fbw_expbapExp[i][0] = (byte)in.getBits(4); parse_exponents( chexpstr[i], nchgrps, fbw_expbapExp[i][0], fbw_expbapExp[i], 1 ); in.getBits( 2 ); /* gainrng */ } } if ( lfeexpstr != EXP_REUSE ) { do_bit_alloc |= 0x20; lfe_expbapExp[0] = (byte)in.getBits( 4 ); parse_exponents( lfeexpstr, 2, lfe_expbapExp[0], lfe_expbapExp, 1 ); } /* Read baie */ if ( in.getTrueFalse() ) { do_bit_alloc = 0x7f; bai = in.getBits( 11 ); if ( debug ) System.out.println( "bai=" + (bai & 7)); } /* Read snroffst */ if ( in.getTrueFalse() ) { do_bit_alloc = 0x7f; csnroffst = in.getBits( 6 ); if ( chincpl != 0 ) { cplbaBai = in.getBits( 7 ); if ( debug ) System.out.println( "cplbaBai=" + (cplbaBai & 7)); } for ( int i = 0; i < nfchans; i++ ) { baBai[i] = in.getBits( 7 ); if ( debug ) System.out.println( "baBai=" + (baBai[i] &7)); } if ( lfeon ) { lfebaBai = in.getBits( 7 ); if ( debug ) System.out.println( "lfebaBai=" + (lfebaBai &7)); } } /* Read cplleak */ if ( (chincpl != 0) && in.getTrueFalse() ) { do_bit_alloc |= 0x40; cplfleak = 9 - in.getBits( 3 ); cplsleak = 9 - in.getBits( 3 ); } /* Read deltbaie */ if ( in.getTrueFalse() ) { do_bit_alloc = 0x7f; if ( chincpl != 0 ) { cplbaDeltbae = in.getBits( 2 ); } for ( int i = 0; i < nfchans; i++ ) { baDeltbae[ i ] = in.getBits( 2 ); } if ( chincpl != 0 && cplbaDeltbae == DELTA_BIT_NEW ) { parse_deltba( cplbaDeltba ); } for ( int i = 0; i < nfchans; i++ ) { if ( baDeltbae[i] == DELTA_BIT_NEW ) { parse_deltba( baDeltba[i] ); } } } if (debug ) System.out.println( "clev " + show_sample(clev) + " slev " + show_sample(slev) ); /* Manage memory allocation or clearing */ if ( do_bit_alloc != 0 ) { if (zero_snr_offsets (nfchans)) { for ( int i = 0; i < cpl_expbapBap.length; i++ ) { cpl_expbapBap[i] = 0; } for ( int j = 0; j < nfchans; j++ ) { for ( int i = 0; i < fbw_expbapBap[j].length; i++ ) { fbw_expbapBap[j][i] = 0; } } for ( int i = 0; i < lfe_expbapBap.length; i++ ) { lfe_expbapBap[i] = 0; } } else { if ( (chincpl != 0) && ((do_bit_alloc & 64) != 0) ) { a52_bit_allocate ( cplbaBai, cplbaDeltbae, cplbaDeltba, cplstrtbnd, cplstrtmant, cplendmant, cplfleak << 8, cplsleak << 8, cpl_expbapExp, cpl_expbapBap ); } for (int i = 0; i < nfchans; i++) if ((do_bit_alloc & (1 << i)) != 0) a52_bit_allocate (baBai[i], baDeltbae[i], baDeltba[i], 0, 0, endmant[i], 0, 0, fbw_expbapExp[i], fbw_expbapBap[i]); if ( lfeon && ((do_bit_alloc & 32) != 0)) { lfebaDeltbae = DELTA_BIT_NONE; a52_bit_allocate (lfebaBai, lfebaDeltbae, lfebaDeltba, 0, 0, 7, 0, 0, lfe_expbapExp, lfe_expbapBap); } } } if ( in.getTrueFalse() ) { int i = in.getBits( 9 ); in.seek( in.getPos() + i * 8 ); if ( debug ) System.out.println( "Skip " + i ); } int samplesPointer = 0; // if ( output & A52_LFE ) samplesPointer += 256; double[] coeff = new double[ 5 ]; if ( debug ) System.out.println( "dynrng " + show_sample(dynrng) + " clev " + show_sample(clev) + " slev " + show_sample(slev) ); a52_downmix_coeff( coeff, acmod, dynrng, clev, slev ); boolean done_cpl = false; int j; quant.setQ1Pointer( -1 ); quant.setQ2Pointer( -1 ); quant.setQ4Pointer( -1 ); for ( int i = 0; i < nfchans; i++ ) { coeff_get( samplesOut, samplesPointer + 256 * i, // Output fbw_expbapExp[i], fbw_expbapBap[i], // Work area quant, coeff[ i ], dithflag[ i ], endmant[ i ] ); if ( ((chincpl >> i) & 1) != 0 ) { if ( !done_cpl ) { done_cpl = true; coeff_get_coupling( nfchans, coeff, samplesOut, samplesPointer, quant, dithflag ); } j = cplendmant; } else { j = endmant[i]; } do { samplesOut[ samplesPointer + 256*i +j ] = 0; } while ( ++j < 256 ); } if ( debug ) { System.out.println( "point1" ); for ( int ch = 0; ch < nfchans; ch++ ) { System.out.println( "\nChannel " + ch ); for ( int i = 0; i < 256; i++ ) { System.out.print( show_sample( samplesOut[ samplesPointer + i + 256 * ch] ) + " " ); } } System.out.println(); } if ( acmod == 2 ) { int i = 0; j = 13; int end = ( endmant[ 0 ] < endmant[ 1 ] ) ? endmant[ 0 ] : endmant[ 1 ]; int rematflgLocal = rematflg; do { if ( (rematflgLocal & 1) == 0 ) { rematflgLocal >>= 1; j = rematrix_band[ i++ ]; continue; } rematflgLocal >>= 1; int band = rematrix_band[ i++ ]; if ( band > end ) band = end; do { //System.out.println( "Reorder " + j ); double tmp0 = samplesOut[ samplesPointer + j ]; double tmp1 = samplesOut[ samplesPointer + j + 256 ]; samplesOut[ samplesPointer + j ] = tmp0 + tmp1; samplesOut[ samplesPointer + j + 256 ] = tmp0 - tmp1; } while ( ++j < band ); } while ( j < end ); } if ( debug ) { System.out.println( "point2" ); for ( int ch = 0; ch < nfchans; ch++ ) { System.out.println( "\nChannel " + ch ); for ( int i = 0; i < 256; i++ ) { System.out.print( show_sample( samplesOut[ samplesPointer + i + 256 * ch] ) + " " ); } } System.out.println(); } if ( lfeon ) { // if ( output & A52_LFE ) { coeff_get( samplesOut, samplesPointer - 256, lfe_expbapExp, lfe_expbapBap, quant, 0, false, 7 ); for ( int i = 7; i < 256; i++ ) { samplesOut[ samplesPointer - 256 + i ] = 0; } soundOutput.a52_imdct_512( samplesOut, samplesPointer - 256, samplesPointer -256 + 1536, bias ); } } int i = 0; /* Stereo or 5.2 */ // if ( nfchans_tbl[ output & A52_CHANNEL_MASK ] < nfchans ) if ( 2 < nfchans ) { for ( i = 1; i < nfchans; i++ ) { if (blksw[i] != blksw[0] ) break; } } if ( i < nfchans ) { if ( debug ) System.out.println( "i < nfchans" ); if ( downmixed ) { downmixed = false; // a52_upmix( samplesOut, 1536, acmod, output ); } for ( i = 0; i < nfchans; i++ ) { // if ( (chanbias & (1<<i)) == 0 ) { // bias = this.bias; // } if ( coeff[i] != 0 ) { if ( blksw[i] ) { soundOutput.a52_imdct_256( samplesOut, samplesPointer + 256 * i, samplesPointer + 1536 + 256 * i, bias ); } else { soundOutput.a52_imdct_512( samplesOut, samplesPointer + 256 * i, samplesPointer + 1536 + 256 * i, bias ); } } else { for ( j = 0; j < 256; j++ ) { samplesOut[ samplesPointer + 256 * i + j ] = 0; } } } //a52_downmix( ); } else { if ( debug ) System.out.println( "i >= nfchans" ); int bias = 0; //nfchans //a52_downmix( ); if ( !downmixed ) { downmixed = true; //a52_downmix } if ( blksw[ 0 ] ) { for ( i = 0; i < nfchans; i++ ) { soundOutput.a52_imdct_256( samplesOut, samplesPointer + 256 * i, samplesPointer + 1536 + 256 * i, bias ); } } else { for ( i = 0; i < nfchans; i++ ) { soundOutput.a52_imdct_512( samplesOut, samplesPointer + 256 * i, samplesPointer + 1536 + 256 * i, bias ); } } } } private static final int[] slowgainValues = new int[] { 0x540, 0x4d8, 0x478, 0x410 }; private static final int[] dbpbValues = new int[] { 0xc00, 0x500, 0x300, 0x100 }; private static final int[] floorValues = new int[] { 0x910, 0x950, 0x990, 0x9d0, 0xa10, 0xa90, 0xb10, 0x1400 }; private static final int[][] hthValues = Tables.getBitAllocHthTable(); private static final int[] zeroBaArray = new int[ MAX_DELT_BA_SIZE ]; private static final byte[] bapTable = Tables.getBitAllocBapTable(); private static final int[] bndTable = Tables.getBitAllocBndTable(); private static final int[] laTable = Tables.getBitAllocLaTable(); private void a52_bit_allocate ( int baBai, int deltbae, int[] deltba, int bndstart, int start, int end, int fastleak, int slowleak, byte[] expbapExp, byte[] expbapBap ) { byte[] exp = expbapExp; byte[] bap = expbapBap; if ( debug ) System.out.println( "bit_alloc" ); int fdecay = ( 63 + 20 * ((this.bai >> 7) & 3)) >> halfrate; int fgain = 128 + 128 * (baBai & 7); int sdecay = (15 + 2 * (this.bai >> 9)) >> halfrate; int sgain = slowgainValues[ (this.bai >> 5 ) & 3 ]; int dbknee = dbpbValues[ ( this.bai >> 3 ) & 3 ]; int[] hth = hthValues[ fscod ]; if ( deltbae == DELTA_BIT_NONE ) deltba = zeroBaArray; int floor = floorValues[ this.bai & 7 ]; int snroffset = 960 - 64 * csnroffst - 4 * ( baBai >> 3) + floor; floor >>= 5; int psd; int mask; int i = bndstart; int j = start; if ( start == 0 ) { /* Not the coupling channel */ int lowcomp = 0; j = end - 1; do { if ( i < j ) { if ( exp[ i + 1 ] == exp[ i ] - 2 ) { lowcomp = 384; } else if ( (lowcomp != 0) && exp[ i + 1 ] > exp[ i ] ) { lowcomp -= 64; } } psd = 128 * exp[i]; mask = psd + fgain + lowcomp; /* COMPUTE_MASK */ if ( psd > dbknee ) mask -= (psd - dbknee)>>2; if ( mask > hth[ i >> halfrate] ) mask = hth[ i >> halfrate ]; mask -= snroffset + 128 * deltba[ i ]; mask = (mask > 0) ? 0: ((-mask) >> 5); mask -= floor; /* End COMPUTE_MASK */ bap[ i ] = bapTable[ 156 + mask + 4 * exp[i] ]; if ( debug ) System.out.println( "Abap[" + i + "]=" + bap[i] + " exp=" + exp[i] + " mask=" + mask + " deltba=" + deltba[i] + " dbknee=" + dbknee + " psd=" +psd + " hth=" + hth[ i >> halfrate]); i++; } while ( (i < 3) || ( i < 7 && exp[i] > exp[i - 1]) ); fastleak = psd + fgain; slowleak = psd + sgain; while ( i < 7 ) { if ( i < j ) { if ( exp[ i + 1 ] == exp[ i ] - 2 ) { lowcomp = 384; } else if ( (lowcomp != 0) && exp[ i + 1 ] > exp[ i ] ) { lowcomp -= 64; } } psd = 128 * exp[i]; /* UPDATE_LEAK */ fastleak += fdecay; if ( fastleak > psd + fgain ) fastleak = psd + fgain; slowleak += sdecay; if ( slowleak > psd + sgain ) slowleak = psd + sgain; /* End UPDATE_LEAK */ mask = ((fastleak + lowcomp) < slowleak) ? (fastleak + lowcomp) : slowleak; /* COMPUTE_MASK */ if ( psd > dbknee ) mask -= (psd - dbknee)>>2; if ( mask > hth[ i >> halfrate] ) mask = hth[ i >> halfrate ]; mask -= snroffset + 128 * deltba[ i ]; mask = (mask > 0) ? 0: ((-mask) >> 5); mask -= floor; /* End COMPUTE_MASK */ bap[i] = bapTable[ 156 + mask + 4 * exp[i] ]; if ( debug ) System.out.println( "Bbap[" + i + "]=" + bap[i] ); i++; } if ( end == 7 ) return; /* LFE channel */ do { if ( exp[ i + 1 ] == exp[ i ] - 2 ) { lowcomp = 320; } else if ( (lowcomp != 0) && exp[ i + 1 ] > exp[ i ] ) { lowcomp -= 64; } psd = 128 * exp[i]; /* UPDATE_LEAK */ fastleak += fdecay; if ( fastleak > psd + fgain ) fastleak = psd + fgain; slowleak += sdecay; if ( slowleak > psd + sgain ) slowleak = psd + sgain; /* End UPDATE_LEAK */ mask = ((fastleak + lowcomp) < slowleak) ? (fastleak + lowcomp) : slowleak; /* COMPUTE_MASK */ if ( psd > dbknee ) mask -= (psd - dbknee)>>2; if ( mask > hth[ i >> halfrate] ) mask = hth[ i >> halfrate ]; mask -= snroffset + 128 * deltba[ i ]; mask = (mask > 0) ? 0: ((-mask) >> 5); mask -= floor; /* End COMPUTE_MASK */ bap[i] = bapTable[ 156 + mask + 4 * exp[ i ] ]; if ( debug ) System.out.println( "Cbap[" + i + "]=" + bap[i] ); i++; } while ( i < 20 ); while ( lowcomp > 128 ) { lowcomp -= 128; psd = 128 * exp[i]; /* UPDATE_LEAK */ fastleak += fdecay; if ( fastleak > psd + fgain ) fastleak = psd + fgain; slowleak += sdecay; if ( slowleak > psd + sgain ) slowleak = psd + sgain; /* End UPDATE_LEAK */ mask = ((fastleak + lowcomp) < slowleak) ? (fastleak + lowcomp) : slowleak; /* COMPUTE_MASK */ if ( psd > dbknee ) mask -= (psd - dbknee)>>2; if ( mask > hth[ i >> halfrate] ) mask = hth[ i >> halfrate ]; mask -= snroffset + 128 * deltba[ i ]; mask = (mask > 0) ? 0: ((-mask) >> 5); mask -= floor; /* End COMPUTE_MASK */ bap[i] = bapTable[ 156 + mask + 4 * exp[ i ] ]; if ( debug ) System.out.println( "Dbap[" + i + "]=" + bap[i] ); i++; } j = i; } do { int startband = j; int endband = ( bndTable[ i - 20] < end ) ? bndTable[ i - 20 ] : end; psd = 128 * exp[ j++ ]; while ( j < endband ) { int next = 128 * exp[j++]; int delta = next - psd; switch ( delta >> 9 ) { case -6: case -5: case -4: case -3: case -2: { psd = next; break; } case -1: { psd = next + laTable[ (-delta) >> 1 ]; break; } case 0: { psd += laTable[ delta >> 1 ]; break; } } } if ( debug ) System.out.println( "leak " + fdecay + " " + sdecay + " " + fastleak + " " + slowleak + " " + psd); /* UPDATE_LEAK */ fastleak += fdecay; if ( fastleak > psd + fgain ) fastleak = psd + fgain; slowleak += sdecay; if ( slowleak > psd + sgain ) slowleak = psd + sgain; /* End UPDATE_LEAK */ mask = (fastleak < slowleak) ? fastleak : slowleak; if ( debug ) System.out.println( "mask1 " + mask ); /* COMPUTE_MASK */ if ( psd > dbknee ) mask -= (psd - dbknee)>>2; if ( mask > hth[ i >> halfrate] ) mask = hth[ i >> halfrate ]; mask -= snroffset + 128 * deltba[ i ]; mask = (mask > 0) ? 0: ((-mask) >> 5); mask -= floor; /* End COMPUTE_MASK */ i++; j = startband; do { bap[j] = bapTable[ 156 + mask + 4 * exp[ j ] ]; if ( debug ) System.out.println( "Ebap[" + j + "] exp="+exp[j]+" mask=" +mask); } while ( ++j < endband ); } while ( j < end ); } private boolean zero_snr_offsets( int nfchans ) { int i; /* Check the value of fsnroffst ( bits 3-7 of bai ) */ if ((csnroffst != 0) || (chincpl != 0 && ((cplbaBai >>3)!= 0)) || (lfeon && ((lfebaBai >> 3) != 0))) { return false; } for (i = 0; i < nfchans; i++) { if ((baBai[i] >> 3) != 0) return false; } return true; } private int dither_gen() { int nstate = dither_lutp[ lfsr_state >> 8 ] ^ (lfsr_state << 8); if ( (nstate & 0x8000) != 0 ) { nstate |= (-1 << 16); } else { nstate &= 0xffff; } lfsr_state = nstate & 0xffff; if ( debug ) System.out.println( "dither_gen=" + lfsr_state ); return nstate; /* NOTE VERSION */ } private void coeff_get( double[] samples, int samplesPointer, byte[] exp, byte[] bap, Quantizer quant, double level, boolean dither, int end) throws FFMpegException { if ( debug ) System.out.println( "coeff_get " + show_sample(level) ); double[] factor = new double[ 25 ]; for ( int i = 0; i <= 24; i++ ) { factor[ i ] = scale_factor[ i ] * level; } for ( int i = 0; i < end; i++ ) { if ( debug ) if ( i != 0 ) System.out.println( "coeff_get: sample=" + show_sample(samples[ samplesPointer + i - 1]) ); int bapi = bap[i]; if ( debug ) System.out.println( "bapi=" + bapi ); switch ( bapi ) { case 0: { if ( dither ) { int d = dither_gen(); samples[ samplesPointer + i ] = d * factor[exp[i]] * LEVEL_3DB; /* NOTE VERSION */ if ( debug ) System.out.println( "dither " + exp[i] + " " + d + " " + show_sample(samples[ samplesPointer + i ]) ); } else { samples[ samplesPointer + i ] = 0; } break; } case -1: { if ( quant.getQ1Pointer() >= 0 ) { samples[ samplesPointer + i ] = quant.getQ1()[ quant.getQ1Pointer() ] * factor[ exp[i] ]; //System.out.println( "case -1 Q1=" + show_sample(quant.getQ1()[ quant.getQ1Pointer() ]) ); quant.setQ1Pointer( quant.getQ1Pointer() - 1 ); } else { int code = in.getBits( 5 ); //System.out.println( "case -1 code=" + code ); quant.setQ1Pointer( 1 ); quant.getQ1()[0] = q_1_2[code]; quant.getQ1()[1] = q_1_1[code]; samples[ samplesPointer + i ] =q_1_0[code] * factor[ exp[i] ]; } break; } case -2: { if ( quant.getQ2Pointer() >= 0 ) { samples[ samplesPointer + i ] =quant.getQ2()[ quant.getQ2Pointer() ] * factor[ exp[i] ]; quant.setQ2Pointer( quant.getQ2Pointer() - 1 ); } else { int code = in.getBits( 7 ); quant.setQ2Pointer( 1 ); quant.getQ2()[0] = q_2_2[code]; quant.getQ2()[1] = q_2_1[code]; samples[ samplesPointer + i ] = q_2_0[code] * factor[ exp[i] ]; } break; } case 3: { samples[ samplesPointer + i ] = q_3[ in.getBits(3) ] * factor[ exp[i] ]; break; } case -3: { if ( quant.getQ4Pointer() == 0 ) { samples[ samplesPointer + i ] = quant.getQ4()[0] * factor[ exp[i] ]; //System.out.println( "-3A: q_4=" + quant.getQ4()[0] + " exp[" + i+"]=" + exp[i] ); quant.setQ4Pointer( -1 ); } else { int code = in.getBits( 7 ); //System.out.println( "-3B: code=" + code + " q_4_0=" + q_4_0[code] + " level=" + level ); quant.setQ4Pointer( 0 ); quant.getQ4()[0] = q_4_1[code]; samples[ samplesPointer + i ] = q_4_0[code] * factor[ exp[i] ]; } break; } case 4: { samples[ samplesPointer + i ] = q_5[in.getBits(4)] * factor[ exp[i] ]; break; } default: { int tmp = bitstream_get_2(bapi); if ( debug ) System.out.println( "default " + tmp + " " + i + " " + exp[i] ); samples[ samplesPointer + i ] = ((double)(tmp << (16 - bapi))) * factor[exp[i]]; break; } } } } /** * Signed get */ private int bitstream_get_2( int numberOfBits ) { int tmp = in.getBits( numberOfBits ); if ( (tmp & ( 1 << (numberOfBits - 1) )) != 0 ) { tmp |= (-1 << numberOfBits ); } return tmp; } private void coeff_get_coupling( int nfchans, double[] coeff, double[] samples, int samplesPointer, Quantizer quant, boolean[] dithflag ) throws FFMpegException { double[] cplcoLocal = new double[ 5 ]; byte[] exp = cpl_expbapExp; byte[] bap = cpl_expbapBap; int bnd = 0; int cplbndstrcLocal = cplbndstrc; int i = cplstrtmant; while ( i < cplendmant ) { int i_end = i + 12; while ( (cplbndstrcLocal & 1) != 0 ) { cplbndstrcLocal >>= 1; i_end += 12; } cplbndstrcLocal >>= 1; for ( int ch = 0; ch < nfchans; ch++ ) { cplcoLocal[ch] = cplco[ch][bnd] * coeff[ch]; if ( debug ) System.out.println( "cplcoLocal[" + ch + "]=" + show_sample(cplcoLocal[ch]) + " coeff=" + show_sample(coeff[ch])); } bnd++; if ( debug ) System.out.println( "i_end " + (i_end - cplstrtmant) ); while ( i < i_end ) { double cplcoeff; if ( debug && i != 0 ) { System.out.print( "coeff_get_coupling: sample=" ); for ( int ch = 0; ch < nfchans; ch++ ) { System.out.print( show_sample(samples[ samplesPointer + i - 1 + ch * 256 ])+ ", "); } System.out.println(); } int bapi = bap[i]; if ( debug ) System.out.println( "bapi=" + bapi ); switch ( bapi ) { case 0: { cplcoeff = LEVEL_3DB * scale_factor[exp[i]]; for ( int ch = 0; ch < nfchans; ch++ ) { if ( ((chincpl >> ch ) & 1) != 0 ) { if ( dithflag[ ch ] ) { samples[ samplesPointer + i + ch * 256 ] = cplcoeff * cplcoLocal[ch] * dither_gen(); if ( debug ) System.out.println( "cplcoeff " + show_sample(cplcoLocal[ch]) ); } else { if ( debug ) System.out.println( "!dithflag" ); samples[ samplesPointer + i + ch * 256 ] = 0; } } } i++; break; } case -1: { if ( quant.getQ1Pointer() >= 0 ) { cplcoeff = quant.getQ1()[ quant.getQ1Pointer() ]; quant.setQ1Pointer( quant.getQ1Pointer() - 1 ); } else { int code = in.getBits( 5 ); quant.setQ1Pointer( 1 ); quant.getQ1()[0] = q_1_2[code]; quant.getQ1()[1] = q_1_1[code]; cplcoeff = q_1_0[code]; } break; } case -2: { if ( quant.getQ2Pointer() >= 0 ) { cplcoeff = quant.getQ2()[ quant.getQ2Pointer() ]; quant.setQ2Pointer( quant.getQ2Pointer() - 1 ); } else { int code = in.getBits( 7 ); quant.setQ2Pointer( 1 ); quant.getQ2()[0] = q_2_2[code]; quant.getQ2()[1] = q_2_1[code]; cplcoeff = q_2_0[code]; } break; } case 3: { cplcoeff = q_3[ in.getBits(3) ]; break; } case -3: { if ( quant.getQ4Pointer() == 0 ) { cplcoeff = quant.getQ4()[ 0 ]; quant.setQ4Pointer( -1 ); } else { int code = in.getBits( 7 ); quant.setQ4Pointer( 0 ); quant.getQ4()[0] = q_4_1[code]; cplcoeff = q_4_0[code]; } break; } case 4: { cplcoeff = q_5[ in.getBits(4) ]; break; } default: { cplcoeff = bitstream_get_2( bapi ) << (16 - bapi); break; } } if (bapi == 0 ) continue; cplcoeff *= scale_factor[ exp[i] ]; for ( int ch = 0; ch < nfchans; ch++ ) { if ( ((chincpl >> ch ) & 1) != 0 ) { samples[ samplesPointer + i + ch * 256 ] = cplcoeff * cplcoLocal[ch]; } } i++; } } } protected static String show_sample( double s ) { if ( s == 0 ) return "0"; if ( s != 0 ) { while ( (s < 1) && (s > -1) ) { s *= 10; } while ( s > 10 || s < -10 ) { s /= 10; } } String sampleDisplay = Double.toString( s ); if ( sampleDisplay.length() < 4 ) sampleDisplay += "0000"; if ( sampleDisplay.length() > 4 ) sampleDisplay = sampleDisplay.substring( 0, 4 ); return sampleDisplay; } private void downmix_init( int acmod ) { /* Stereo output switch ( acmod ) { case A52_3F: { level /= 1 + clev; break; } case A52_3F1R: { level /= 1 + clev + slev * LEVEL_3DB; break; } case A52_2F2R: { level /= 1 + slev; break; } case A52_3F2R: { level /= 1 + clev + slev; break; } } */ } private void a52_downmix_coeff( double[] coeff, int acmod, double level, double clev, double slev ) { /* TODO - depends on output mode */ /* Stereo output */ switch ( acmod ) { case A52_STEREO: { coeff[ 0 ] = level; coeff[ 1 ] = level; coeff[ 2 ] = level; coeff[ 3 ] = level; coeff[ 4 ] = level; break; } case A52_3F: { coeff[ 0 ] = level; coeff[ 1 ] = level * clev; coeff[ 2 ] = level; coeff[ 3 ] = level; coeff[ 4 ] = level; break; } case A52_2F1R: { coeff[ 0 ] = level; coeff[ 1 ] = level; coeff[ 2 ] = level * slev * LEVEL_3DB; coeff[ 3 ] = level; coeff[ 4 ] = level; break; } case A52_3F1R: { coeff[ 0 ] = level; coeff[ 1 ] = level * clev; coeff[ 2 ] = level; coeff[ 3 ] = level * slev * LEVEL_3DB; coeff[ 4 ] = level; break; } case A52_2F2R: { coeff[ 0 ] = level; coeff[ 1 ] = level; coeff[ 2 ] = level * slev; coeff[ 3 ] = level * slev; coeff[ 4 ] = level; break; } case A52_3F2R: { coeff[ 0 ] = level; coeff[ 1 ] = level * clev; coeff[ 2 ] = level; coeff[ 3 ] = level * slev; coeff[ 4 ] = level * slev; break; } default: { break; } } } /* Read Deltba */ private void parse_deltba( int[] deltba ) throws FFMpegException { for ( int i = 0; i < deltba.length; i++ ) { deltba[i] = 0; } int deltnseg = in.getBits( 3 ); int j = 0; do { j += in.getBits( 5 ); int deltlen = in.getBits( 4 ); int delta = in.getBits( 3 ); delta -= (delta >= 4) ? 3:4; if ( debug ) System.out.println( "j=" + j + " delta=" + delta + " len=" + deltlen ); while ( deltlen-- != 0 ) { deltba[ j++ ] = delta; } } while ( deltnseg-- != 0 ); } /* * Extract exponents */ private void parse_exponents( int expstr, int ngrps, byte exponent, byte[] exponents, int exponentPointer ) throws FFMpegException { if ( debug ) System.out.println( "parse_exponents " + ngrps ); while ( ngrps-- != 0 ) { int exps = in.getBits( 7 ); //System.out.println( "exps=" + exps ); /* Exponent 1 */ exponent += exp_1[ exps ]; if ( (0xff & exponent) > 24 ) throw new AC3Exception( "Exponent too large" ); switch ( expstr ) { case EXP_D45: exponents[ exponentPointer++ ] = exponent; exponents[ exponentPointer++ ] = exponent; case EXP_D25: exponents[ exponentPointer++ ] = exponent; case EXP_D15: exponents[ exponentPointer++ ] = exponent; } /* Exponent 2 */ exponent += exp_2[ exps ]; if ( (0xff & exponent) > 24 ) throw new AC3Exception( "Exponent too large" ); switch ( expstr ) { case EXP_D45: exponents[ exponentPointer++ ] = exponent; exponents[ exponentPointer++ ] = exponent; case EXP_D25: exponents[ exponentPointer++ ] = exponent; case EXP_D15: exponents[ exponentPointer++ ] = exponent; } /* Exponent 3 */ exponent += exp_3[ exps ]; if ( (0xff & exponent) > 24 ) throw new AC3Exception( "Exponent too large" ); switch ( expstr ) { case EXP_D45: exponents[ exponentPointer++ ] = exponent; exponents[ exponentPointer++ ] = exponent; case EXP_D25: exponents[ exponentPointer++ ] = exponent; case EXP_D15: exponents[ exponentPointer++ ] = exponent; } } } /** * Codec management */ public Format[] getSupportedInputFormats() { return new Format[] { new AudioFormat( "ac3" ) }; } public Format[] getSupportedOutputFormats(Format format) { return new Format[] { new AudioFormat( "LINEAR" ) }; } private AudioFormat inputFormat; public Format setInputFormat( Format format ) { inputFormat = (AudioFormat)format; return format; } public Format setOutputFormat( Format format ) { return new AudioFormat("LINEAR", inputFormat.getSampleRate(), inputFormat.getSampleSizeInBits() > 0 ? inputFormat.getSampleSizeInBits() : 16, inputFormat.getChannels(), 0, 1); // endian, int signed } private boolean readSyncBlock = true; public int process( Buffer input, Buffer output ) { output.setFlags( input.getFlags() ); output.setTimeStamp( input.getTimeStamp() ); output.setDuration( input.getDuration() ); try { byte[] data = (byte[])input.getData(); //in.getLength int length = input.getLength(); output.setLength(0); // System.out.println( "Process" ); /* System.out.println( "Parsing packet" ); for ( int i = 0; i < length; i++ ) { System.out.print( Integer.toHexString( data[i] & 0xff ) + " " ); } */ /** * Parse data */ // in = new Mpeg4Stream(); in.addData( data, 0, length ); /** * Until we have no more data */ while ( in.availableBits() > HEADER_LENGTH * 8 ) { /** * Find sync block */ if ( readSyncBlock ) { // System.out.println( "SYNC" ); in.seek(in.getPos() & ~0x7); if ( !a52_syncinfo() ) continue; readSyncBlock = false; } /** * Do we have an entire frame? */ if ( in.availableBits() >= frame_length * 8 ) { int syncPos = in.getPos(); a52_frame(); //a52_dynring() while ( in.getPos() - syncPos < (frame_length - HEADER_LENGTH) * 8 ) { a52_block(); soundOutput.getAudioBuffer( samplesOut, 2, output ); } readSyncBlock = true; } else break; } // System.out.println( "EXITING!!!" ); } catch (Exception e) { // e.printStackTrace(); readSyncBlock = true; in.seek( in.getPos() + in.availableBits() ); return BUFFER_PROCESSED_FAILED; } return BUFFER_PROCESSED_OK; } public void open() { } public void close() { } public void reset() { } public String getName() { return "ac3"; } public Object[] getControls() { return new Object[ 0 ]; } public Object getControl( String type ) { return null; } /** * Implement the Jffmpeg codec interface */ public boolean isCodecAvailable() { return true; } /** * Outofbands video size */ public void setVideoSize( Dimension size ) { } public void setEncoding( String encoding ) { } public void setIsRtp( boolean isRtp ) { } public void setIsTruncated( boolean isTruncated ) { } }