/* * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * by the Xiph.Org Foundation http://www.xiph.org/ */ package org.xiph.libvorbis; import java.util.*; import org.xiph.libogg.*; import static org.xiph.libvorbis.vorbis_constants.integer_constants.*; public class vorbis_dsp_state { int analysisp; public vorbis_info vi; float[][] pcm; // float **pcm // float **pcmret int pcm_storage; public int pcm_current; int pcm_returned; int preextrapolate; int eofflag; int lW; int W; int nW; int centerW; int granulepos; int sequence; int glue_bits; int time_bits; int floor_bits; int res_bits; private_state backend_state; public vorbis_dsp_state() { } private vorbis_look_floor1 floor1_look(vorbis_info_floor1 info) { int sortpointer[] = new int[VIF_POSIT + 2]; vorbis_look_floor1 look = new vorbis_look_floor1(); int i, j, n = 0; look.vi = info; look.n = info.postlist[1]; // we drop each position value in-between already decoded values, // and use linear interpolation to predict each new value past the // edges. The positions are read in the order of the position // list... we precompute the bounding positions in the lookup. Of // course, the neighbors can change (if a position is declined), but // this is an initial mapping for (i = 0; i < info.partitions; i++) n += info.class_dim[info.partitionclass[i]]; n += 2; look.posts = n; // also store a sorted position index for (i = 0; i < n; i++) sortpointer[i] = info.postlist[i]; // sortpointer[i] = info.postlist+i; // static int icomp(const void *a,const void *b) { return(**(int **)a-**(int **)b); } // qsort( sortpointer, n, sizeof(*sortpointer), icomp ); Arrays.sort(sortpointer, 0, n); // points from sort order back to range number for (i = 0; i < n; i++) for (j = 0; j < n; j++) if (sortpointer[i] == info.postlist[j]) look.forward_index[i] = j; // look.forward_index[i] = sortpointer[i]-info.postlist; // points from range order to sorted position for (i = 0; i < n; i++) look.reverse_index[look.forward_index[i]] = i; // we actually need the post values too for (i = 0; i < n; i++) look.sorted_index[i] = info.postlist[look.forward_index[i]]; switch (info.mult) { // quantize values to multiplier spec case 1: look.quant_q = 256; break; case 2: look.quant_q = 128; break; case 3: look.quant_q = 86; break; case 4: look.quant_q = 64; break; } // discover our neighbors for decode where we don't use fit flags (that would push the neighbors outward) for (i = 0; i < n - 2; i++) { int lo = 0; int hi = 1; int lx = 0; int hx = look.n; int currentx = info.postlist[i + 2]; for (j = 0; j < i + 2; j++) { int x = info.postlist[j]; if (x > lx && x < currentx) { lo = j; lx = x; } if (x < hx && x > currentx) { hi = j; hx = x; } } look.loneighbor[i] = lo; look.hineighbor[i] = hi; } return look; } private vorbis_look_residue0 res0_look(vorbis_info_residue0 info) { vorbis_look_residue0 look = new vorbis_look_residue0(); codec_setup_info ci = vi.codec_setup; int j, k, acc = 0; int dim; int maxstage = 0; look.info = info; look.parts = info.partitions; look.fullbooks = ci.fullbooks; // PTR CITY // look.phrasebook = ci.fullbooks+info.groupbook; look.phrasebook = ci.fullbooks[info.groupbook]; dim = look.phrasebook.dim; // look.partbooks = _ogg_calloc(look.parts,sizeof(*look.partbooks)); look.partbooks = new codebook[look.parts][]; for (j = 0; j < look.parts; j++) { int stages = ilog(info.secondstages[j]); if (stages > 0) { if (stages > maxstage) maxstage = stages; // look.partbooks[j]=_ogg_calloc(stages,sizeof(*look.partbooks[j])); look.partbooks[j] = new codebook[stages]; for (k = 0; k < stages; k++) if ((info.secondstages[j] & (1 << k)) > 0) { // look.partbooks[j][k] = ci.fullbooks + info.booklist[acc++]; look.partbooks[j][k] = ci.fullbooks[info.booklist[acc++]]; // #ifdef TRAIN_RES // look.training_data[k][j]=calloc(look.partbooks[j][k].entries,sizeof(***look.training_data)); // #endif } } } look.partvals = new Double(Math.rint(Math.pow(look.parts, dim))).intValue(); look.stages = maxstage; // look.decodemap=_ogg_malloc(look.partvals*sizeof(*look.decodemap)); look.decodemap = new int[look.partvals][dim]; for (j = 0; j < look.partvals; j++) { int val = j; int mult = look.partvals / look.parts; // look.decodemap[j]=_ogg_malloc(dim*sizeof(*look.decodemap[j])); for (k = 0; k < dim; k++) { int deco = val / mult; val -= deco * mult; mult /= look.parts; look.decodemap[j][k] = deco; } } // #ifdef TRAIN_RES // static int train_seq=0; // look.train_seq=train_seq++; // #endif return look; } private boolean _vds_shared_init(boolean encp) { int i; codec_setup_info ci = vi.codec_setup; int hs; if (ci == null) return false; hs = ci.halfrate_flag; backend_state = new private_state(); backend_state.modebits = ilog2(ci.modes); backend_state.transform[0][0].mdct_init(ci.blocksizes[0] >>> hs); backend_state.transform[1][0].mdct_init(ci.blocksizes[1] >>> hs); // Vorbis I uses only window type 0 backend_state.window[0] = ilog2(ci.blocksizes[0]) - 6; backend_state.window[1] = ilog2(ci.blocksizes[1]) - 6; if (encp) { // encode/decode differ here // analysis always needs an fft backend_state.fft_look[0].drft_init(ci.blocksizes[0]); backend_state.fft_look[1].drft_init(ci.blocksizes[1]); // finish the codebooks if (ci.fullbooks == null) { // ci.fullbooks=_ogg_calloc(ci.books,sizeof(*ci.fullbooks)); ci.fullbooks = new codebook[ci.books]; for (i = 0; i < ci.books; i++) { ci.fullbooks[i] = new codebook(); ci.fullbooks[i].vorbis_book_init_encode(ci.book_param[i]); } } // backend_state.psy=_ogg_calloc(ci.psys,sizeof(*backend_state.psy)); backend_state.psy = new vorbis_look_psy[ci.psys]; for (i = 0; i < ci.psys; i++) { backend_state.psy[i] = new vorbis_look_psy(); backend_state.psy[i]._vp_psy_init(ci.psy_param[i], ci.psy_g_param, ci.blocksizes[ci.psy_param[i].blockflag] / 2, vi.rate); } analysisp = 1; } else { // decode the codebooks - not yet implemented System.out.println("_vds_shared_init DECODE not yet implemented"); return false; } pcm_storage = ci.blocksizes[1]; pcm = new float[vi.channels][pcm_storage]; // pcmret = new float[ vi.channels ]; lW = 0; // previous window size W = 0; // current window size // all vector indexes centerW = ci.blocksizes[1] / 2; pcm_current = centerW; // flr - will always be floor1 // residue - look data will always fit in res0 // initialize all the backend lookups // backend_state.flr = _ogg_calloc(ci.floors,sizeof(*backend_state.flr)); // backend_state.residue = _ogg_calloc(ci.residues,sizeof(*backend_state.residue)); backend_state.flr = new vorbis_look_floor1[ci.floors]; backend_state.residue = new vorbis_look_residue0[ci.residues]; // for ( i=0; i < ci.floors; i++ ) // backend_state.flr[i] = _floor_P[ci.floor_type[i]].look( v, ci.floor_param[i] ); for (i = 0; i < ci.floors; i++) { backend_state.flr[i] = floor1_look(ci.floor_param[i]); } // for ( i=0; i < ci.residues; i++ ) // backend_state.residue[i] = _residue_P[ci.residue_type[i]].look( v, ci.residue_param[i] ); for (i = 0; i < ci.residues; i++) { backend_state.residue[i] = res0_look(ci.residue_param[i]); } return true; } public boolean vorbis_analysis_init(vorbis_info _vi) { vi = _vi; if (!_vds_shared_init(true)) return false; backend_state.psy_g_look = new vorbis_look_psy_global(vi); backend_state.ve = new envelope_lookup(vi); backend_state.bms = new bitrate_manager_state(vi); // compressed audio packets start after the headers with sequence number 3 sequence = 3; return true; } public boolean vorbis_analysis_headerout(vorbis_comment vc, ogg_packet op, ogg_packet op_comm, ogg_packet op_code) { oggpack_buffer opb; // first header packet ********************************************** opb = new oggpack_buffer(); if (!opb._vorbis_pack_info(vi)) return false; // build the packet backend_state.header = new byte[opb.oggpack_bytes()]; // memcpy(b->header,opb.buffer,oggpack_bytes(&opb)); System.arraycopy(opb.buffer, 0, backend_state.header, 0, opb.oggpack_bytes()); op.packet = backend_state.header; op.bytes = opb.oggpack_bytes(); op.b_o_s = 1; op.e_o_s = 0; op.granulepos = 0; op.packetno = 0; // second header packet (comments) ********************************** opb = new oggpack_buffer(); if (!opb._vorbis_pack_comment(vc)) return false; // build the packet backend_state.header1 = new byte[opb.oggpack_bytes()]; // memcpy(b->header1,opb.buffer,oggpack_bytes(&opb)); System.arraycopy(opb.buffer, 0, backend_state.header1, 0, opb.oggpack_bytes()); op_comm.packet = backend_state.header1; op_comm.bytes = opb.oggpack_bytes(); op_comm.b_o_s = 0; op_comm.e_o_s = 0; op_comm.granulepos = 0; op_comm.packetno = 1; // third header packet (modes/codebooks) **************************** opb = new oggpack_buffer(); if (!opb._vorbis_pack_books(vi)) return false; // build the packet backend_state.header2 = new byte[opb.oggpack_bytes()]; // memcpy(b->header2,opb.buffer,oggpack_bytes(&opb)); System.arraycopy(opb.buffer, 0, backend_state.header2, 0, opb.oggpack_bytes()); op_code.packet = backend_state.header2; op_code.bytes = opb.oggpack_bytes(); op_code.b_o_s = 0; op_code.e_o_s = 0; op_code.granulepos = 0; op_code.packetno = 2; return true; } public float[][] vorbis_analysis_buffer(int vals) { int i; // free header, header1, header2 backend_state.header = null; backend_state.header1 = null; backend_state.header2 = null; // Do we have enough storage space for the requested buffer? If not, expand the PCM (and envelope) storage if (pcm_current + vals >= pcm_storage) { pcm_storage = pcm_current + vals * 2; // TODO - multiple channels // temp work around for loop - need hash, etc // for unique array name for array reference // this is workaround for only 2 channels for (i = 0; i < vi.channels; i += 2) { // pcm[i] = _ogg_realloc( v->pcm[i], v->pcm_storage*sizeof(*v->pcm[i]) ); float[] temp = new float[pcm_storage]; System.arraycopy(pcm[i], 0, temp, 0, pcm_current); pcm[i] = temp; float[] temp2 = new float[pcm_storage]; System.arraycopy(pcm[i + 1], 0, temp2, 0, pcm_current); pcm[i + 1] = temp2; } } // for now I just return pcm and offset // by pcm_current to alter the .wav buffer // for ( i=0; i < vi.channels; i++ ) // v->pcmret[i] = v->pcm[i] + v->pcm_current; return pcm; } public boolean vorbis_analysis_wrote(int vals) { codec_setup_info ci = vi.codec_setup; if (vals <= 0) { int order = 32; int i; float[] lpc = new float[order]; // if it wasn't done earlier (very short sample) if (preextrapolate <= 0) _preextrapolate_helper(); // We're encoding the end of the stream. Just make sure we have // [at least] a few full blocks of zeroes at the end. // Actually, we don't want zeroes; that could drop a large // amplitude off a cliff, creating spread spectrum noise that will // suck to encode. Extrapolate for the sake of cleanliness. vorbis_analysis_buffer(ci.blocksizes[1] * 3); eofflag = pcm_current; pcm_current += ci.blocksizes[1] * 3; for (i = 0; i < vi.channels; i++) { if (eofflag > order * 2) { // extrapolate with LPC to fill in int n; // make a predictor filter n = eofflag; if (n > ci.blocksizes[1]) n = ci.blocksizes[1]; // vorbis_lpc_from_data(v->pcm[i]+v->eofflag-n,lpc,n,order); vorbis_lpc_from_data(pcm[i], eofflag - n, lpc, n, order); // run the predictor filter // vorbis_lpc_predict(lpc,v->pcm[i]+v->eofflag-order,order, v->pcm[i]+v->eofflag,v->pcm_current-v->eofflag); vorbis_lpc_predict(lpc, pcm[i], eofflag - order, order, eofflag, pcm_current - eofflag); } else { // not enough data to extrapolate (unlikely to happen due to // guarding the overlap, but bulletproof in case that // assumtion goes away). zeroes will do. // memset(v->pcm[i]+v->eofflag,0,(v->pcm_current-v->eofflag)*sizeof(*v->pcm[i])); // for ( int j=eofflag; j < pcm_current; j++ ) { // pcm[i][j] = 0; // } Arrays.fill(pcm[i], eofflag, pcm_current, 0); } } } else { if (pcm_current + vals > pcm_storage) return false; pcm_current += vals; // we may want to reverse extrapolate the beginning of a stream // too... in case we're beginning on a cliff! // clumsy, but simple. It only runs once, so simple is good. if ((preextrapolate <= 0) && pcm_current - centerW > ci.blocksizes[1]) _preextrapolate_helper(); } return true; } public boolean vorbis_bitrate_flushpacket(ogg_packet op) { private_state b = backend_state; bitrate_manager_state bm = b.bms; vorbis_block vb = bm.vb; int choice = PACKETBLOBS / 2; if (vb == null) return false; if (op != null) { vorbis_block_internal vbi = vb.internal; if (vb.vorbis_bitrate_managed()) choice = bm.choice; op.packet = vbi.packetblob[choice].buffer; // oggpack_get_buffer(); op.bytes = vbi.packetblob[choice].oggpack_bytes(); op.b_o_s = 0; op.e_o_s = vb.eofflag; op.granulepos = vb.granulepos; op.packetno = vb.sequence; // for sake of completeness } // bm.vb = 0; bm.vb = null; return true; } public void _preextrapolate_helper() { int i; int order = 32; float[] lpc = new float[order]; float[] work = new float[pcm_current]; int j; preextrapolate = 1; if (pcm_current - centerW > order * 2) { // safety for (i = 0; i < vi.channels; i++) { // need to run the extrapolation in reverse! for (j = 0; j < pcm_current; j++) work[j] = pcm[i][pcm_current - j - 1]; // prime as above vorbis_lpc_from_data(work, 0, lpc, pcm_current - centerW, order); // run the predictor filter vorbis_lpc_predict(lpc, work, pcm_current - centerW - order, order, pcm_current - centerW, centerW); for (j = 0; j < pcm_current; j++) pcm[i][pcm_current - j - 1] = work[j]; } } } public float vorbis_lpc_from_data(float[] data, int offset1, float[] lpci, int n, int m) { // double *aut=alloca(sizeof(*aut)*(m+1)); double[] aut = new double[m + 1]; // double *lpc=alloca(sizeof(*lpc)*(m)); double[] lpc = new double[m]; double error; int i, j; // autocorrelation, p+1 lag coefficients j = m + 1; while (j-- > 0) { double d = 0; // double needed for accumulator depth for (i = j; i < n; i++) d += (double) data[offset1 + i] * data[offset1 + i - j]; aut[j] = d; } // Generate lpc coefficients from autocorr values error = aut[0]; for (i = 0; i < m; i++) { double r = -aut[i + 1]; if (error == 0) { lpci = new float[lpci.length]; return 0; } // Sum up this iteration's reflection coefficient; note that in Vorbis // we don't save it. If anyone wants to recycle this code and needs // reflection coefficients, save the results of 'r' from each iteration. for (j = 0; j < i; j++) r -= lpc[j] * aut[i - j]; r /= error; // Update LPC coefficients and total error lpc[i] = r; for (j = 0; j < i / 2; j++) { double tmp = lpc[j]; lpc[j] += r * lpc[i - 1 - j]; lpc[i - 1 - j] += r * tmp; } if (i % 2 > 0) lpc[j] += lpc[j] * r; error *= 1.f - r * r; } for (j = 0; j < m; j++) lpci[j] = (float) lpc[j]; // we need the error value to know how big an impulse to hit the filter with later return new Double(error).floatValue(); } public void vorbis_lpc_predict(float[] coeff, float[] prime, int offset1, int m, int offset2, int n) { // in: coeff[0...m-1] LPC coefficients // prime[0...m-1] initial values (allocated size of n+m-1) // out: data[0...n-1] data samples int i, j, o, p; float y; float[] work = new float[m + n]; if (prime == null) for (i = 0; i < m; i++) work[i] = 0.f; else for (i = 0; i < m; i++) work[i] = prime[offset1 + i]; for (i = 0; i < n; i++) { y = 0; o = i; p = m; for (j = 0; j < m; j++) y -= work[o++] * coeff[--p]; prime[offset2 + i] = work[o] = y; } } public int _ve_envelope_search() { codec_setup_info ci = vi.codec_setup; vorbis_info_psy_global gi = ci.psy_g_param; envelope_lookup ve = backend_state.ve; int i, j; int first = ve.current / ve.searchstep; int last = pcm_current / ve.searchstep - VE_WIN; if (first < 0) first = 0; // make sure we have enough storage to match the PCM if (last + VE_WIN + VE_POST > ve.storage) { ve.storage = last + VE_WIN + VE_POST; // be sure // ve.mark = _ogg_realloc(ve.mark,ve.storage*sizeof(*ve.mark)); int[] temp = new int[ve.storage]; System.arraycopy(ve.mark, 0, temp, 0, ve.mark.length); ve.mark = temp; } for (j = first; j < last; j++) { int ret = 0; ve.stretch++; if (ve.stretch > VE_MAXSTRETCH * 2) ve.stretch = VE_MAXSTRETCH * 2; for (i = 0; i < ve.ch; i++) { // float *pcm = pcm[i] + ve.searchstep*(j); // ret |= _ve_amp( ve, gi, _pcm, ve.band, ve.filter+i*VE_BANDS, j ); ret |= ve._ve_amp(gi, pcm[i], ve.searchstep * (j), ve.band, ve.filter, i * VE_BANDS, j); } ve.mark[j + VE_POST] = 0; if ((ret & 1) > 0) { ve.mark[j] = 1; ve.mark[j + 1] = 1; } if ((ret & 2) > 0) { ve.mark[j] = 1; if (j > 0) ve.mark[j - 1] = 1; } if ((ret & 4) > 0) ve.stretch = -1; } // ve.out( "", null ); ve.current = last * ve.searchstep; // OOP local variable rename int centerW_local = centerW; int testW = centerW_local + ci.blocksizes[W] / 4 + ci.blocksizes[1] / 2 + ci.blocksizes[0] / 4; j = ve.cursor; while (j < ve.current - (ve.searchstep)) { // account for postecho working back one window if (j >= testW) return 1; ve.cursor = j; if (ve.mark[j / ve.searchstep] > 0) { if (j > centerW_local) { ve.curmark = j; if (j >= testW) return 1; return 0; } } j += ve.searchstep; } return -1; } public boolean _ve_envelope_mark() { envelope_lookup ve = backend_state.ve; codec_setup_info ci = vi.codec_setup; // local OOP variable rename centerW int centerW_local = centerW; int beginW = centerW_local - ci.blocksizes[W] / 4; int endW = centerW_local + ci.blocksizes[W] / 4; if (W > 0) { beginW -= ci.blocksizes[lW] / 4; endW += ci.blocksizes[nW] / 4; } else { beginW -= ci.blocksizes[0] / 4; endW += ci.blocksizes[0] / 4; } if (ve.curmark >= beginW && ve.curmark < endW) return true; { int first = beginW / ve.searchstep; int last = endW / ve.searchstep; int i; for (i = first; i < last; i++) if (ve.mark[i] > 0) return true; } return false; } public float _vp_ampmax_decay(float amp) { codec_setup_info ci = vi.codec_setup; vorbis_info_psy_global gi = ci.psy_g_param; int n = ci.blocksizes[W] / 2; float secs = (float) n / vi.rate; amp += secs * gi.ampmax_att_per_sec; if (amp < -9999) amp = -9999; return amp; } }