/* * 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 org.xiph.libogg.*; import static org.xiph.libvorbis.vorbis_constants.integer_constants.*; public class vorbis_block { // necessary stream state for linking to the framing abstraction float[][] pcm; // float **pcm // this is a pointer into local storage int pcm_offset; oggpack_buffer opb; int lW; // long int W; // long int nW; // long int pcmend; int mode; int eofflag; int granulepos; // ogg_int64_t int sequence; // ogg_int64_t vorbis_dsp_state vd; // *vd // For read-only access of configuration // local storage to avoid remallocing; it's up to the mapping to structure it Object[] localstore; int localtop; // long int localalloc; // long int totaluse; // long alloc_chain reap; // bitmetrics for the frame int glue_bits; // long int time_bits; // long int floor_bits; // long int res_bits; // long vorbis_block_internal internal; // void *internal; // vorbis_window used to hold window arrays and functions vorbis_window window; public vorbis_block(vorbis_dsp_state v) { vd = v; localalloc = 0; opb = new oggpack_buffer(); if (v.analysisp > 0) { internal = new vorbis_block_internal(); internal.ampmax = -9999.0f; for (int i = 0; i < PACKETBLOBS; i++) { if (i == PACKETBLOBS / 2) { internal.packetblob[i] = opb; } else { internal.packetblob[i] = new oggpack_buffer(); } } } window = new vorbis_window(); } public int _vorbis_block_alloc(int bytes) { bytes = (bytes + (WORD_ALIGN - 1)) & ~(WORD_ALIGN - 1); if (bytes + localtop > localalloc) { // can't just _ogg_realloc... there are outstanding pointers if (localstore != null) { alloc_chain link = new alloc_chain(); totaluse += localtop; link.next = reap; // link.ptr = localstore; link.ptr = new Object[localtop]; System.arraycopy(localstore, 0, link.ptr, 0, localtop); reap = link; } // highly conservative localalloc = bytes; // localstore = _ogg_malloc(vb->localalloc); localstore = new Object[localalloc]; localtop = 0; } // void *ret=(void *)(((char *)vb->localstore)+vb->localtop); int ret = localtop; localtop += bytes; return ret; } public void _vorbis_block_ripcord() { // reap the chain while (reap != null) { alloc_chain next = reap.next; // _ogg_free(reap->ptr); // memset(reap,0,sizeof(*reap)); // _ogg_free(reap); reap = null; reap = next; } // consolidate storage if (totaluse > 0) { // localstore =_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); Object[] temp = new Object[totaluse + localalloc]; System.arraycopy(localstore, 0, temp, 0, localalloc); localstore = temp; localalloc += totaluse; totaluse = 0; } // pull the ripcord localtop = 0; reap = null; } public boolean vorbis_analysis_blockout(vorbis_dsp_state v) { int i; codec_setup_info ci = v.vi.codec_setup; private_state b = v.backend_state; vorbis_look_psy_global g = b.psy_g_look; int beginW = v.centerW - ci.blocksizes[v.W] / 2; int centerNext; vorbis_block_internal vbi = internal; // check to see if we're started... if (v.preextrapolate <= 0) return false; // check to see if we're done... if (v.eofflag == -1) return false; // By our invariant, we have lW, W and centerW set. Search for // the next boundary so we can determine nW (the next window size) // which lets us compute the shape of the current block's window // we do an envelope search even on a single blocksize; we may still be throwing // more bits at impulses, and envelope search handles marking impulses too int bp = v._ve_envelope_search(); if (bp == -1) { if (v.eofflag == 0) return false; // not enough data currently to search for a full long block v.nW = 0; } else { if (ci.blocksizes[0] == ci.blocksizes[1]) v.nW = 0; else v.nW = bp; } centerNext = v.centerW + ci.blocksizes[v.W] / 4 + ci.blocksizes[v.nW] / 4; // center of next block + next block maximum right side. int blockbound = centerNext + ci.blocksizes[v.nW] / 2; // not enough data yet; although this check is // less strict that the _ve_envelope_search, the // search is not run if we only use one block size if (v.pcm_current < blockbound) return false; // fill in the block. Note that for a short window, lW and nW are *short* regardless of actual settings in the stream _vorbis_block_ripcord(); lW = v.lW; W = v.W; nW = v.nW; if (v.W > 0) { if ((v.lW <= 0) || (v.nW <= 0)) { vbi.blocktype = BLOCKTYPE_TRANSITION; } else { vbi.blocktype = BLOCKTYPE_LONG; } } else { if (v._ve_envelope_mark()) { vbi.blocktype = BLOCKTYPE_IMPULSE; } else { vbi.blocktype = BLOCKTYPE_PADDING; } } vd = v; sequence = v.sequence++; granulepos = v.granulepos; pcmend = ci.blocksizes[v.W]; // copy the vectors; this uses the local storage in vb // this tracks 'strongest peak' for later psychoacoustics // moved to the global psy state; clean this mess up if (vbi.ampmax > g.ampmax) g.ampmax = vbi.ampmax; g.ampmax = v._vp_ampmax_decay(g.ampmax); vbi.ampmax = g.ampmax; // TODO - CRITICAL - memory arrays // we have some duplicate memory arrays probably increasing memory footprint // would need to work on offset variables for pcmdelay // pcm = _vorbis_block_alloc(vb,sizeof(*pcm)*v.vi.channels); pcm = new float[v.vi.channels][]; // vbi.pcmdelay = _vorbis_block_alloc(vb,sizeof(*vbi.pcmdelay)*v.vi.channels); vbi.pcmdelay = new float[v.vi.channels][]; for (i = 0; i < v.vi.channels; i++) { // vbi.pcmdelay[i] = _vorbis_block_alloc(vb,(pcmend+beginW)*sizeof(*vbi.pcmdelay[i])); vbi.pcmdelay[i] = new float[pcmend + beginW]; // memcpy( vbi.pcmdelay[i], v.pcm[i], (pcmend+beginW)*sizeof(*vbi.pcmdelay[i]) ); System.arraycopy(v.pcm[i], 0, vbi.pcmdelay[i], 0, pcmend + beginW); // pcm[i] = vbi.pcmdelay[i]+beginW; pcm[i] = new float[vbi.pcmdelay[i].length - beginW]; System.arraycopy(vbi.pcmdelay[i], beginW, pcm[i], 0, vbi.pcmdelay[i].length - beginW); } // handle eof detection: // eof==0 means that we've not yet received EOF // eof>0 marks the last 'real' sample in pcm[] // eof<0 'no more to do'; doesn't get here */ if (v.eofflag > 0) { if (v.centerW >= v.eofflag) { v.eofflag = -1; eofflag = 1; return true; } } // advance storage vectors and clean up int new_centerNext = ci.blocksizes[1] / 2; int movementW = centerNext - new_centerNext; if (movementW > 0) { b.ve._ve_envelope_shift(movementW); v.pcm_current -= movementW; for (i = 0; i < v.vi.channels; i++) { // memmove(v.pcm[i],v.pcm[i]+movementW,v.pcm_current*sizeof(*v.pcm[i])); System.arraycopy(v.pcm[i], movementW, v.pcm[i], 0, v.pcm_current); } v.lW = v.W; v.W = v.nW; v.centerW = new_centerNext; if (v.eofflag > 0) { v.eofflag -= movementW; if (v.eofflag <= 0) v.eofflag = -1; // do not add padding to end of stream! if (v.centerW >= v.eofflag) { v.granulepos += movementW - (v.centerW - v.eofflag); } else { v.granulepos += movementW; } } else { v.granulepos += movementW; } } return true; } public boolean vorbis_bitrate_managed() { // if(bm->queue_binned)return(1); if (vd.backend_state.bms.managed > 0) return true; return false; } public boolean vorbis_analysis(ogg_packet op) { glue_bits = 0; time_bits = 0; floor_bits = 0; res_bits = 0; // first things first. Make sure encode is ready for (int i = 0; i < PACKETBLOBS; i++) internal.packetblob[i].oggpack_reset(); // we only have one mapping type (0), and we let the mapping code itself // figure out what soft mode to use. This allows easier bitrate management mapping0_forward(); if (op != null) { if (vorbis_bitrate_managed()) return false; // The app is using a bitmanaged mode... but not using the bitrate management interface. op.packet = opb.buffer; op.bytes = opb.oggpack_bytes(); op.b_o_s = 0; op.e_o_s = eofflag; op.granulepos = granulepos; op.packetno = sequence; // for sake of completeness } return true; } // the floor has already been filtered to only include relevant sections static int accumulate_fit(float[] flr, int mdct, int x0, int x1, lsfit_acc a, int n, vorbis_info_floor1 info) { int i; // int quantized = vorbis_dBquant(flr[x0]); int xa = 0, ya = 0, x2a = 0, y2a = 0, xya = 0, na = 0, xb = 0, yb = 0, x2b = 0, y2b = 0, xyb = 0, nb = 0; a.x0 = x0; a.x1 = x1; if (x1 >= n) x1 = n - 1; for (i = x0; i <= x1; i++) { int quantized = vorbis_dBquant(flr[i]); if (quantized > 0) { if (flr[mdct + i] + info.twofitatten >= flr[i]) { xa += i; ya += quantized; x2a += i * i; y2a += quantized * quantized; xya += i * quantized; na++; } else { xb += i; yb += quantized; x2b += i * i; y2b += quantized * quantized; xyb += i * quantized; nb++; } } } xb += xa; yb += ya; x2b += x2a; y2b += y2a; xyb += xya; nb += na; // weight toward the actually used frequencies if we meet the threshhold int weight = new Float(nb * info.twofitweight / (na + 1)).intValue(); a.xa = xa * weight + xb; a.ya = ya * weight + yb; a.x2a = x2a * weight + x2b; a.y2a = y2a * weight + y2b; a.xya = xya * weight + xyb; a.an = na * weight + nb; return na; } static int[] fit_line(lsfit_acc[] a, int offset, int fits, int y0, int y1) { int x = 0, y = 0, x2 = 0, y2 = 0, xy = 0, an = 0, i; int x0 = a[offset].x0; int x1 = a[offset + fits - 1].x1; for (i = 0; i < fits; i++) { x += a[offset + i].xa; y += a[offset + i].ya; x2 += a[offset + i].x2a; y2 += a[offset + i].y2a; xy += a[offset + i].xya; an += a[offset + i].an; } // if (*y0 >= 0 ) { if (y0 >= 0) { x += x0; y += y0; // y += *y0; x2 += x0 * x0; y2 += y0 * y0; // y2 += *y0 * *y0; xy += y0 * x0; // xy += *y0 * x0; an++; } // if(*y1>=0){ if (y1 >= 0) { x += x1; y += y1; // y+= *y1; x2 += x1 * x1; y2 += y1 * y1; // y2+= *y1 * *y1; xy += y1 * x1; // xy+= *y1 * x1; an++; } if (an > 0) { // need 64 bit multiplies, which C doesn't give portably as int double fx = x; double fy = y; double fx2 = x2; double fxy = xy; double denom = 1. / (an * fx2 - fx * fx); double a_local = (fy * fx2 - fxy * fx) * denom; double b = (an * fxy - fx * fy) * denom; y0 = (int) Math.rint(a_local + b * x0); // *y0=rint(a_local+b*x0); y1 = (int) Math.rint(a_local + b * x1); // *y1=rint(a_local+b*x1); // limit to our range! if (y0 > 1023) // if(*y0>1023)*y0=1023; y0 = 1023; if (y1 > 1023) // if(*y1>1023)*y1=1023; y1 = 1023; if (y0 < 0) // if(*y0<0)*y0=0; y0 = 0; if (y1 < 0) // if(*y1<0)*y1=0; y1 = 0; } else { y0 = 0; // *y0=0; y1 = 0; // *y1=0; } int[] return_buffer = {y0, y1}; return return_buffer; } static int inspect_error(int x0, int x1, int y0, int y1, float[] mask, int mdct, vorbis_info_floor1 info) { int dy = y1 - y0; int adx = x1 - x0; int ady = Math.abs(dy); int base = dy / adx; int sy = (dy < 0 ? base - 1 : base + 1); int x = x0; int y = y0; int err = 0; int val = vorbis_dBquant(mask[x]); int mse = 0; int n = 0; ady -= Math.abs(base * adx); mse = (y - val); mse *= mse; n++; if (mask[mdct + x] + info.twofitatten >= mask[x]) { if (y + info.maxover < val) return (1); if (y - info.maxunder > val) return (1); } while (++x < x1) { err = err + ady; if (err >= adx) { err -= adx; y += sy; } else { y += base; } val = vorbis_dBquant(mask[x]); mse += ((y - val) * (y - val)); n++; if (mask[mdct + x] + info.twofitatten >= mask[x]) { if (val > 0) { if (y + info.maxover < val) return (1); if (y - info.maxunder > val) return (1); } } } if (info.maxover * info.maxover / n > info.maxerr) return (0); if (info.maxunder * info.maxunder / n > info.maxerr) return (0); if (mse / n > info.maxerr) return (1); return (0); } static int post_Y(int[] A, int[] B, int pos) { if (A[pos] < 0) return B[pos]; if (B[pos] < 0) return A[pos]; return (A[pos] + B[pos]) >>> 1; } public int[] floor1_fit(vorbis_look_floor1 look, int logmdct, float[] logmask) { int i, j; vorbis_info_floor1 info = look.vi; int n = look.n; int posts = look.posts; int nonzero = 0; // lsfit_acc fits[VIF_POSIT+1]; // int fit_valueA[VIF_POSIT+2]; // index by range list position // int fit_valueB[VIF_POSIT+2]; // index by range list position // int loneighbor[VIF_POSIT+2]; // sorted index of range list position (+2) // int hineighbor[VIF_POSIT+2]; // int *output=NULL; // int memo[VIF_POSIT+2]; lsfit_acc[] fits = new lsfit_acc[VIF_POSIT + 1]; for (i = 0; i < VIF_POSIT + 1; i++) fits[i] = new lsfit_acc(); int[] fit_valueA = new int[VIF_POSIT + 2]; // index by range list position int[] fit_valueB = new int[VIF_POSIT + 2]; // index by range list position int[] loneighbor = new int[VIF_POSIT + 2]; // sorted index of range list position (+2) int[] hineighbor = new int[VIF_POSIT + 2]; int[] output = null; int[] memo = new int[VIF_POSIT + 2]; int[] return_buffer = new int[2]; for (i = 0; i < posts; i++) fit_valueA[i] = -200; // mark all unused for (i = 0; i < posts; i++) fit_valueB[i] = -200; // mark all unused for (i = 0; i < posts; i++) loneighbor[i] = 0; // 0 for the implicit 0 post for (i = 0; i < posts; i++) hineighbor[i] = 1; // 1 for the implicit post at n for (i = 0; i < posts; i++) memo[i] = -1; // no neighbor yet // quantize the relevant floor points and collect them into line fit // structures (one per minimal division) at the same time if (posts == 0) { nonzero += accumulate_fit(logmask, logmdct, 0, n, fits[0], n, info); } else { for (i = 0; i < posts - 1; i++) nonzero += accumulate_fit(logmask, logmdct, look.sorted_index[i], look.sorted_index[i + 1], fits[i], n, info); } if (nonzero > 0) { // start by fitting the implicit base case.... int y0 = -200; int y1 = -200; return_buffer = fit_line(fits, 0, posts - 1, y0, y1); y0 = return_buffer[0]; y1 = return_buffer[1]; fit_valueA[0] = y0; fit_valueB[0] = y0; fit_valueB[1] = y1; fit_valueA[1] = y1; // Non degenerate case // start progressive splitting. This is a greedy, non-optimal // algorithm, but simple and close enough to the best answer. for (i = 2; i < posts; i++) { int sortpos = look.reverse_index[i]; int ln = loneighbor[sortpos]; int hn = hineighbor[sortpos]; // eliminate repeat searches of a particular range with a memo if (memo[ln] != hn) { // haven't performed this error search yet int lsortpos = look.reverse_index[ln]; int hsortpos = look.reverse_index[hn]; memo[ln] = hn; // A note: we want to bound/minimize *local*, not global, error int lx = info.postlist[ln]; int hx = info.postlist[hn]; int ly = post_Y(fit_valueA, fit_valueB, ln); int hy = post_Y(fit_valueA, fit_valueB, hn); if (ly == -1 || hy == -1) { System.out.println("ERROR: We want to bound/minimize *local*, not global"); System.exit(1); } if (inspect_error(lx, hx, ly, hy, logmask, logmdct, info) > 0) { // outside error bounds/begin search area. Split it. int ly0 = -200; int ly1 = -200; int hy0 = -200; int hy1 = -200; return_buffer = fit_line(fits, lsortpos, sortpos - lsortpos, ly0, ly1); ly0 = return_buffer[0]; ly1 = return_buffer[1]; return_buffer = fit_line(fits, sortpos, hsortpos - sortpos, hy0, hy1); hy0 = return_buffer[0]; hy1 = return_buffer[1]; // store new edge values fit_valueB[ln] = ly0; if (ln == 0) fit_valueA[ln] = ly0; fit_valueA[i] = ly1; fit_valueB[i] = hy0; fit_valueA[hn] = hy1; if (hn == 1) fit_valueB[hn] = hy1; if (ly1 >= 0 || hy0 >= 0) { // store new neighbor values for (j = sortpos - 1; j >= 0; j--) if (hineighbor[j] == hn) hineighbor[j] = i; else break; for (j = sortpos + 1; j < posts; j++) if (loneighbor[j] == ln) loneighbor[j] = i; else break; } } else { fit_valueA[i] = -200; fit_valueB[i] = -200; } } } // output = _vorbis_block_alloc(vb,sizeof(*output)*posts); output = new int[posts]; output[0] = post_Y(fit_valueA, fit_valueB, 0); output[1] = post_Y(fit_valueA, fit_valueB, 1); // fill in posts marked as not using a fit; we will zero back out to 'unused' when encoding // them so long as curve interpolation doesn't force them into use for (i = 2; i < posts; i++) { int ln = look.loneighbor[i - 2]; int hn = look.hineighbor[i - 2]; int x0 = info.postlist[ln]; int x1 = info.postlist[hn]; y0 = output[ln]; y1 = output[hn]; int predicted = render_point(x0, x1, y0, y1, info.postlist[i]); int vx = post_Y(fit_valueA, fit_valueB, i); if (vx >= 0 && predicted != vx) { output[i] = vx; } else { output[i] = predicted | 0x8000; } } } return output; } static int[] floor1_interpolate_fit(vorbis_look_floor1 look, int[] A, int[] B, int del) { int i; int posts = look.posts; int[] output = null; if (A != null && B != null) { // output=_vorbis_block_alloc(vb,sizeof(*output)*posts); output = new int[posts]; for (i = 0; i < posts; i++) { output[i] = ((65536 - del) * (A[i] & 0x7fff) + del * (B[i] & 0x7fff) + 32768) >> 16; boolean aTrue = (A[i] & 0x8000) > 0; boolean bTrue = (B[i] & 0x8000) > 0; if (aTrue && bTrue) output[i] |= 0x8000; } } return (output); } static float dipole_hypot(float a, float b) { if (a > 0.) { if (b > 0.) return new Double(Math.sqrt(a * a + b * b)).floatValue(); if (a > -b) return new Double(Math.sqrt(a * a - b * b)).floatValue(); return new Double(-Math.sqrt(b * b - a * a)).floatValue(); } if (b < 0.) return new Double(-Math.sqrt(a * a + b * b)).floatValue(); if (-a > b) return new Double(-Math.sqrt(a * a - b * b)).floatValue(); return new Double(Math.sqrt(b * b - a * a)).floatValue(); } static float round_hypot(float a, float b) { if (a > 0.) { if (b > 0.) return new Double(Math.sqrt(a * a + b * b)).floatValue(); if (a > -b) return new Double(Math.sqrt(a * a + b * b)).floatValue(); return new Double(-Math.sqrt(b * b + a * a)).floatValue(); } if (b < 0.) return new Double(-Math.sqrt(a * a + b * b)).floatValue(); if (-a > b) return new Double(-Math.sqrt(a * a + b * b)).floatValue(); return new Double(Math.sqrt(b * b + a * a)).floatValue(); } private float[][] _vp_quantize_couple_memo(vorbis_info_psy_global g, vorbis_look_psy p, vorbis_info_mapping0 vi, float[][] mdct) { int i, j; int n = p.n; // float **ret=_vorbis_block_alloc(vb,vi->coupling_steps*sizeof(*ret)); float[][] ret = new float[vi.coupling_steps][n]; int limit = g.coupling_pointlimit[p.vi.blockflag][PACKETBLOBS / 2]; for (i = 0; i < vi.coupling_steps; i++) { float[] mdctM = mdct[vi.coupling_mag[i]]; float[] mdctA = mdct[vi.coupling_ang[i]]; // ret[i] = _vorbis_block_alloc(vb,n*sizeof(**ret)); for (j = 0; j < limit; j++) ret[i][j] = dipole_hypot(mdctM[j], mdctA[j]); for (; j < n; j++) ret[i][j] = round_hypot(mdctM[j], mdctA[j]); } return ret; } private static void SORT4(int o, float[] data, int offset, int[] n) { if (Math.abs(data[offset + o + 2]) >= Math.abs(data[offset + o + 3])) if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 1])) if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 2])) if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 2])) { n[o] = o + 0; n[o + 1] = o + 1; n[o + 2] = o + 2; n[o + 3] = o + 3; } else if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 3])) { n[o] = o + 0; n[o + 1] = o + 2; n[o + 2] = o + 1; n[o + 3] = o + 3; } else { n[o] = o + 0; n[o + 1] = o + 2; n[o + 2] = o + 3; n[o + 3] = o + 1; } else if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 3])) if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 3])) { n[o] = o + 2; n[o + 1] = o + 0; n[o + 2] = o + 1; n[o + 3] = o + 3; } else { n[o] = o + 2; n[o + 1] = o + 0; n[o + 2] = o + 3; n[o + 3] = o + 1; } else { n[o] = o + 2; n[o + 1] = o + 3; n[o + 2] = o + 0; n[o + 3] = o + 1; } else if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 2])) if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 2])) { n[o] = o + 1; n[o + 1] = o + 0; n[o + 2] = o + 2; n[o + 3] = o + 3; } else if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 3])) { n[o] = o + 1; n[o + 1] = o + 2; n[o + 2] = o + 0; n[o + 3] = o + 3; } else { n[o] = o + 1; n[o + 1] = o + 2; n[o + 2] = o + 3; n[o + 3] = o + 0; } else if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 3])) if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 3])) { n[o] = o + 2; n[o + 1] = o + 1; n[o + 2] = o + 0; n[o + 3] = o + 3; } else { n[o] = o + 2; n[o + 1] = o + 1; n[o + 2] = o + 3; n[o + 3] = o + 0; } else { n[o] = o + 2; n[o + 1] = o + 3; n[o + 2] = o + 1; n[o + 3] = o + 0; } else if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 1])) if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 3])) if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 3])) { n[o] = o + 0; n[o + 1] = o + 1; n[o + 2] = o + 3; n[o + 3] = o + 2; } else if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 2])) { n[o] = o + 0; n[o + 1] = o + 3; n[o + 2] = o + 1; n[o + 3] = o + 2; } else { n[o] = o + 0; n[o + 1] = o + 3; n[o + 2] = o + 2; n[o + 3] = o + 1; } else if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 2])) if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 2])) { n[o] = o + 3; n[o + 1] = o + 0; n[o + 2] = o + 1; n[o + 3] = o + 2; } else { n[o] = o + 3; n[o + 1] = o + 0; n[o + 2] = o + 2; n[o + 3] = o + 1; } else { n[o] = o + 3; n[o + 1] = o + 2; n[o + 2] = o + 0; n[o + 3] = o + 1; } else if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 3])) if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 3])) { n[o] = o + 1; n[o + 1] = o + 0; n[o + 2] = o + 3; n[o + 3] = o + 2; } else if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 2])) { n[o] = o + 1; n[o + 1] = o + 3; n[o + 2] = o + 0; n[o + 3] = o + 2; } else { n[o] = o + 1; n[o + 1] = o + 3; n[o + 2] = o + 2; n[o + 3] = o + 0; } else if (Math.abs(data[offset + o + 1]) >= Math.abs(data[offset + o + 2])) if (Math.abs(data[offset + o + 0]) >= Math.abs(data[offset + o + 2])) { n[o] = o + 3; n[o + 1] = o + 1; n[o + 2] = o + 0; n[o + 3] = o + 2; } else { n[o] = o + 3; n[o + 1] = o + 1; n[o + 2] = o + 2; n[o + 3] = o + 0; } else { n[o] = o + 3; n[o + 1] = o + 2; n[o + 2] = o + 1; n[o + 3] = o + 0; } } static void sortindex_fix8(int[] index, int ioff, float[] data, int offset) { int i, j, k; int[] n = new int[8]; // index+=offset; // data+=offset; SORT4(0, data, offset, n); SORT4(4, data, offset, n); j = 0; k = 4; for (i = 0; i < 8; i++) index[ioff + offset + i] = n[((k >= 8) || (j < 4) && (Math.abs(data[offset + 0 + n[j]]) >= Math.abs(data[offset + 0 + n[k]])) ? j++ : k++)] + offset; } static void sortindex_fix32(int[] index, int ioff, float[] data, int offset) { int i, j, k; int[] n = new int[32]; for (i = 0; i < 32; i += 8) sortindex_fix8(index, ioff, data, offset + i); ioff += offset; for (i = j = 0, k = 8; i < 16; i++) n[i] = index[ioff + ((k >= 16) || (j < 8) && (Math.abs(data[0 + index[ioff + j]]) >= Math.abs(data[0 + index[ioff + k]])) ? j++ : k++)]; for (i = j = 16, k = 24; i < 32; i++) n[i] = index[ioff + ((k >= 32) || (j < 24) && (Math.abs(data[0 + index[ioff + j]]) >= Math.abs(data[0 + index[ioff + k]])) ? j++ : k++)]; for (i = j = 0, k = 16; i < 32; i++) index[ioff + i] = n[((k >= 32) || (j < 16) && (Math.abs(data[0 + n[j]]) >= Math.abs(data[0 + n[k]])) ? j++ : k++)]; } static void sortindex_shellsort(int[] index, int ioff, float[] data, int offset, int count) { int gap, pos, left; // int right; int i, j; ioff += offset; for (i = 0; i < count; i++) index[ioff + i] = i + offset; gap = 1; while (gap <= count) gap = gap * 3 + 1; gap /= 3; if (gap >= 4) gap /= 3; while (gap > 0) { for (pos = gap; pos < count; pos++) { for (left = pos - gap; left >= 0; left -= gap) { i = index[ioff + left]; j = index[ioff + left + gap]; if (!(Math.abs(data[0 + i]) >= Math.abs(data[0 + j]))) { index[ioff + left] = j; index[ioff + left + gap] = i; } else break; } } gap /= 3; } } static void sortindex(int[] index, int ioff, float[] data, int offset, int count) { if (count == 8) sortindex_fix8(index, ioff, data, offset); else if (count == 32) sortindex_fix32(index, ioff, data, offset); else sortindex_shellsort(index, ioff, data, offset, count); } private int[][] _vp_quantize_couple_sort(vorbis_look_psy p, vorbis_info_mapping0 vi, float[][] mags) { if (p.vi.normal_point_p > 0) { int i, j; int n = p.n; // int **ret=_vorbis_block_alloc(vb,vi->coupling_steps*sizeof(*ret)); int[][] ret = new int[vi.coupling_steps][n]; int partition = p.vi.normal_partition; // float **work=alloca(sizeof(*work)*partition); // float[][] work = new float[ partition ][]; for (i = 0; i < vi.coupling_steps; i++) { // ret[i]=_vorbis_block_alloc(vb,n*sizeof(**ret)); for (j = 0; j < n; j += partition) { /* for ( k=0; k < partition; k++ ) { // work[k] = mags[i]+k+j; work[k] = new float[ mags[i].length - k+j ]; System.arraycopy(mags[i], k+j, work[k], 0, mags[i].length - k+j ); } // qsort(work,partition,sizeof(*work),apsort); Arrays.sort( work ); // for( k=0; k < partition; k++ ) // ret[i][k+j] = work[k]-mags[i]; for ( k=0; k < partition; k++) ret[i][k+j] = work[k] - mags[i]; */ sortindex(ret[i], 0, mags[i], j, partition); } } return (ret); } return null; } private void _vp_noise_normalize_sort(vorbis_look_psy p, float[] magnitudes, int[] sortedindex) { int j; int n = p.n; vorbis_info_psy vi = p.vi; int partition = vi.normal_partition; // float **work=alloca(sizeof(*work)*partition); int start = vi.normal_start; for (j = start; j < n; j += partition) { if (j + partition > n) partition = n - j; // for(i=0;i<partition;i++)work[i]=magnitudes+i+j; // qsort(work,partition,sizeof(*work),apsort); // for(i=0;i<partition;i++){ // sortedindex[i+j-start]=work[i]-magnitudes; // } sortindex(sortedindex, -start, magnitudes, j, partition); } } private void hf_reduction(vorbis_info_psy_global g, vorbis_look_psy p, vorbis_info_mapping0 vi, float[][] mdct) { int i, j; int n = p.n; int de = (int) (0.3 * p.m_val); int limit = g.coupling_pointlimit[p.vi.blockflag][PACKETBLOBS / 2]; // int start = p.vi.normal_start; for (i = 0; i < vi.coupling_steps; i++) { // for(j=start; j<limit; j++){} // ??? for (j = limit; j < n; j++) mdct[i][j] *= (1.0 - de * ((float) (j - limit) / (float) (n - limit))); } } private void _vp_remove_floor(vorbis_look_psy p, float[] mdct, int[] codedflr, float[] residue, int sliding_lowpass) { int i; int n = p.n; if (sliding_lowpass > n) sliding_lowpass = n; for (i = 0; i < sliding_lowpass; i++) residue[i] = mdct[i] * FLOOR1_fromdB_INV_LOOKUP[codedflr[i]]; for (; i < n; i++) residue[i] = 0.f; } private void _vp_noise_normalize(vorbis_look_psy p, float[] in, int out, int[] sortedindex) { // int flag = 0; int i; int j = 0; int n = p.n; vorbis_info_psy vi = p.vi; int partition = vi.normal_partition; int start = vi.normal_start; if (start > n) start = n; if (vi.normal_channel_p > 0) { for (; j < start; j++) in[out + j] = (float) Math.rint(in[j]); for (; j + partition <= n; j += partition) { float acc = 0.f; int k; for (i = j; i < j + partition; i++) acc += in[i] * in[i]; for (i = 0; i < partition; i++) { k = sortedindex[i + j - start]; if (in[k] * in[k] >= .25f) { in[out + k] = (float) Math.rint(in[k]); acc -= in[k] * in[k]; // flag=1; } else { if (acc < vi.normal_thresh) break; in[out + k] = unitnorm(in[k]); acc -= 1.; } } for (; i < partition; i++) { k = sortedindex[i + j - start]; in[out + k] = 0.f; } } } for (; j < n; j++) in[out + j] = (float) Math.rint(in[j]); } static float[] couple_lossless(float A, float B, float qA, float qB) { int test1 = ((Math.abs(qA) > Math.abs(qB)) ? 1 : 0); test1 -= ((Math.abs(qA) < Math.abs(qB)) ? 1 : 0); if (test1 <= 0) test1 = ((((Math.abs(A) > Math.abs(B))) ? 1 : 0) << 1) - 1; if (test1 == 1) { qB = (qA > 0.f ? qA - qB : qB - qA); } else { float temp = qB; qB = (qB > 0.f ? qA - qB : qB - qA); qA = temp; } if (qB > Math.abs(qA) * 1.9999f) { qB = -Math.abs(qA) * 2.f; qA = -qA; } float[] ret = {qA, qB}; return ret; } static float[] hypot_lookup = { // [32] -0.009935f, -0.011245f, -0.012726f, -0.014397f, -0.016282f, -0.018407f, -0.020800f, -0.023494f, -0.026522f, -0.029923f, -0.033737f, -0.038010f, -0.042787f, -0.048121f, -0.054064f, -0.060671f, -0.068000f, -0.076109f, -0.085054f, -0.094892f, -0.105675f, -0.117451f, -0.130260f, -0.144134f, -0.159093f, -0.175146f, -0.192286f, -0.210490f, -0.229718f, -0.249913f, -0.271001f, -0.292893f}; static float[] precomputed_couple_point(float premag, int floorA, int floorB, float mag, float ang) { int test = ((floorA > floorB) ? 1 : 0) - 1; int offset = 31 - Math.abs(floorA - floorB); float floormag = hypot_lookup[(((offset < 0) ? 1 : 0) - 1) & offset] + 1.f; floormag *= FLOOR1_fromdB_INV_LOOKUP[(floorB & test) | (floorA & (~test))]; mag = premag * floormag; ang = 0.f; float[] ret = {mag, ang}; return ret; } private void _vp_couple(int blobno, vorbis_info_psy_global g, vorbis_look_psy p, vorbis_info_mapping0 vi, float[][] res, float[][] mag_memo, int[][] mag_sort, int[][] ifloor, int[] nonzero, int sliding_lowpass) { int i, j, k; int n = p.n; // perform any requested channel coupling // point stereo can only be used in a first stage (in this encoder) because of the dependency on floor lookups for (i = 0; i < vi.coupling_steps; i++) { // once we're doing multistage coupling in which a channel goes // through more than one coupling step, the floor vector // magnitudes will also have to be recalculated an propogated // along with PCM. Right now, we're not (that will wait until 5.1 // most likely), so the code isn't here yet. The memory management // here is all assuming single depth couplings anyway. // make sure coupling a zero and a nonzero channel results in two nonzero channels. if ((nonzero[vi.coupling_mag[i]] > 0) || (nonzero[vi.coupling_ang[i]] > 0)) { float[] rM = res[vi.coupling_mag[i]]; float[] rA = res[vi.coupling_ang[i]]; // float *qM = rM+n; int qM = n; // float *qA = rA+n; int qA = n; int[] floorM = ifloor[vi.coupling_mag[i]]; int[] floorA = ifloor[vi.coupling_ang[i]]; float prepoint = stereo_threshholds[g.coupling_prepointamp[blobno]]; float postpoint = stereo_threshholds[g.coupling_postpointamp[blobno]]; int partition = ((p.vi.normal_point_p > 0) ? p.vi.normal_partition : p.n); int limit = g.coupling_pointlimit[p.vi.blockflag][blobno]; int pointlimit = limit; nonzero[vi.coupling_mag[i]] = 1; nonzero[vi.coupling_ang[i]] = 1; // The threshold of a stereo is changed with the size of n if (n > 1000) postpoint = stereo_threshholds_limited[g.coupling_postpointamp[blobno]]; for (j = 0; j < p.n; j += partition) { float acc = 0.f; for (k = 0; k < partition; k++) { int l = k + j; if (l < sliding_lowpass) { if ((l >= limit && Math.abs(rM[l]) < postpoint && Math.abs(rA[l]) < postpoint) || (Math.abs(rM[l]) < prepoint && Math.abs(rA[l]) < prepoint)) { float[] return_buffer = precomputed_couple_point(mag_memo[i][l], floorM[l], floorA[l], rM[qM + l], rA[qA + l]); rM[qM + l] = return_buffer[0]; rA[qA + l] = return_buffer[1]; if (Math.rint(rM[qM + l]) == 0) acc += rM[qM + l] * rM[qM + l]; } else { float[] return_buffer = couple_lossless(rM[l], rA[l], rM[qM + l], rA[qA + l]); rM[qM + l] = return_buffer[0]; rA[qA + l] = return_buffer[1]; } } else { rM[qM + l] = 0.f; rA[qA + l] = 0.f; } } if (p.vi.normal_point_p > 0) { for (k = 0; k < partition && acc >= p.vi.normal_thresh; k++) { int l = mag_sort[i][j + k]; if (l < sliding_lowpass && l >= pointlimit && Math.rint(rM[qM + l]) == 0) { rM[qM + l] = unitnorm(rM[qM + l]); acc -= 1.f; } } } } } } } // designed for stereo or other modes where the partition size is an // integer multiple of the number of channels encoded in the current submap private int[][] _2class(vorbis_look_residue0 look, float[][] in, int in_offset, int ch) { int i, j, k, l; vorbis_info_residue0 info = look.info; // move all this setup out later int samples_per_partition = info.grouping; int possible_partitions = info.partitions; int n = info.end - info.begin; int partvals = n / samples_per_partition; // int[][] partword=_vorbis_block_alloc(vb,sizeof(*partword)); // partword[0] = _vorbis_block_alloc(vb,n*ch/samples_per_partition*sizeof(*partword[0])); // memset(partword[0],0,n*ch/samples_per_partition*sizeof(*partword[0])); int[][] partword = new int[1][n * ch / samples_per_partition]; for (i = 0, l = info.begin / ch; i < partvals; i++) { float magmax = 0.f; float angmax = 0.f; for (j = 0; j < samples_per_partition; j += ch) { if (Math.abs(in[0][l + in_offset]) > magmax) magmax = Math.abs(in[0][l + in_offset]); for (k = 1; k < ch; k++) if (Math.abs(in[k][l + in_offset]) > angmax) angmax = Math.abs(in[k][l + in_offset]); l++; } for (j = 0; j < possible_partitions - 1; j++) if (magmax <= info.classmetric1[j] && angmax <= info.classmetric2[j]) break; partword[0][i] = j; } look.frames++; return partword; } private int[][] res2_class(vorbis_look_residue0 vl, float[][] in, int in_offset, int[] nonzero, int ch) { int i, used = 0; for (i = 0; i < ch; i++) if (nonzero[i] > 0) used++; if (used > 0) return _2class(vl, in, in_offset, ch); else return new int[][]{{0}}; } // break an abstraction and copy some code for performance purposes private int local_book_besterror(codebook book, float[] a, int a_off) { int dim = book.dim, i, k, o; int best = 0; encode_aux_threshmatch tt = book.c.thresh_tree; // find the quant val of each scalar for (k = 0, o = dim; k < dim; ++k) { float val = a[a_off + --o]; i = tt.threshvals >> 1; if (val < tt.quantthresh[i]) { if (val < tt.quantthresh[i - 1]) { for (--i; i > 0; --i) if (val >= tt.quantthresh[i - 1]) break; } } else { for (++i; i < tt.threshvals - 1; ++i) if (val < tt.quantthresh[i]) break; } best = (best * tt.quantvals) + tt.quantmap[i]; } // regular lattices are easy :-) if (book.c.lengthlist[best] <= 0) { final static_codebook c = book.c; int index, j; float bestf = 0.f; // float[] e = book.valuelist; int e_off = 0; best = -1; for (index = 0; index < book.entries; index++) { if (c.lengthlist[index] > 0) { float this_local = 0.f; for (j = 0; j < dim; j++) { float val = (book.valuelist[e_off + j] - a[a_off + j]); this_local += val * val; } if (best == -1 || this_local < bestf) { bestf = this_local; best = index; } } e_off += dim; } } { // float *ptr = book.valuelist + best*dim; int ptr = best * dim; for (i = 0; i < dim; i++) { // *a++ -= *ptr++; a[a_off++] -= book.valuelist[ptr++]; } } return best; } private int _encodepart(oggpack_buffer opb_local, float[] vec, int vec_off, int n, codebook book, int[] acc) { int i, bits = 0; int dim = book.dim; int step = n / dim; for (i = 0; i < step; i++) { int entry = local_book_besterror(book, vec, vec_off + i * dim); // #ifdef TRAIN_RES // acc[entry]++; // #endif bits += opb_local.vorbis_book_encode(book, entry); } return bits; } private boolean _01forward(oggpack_buffer opb_local, vorbis_look_residue0 look, float[] in, int ch, int[][] partword) { int i, j, k, s; // long vorbis_info_residue0 info = look.info; // move all this setup out later int samples_per_partition = info.grouping; int possible_partitions = info.partitions; int partitions_per_word = look.phrasebook.dim; int n = info.end - info.begin; int partvals = n / samples_per_partition; // long resbits[128]; // long resvals[128]; // memset(resbits,0,sizeof(resbits)); // memset(resvals,0,sizeof(resvals)); int[] resbits = new int[128]; int[] resvals = new int[128]; // we code the partition words for each channel, then the residual // words for a partition per channel until we've written all the // residual words for that partition word. Then write the next // partition channel words... for (s = 0; s < look.stages; s++) { for (i = 0; i < partvals;) { // first we encode a partition codeword for each channel if (s == 0) { for (j = 0; j < ch; j++) { int val = partword[j][i]; // long for (k = 1; k < partitions_per_word; k++) { val *= possible_partitions; if (i + k < partvals) val += partword[j][i + k]; } // training hack if (val < look.phrasebook.entries) look.phrasebits += opb_local.vorbis_book_encode(look.phrasebook, val); else System.out.println("! - val < look.phrasebook.entries - !"); } } // now we encode interleaved residual values for the partitions for (k = 0; k < partitions_per_word && i < partvals; k++, i++) { int offset = i * samples_per_partition + info.begin; // long for (j = 0; j < ch; j++) { if (s == 0) resvals[partword[j][i]] += samples_per_partition; if ((info.secondstages[partword[j][i]] & (1 << s)) > 0) { codebook statebook = look.partbooks[partword[j][i]][s]; if (statebook != null) { int ret; // long *accumulator = NULL; int[] accumulator = null; // long ret = _encodepart(opb_local, in, j + offset, samples_per_partition, statebook, accumulator); look.postbits += ret; resbits[partword[j][i]] += ret; } } } } } } return true; } // res2 is slightly more different; all the channels are interleaved into a single vector and encoded. private boolean res2_forward(oggpack_buffer opb_local, vorbis_look_residue0 vl, float[][] in, int in_offset, float[][] out, int[] nonzero, int ch, int[][] partword) { int i, j, k; // long int n = pcmend / 2; // long int used = 0; // long // don't duplicate the code; use a working vector hack for now and reshape ourselves into a single channel res1 // ugly; reallocs for each coupling pass :-( // float *work=_vorbis_block_alloc(vb,ch*n*sizeof(*work)); float[] work = new float[ch * n]; for (i = 0; i < ch; i++) { // float *pcm = in[i]; if (nonzero[i] > 0) used++; for (j = 0, k = i; j < n; j++, k += ch) work[k] = in[i][j + in_offset]; } if (used > 0) { boolean ret = _01forward(opb_local, vl, work, 1, partword); // TODO - offset for out // out is currently always called as null // update the sofar vector if (out != null) { for (i = 0; i < ch; i++) { // float *pcm=in[i]; // float *sofar=out[i]; float[] sofar = out[i]; for (j = 0, k = i; j < n; j++, k += ch) sofar[j] += in[i][j + in_offset] - work[k]; } } return ret; } else { return false; } } public void mapping0_forward() { vorbis_info vi = vd.vi; codec_setup_info ci = vi.codec_setup; private_state b = vd.backend_state; vorbis_block_internal vbi = internal; int n = pcmend; int i, j, k; // int *nonzero = alloca(sizeof(*nonzero)*vi.channels); int[] nonzero = new int[vi.channels]; // float **gmdct = _vorbis_block_alloc(vb,vi.channels*sizeof(*gmdct)); float[][] gmdct = new float[vi.channels][]; // int **ilogmaskch = _vorbis_block_alloc(vb,vi.channels*sizeof(*ilogmaskch)); int[][] ilogmaskch = new int[vi.channels][]; // int ***floor_posts = _vorbis_block_alloc(vb,vi.channels*sizeof(*floor_posts)); int[][][] floor_posts = new int[vi.channels][][]; float global_ampmax = vbi.ampmax; // float *local_ampmax = alloca(sizeof(*local_ampmax)*vi.channels); float[] local_ampmax = new float[vi.channels]; int blocktype = vbi.blocktype; int modenumber = W; vorbis_info_mapping0 info = ci.map_param[modenumber]; // vorbis_look_psy psy_look = b.psy+blocktype+(vb.W?2:0); int psy_look_offset; if (W > 0) psy_look_offset = blocktype + 2; else psy_look_offset = blocktype; mode = modenumber; for (i = 0; i < vi.channels; i++) { float scale = 4.f / n; float scale_dB; // TODO - trace where the logfft (pcm) data gets slightly thrown from C // causes flr errors later call to accumulate_fit( logmask, logmdct, look.sorted_index[i], look.sorted_index[i+1], fits[i], n, info ); // sets off the lsfit_acc data when flr[mdct+i]+info.twofitatten >= flr[i] float[] logfft = pcm[i]; // gmdct[i] = _vorbis_block_alloc(vb,n/2*sizeof(**gmdct)); gmdct[i] = new float[n / 2]; scale_dB = todB(scale) + .345f; // + .345 is a hack; the original todB estimation used on IEEE 754 // compliant machines had a bug that returned dB values about a third // of a decibel too high. The bug was harmless because tunings // implicitly took that into account. However, fixing the bug // in the estimator requires changing all the tunings as well. // For now, it's easier to sync things back up here, and // recalibrate the tunings in the next major model upgrade. // window the PCM data window._vorbis_apply_window(pcm[i], b.window, ci.blocksizes, lW, W, nW); // transform the PCM data // only MDCT right now.... b.transform[W][0].mdct_forward(pcm[i], gmdct[i]); // FFT yields more accurate tonal estimation (not phase sensitive) b.fft_look[W].drft_forward(pcm[i]); logfft[0] = scale_dB + todB(pcm[i][0]) + .345f; local_ampmax[i] = logfft[0]; for (j = 1; j < n - 1; j += 2) { float temp = pcm[i][j] * pcm[i][j] + pcm[i][j + 1] * pcm[i][j + 1]; temp = logfft[(j + 1) >> 1] = scale_dB + .5f * todB(temp) + .345f; if (temp > local_ampmax[i]) local_ampmax[i] = temp; } if (local_ampmax[i] > 0.f) local_ampmax[i] = 0.f; if (local_ampmax[i] > global_ampmax) global_ampmax = local_ampmax[i]; } // float *noise = _vorbis_block_alloc(vb,n/2*sizeof(*noise)); float[] noise = new float[n / 2]; // float *tone = _vorbis_block_alloc(vb,n/2*sizeof(*tone)); float[] tone = new float[n / 2]; for (i = 0; i < vi.channels; i++) { // the encoder setup assumes that all the modes used by any // specific bitrate tweaking use the same floor int submap = info.chmuxlist[i]; // the following makes things clearer to *me* anyway float[] mdct = gmdct[i]; float[] logfft = pcm[i]; // float *logmdct = logfft+n/2; int logmdct = n / 2; // float *logmask = logfft; mode = modenumber; // floor_posts[i] = _vorbis_block_alloc(vb,PACKETBLOBS*sizeof(**floor_posts)); // memset(floor_posts[i],0,sizeof(**floor_posts)*PACKETBLOBS); floor_posts[i] = new int[PACKETBLOBS][]; for (j = 0; j < n / 2; j++) logfft[logmdct + j] = todB(mdct[j]) + .345f; // logmdct[j] = todB(mdct+j) + .345f; // first step; noise masking. Not only does 'noise masking' // give us curves from which we can decide how much resolution // to give noise parts of the spectrum, it also implicitly hands // us a tonality estimate (the larger the value in the // 'noise_depth' vector, the more tonal that area is) // b.psy[ psy_look_offset ]._vp_noisemask( logmdct, noise); // noise does not have by-frequency offset bias applied yet b.psy[psy_look_offset]._vp_noisemask(logfft, logmdct, noise); // noise does not have by-frequency offset bias applied yet // second step: 'all the other crap'; all the stuff that isn't // computed/fit for bitrate management goes in the second psy // vector. This includes tone masking, peak limiting and ATH b.psy[psy_look_offset]._vp_tonemask(logfft, tone, global_ampmax, local_ampmax[i]); // third step; we offset the noise vectors, overlay tone // masking. We then do a floor1-specific line fit. If we're // performing bitrate management, the line fit is performed // multiple times for up/down tweakage on demand. // b.psy[ psy_look_offset ]._vp_offset_and_mix( noise, tone, 1, logmask, mdct, logmdct ); b.psy[psy_look_offset]._vp_offset_and_mix(noise, tone, 1, logfft, mdct, logmdct); // this algorithm is hardwired to floor 1 for now; abort out if // we're *not* floor1. This won't happen unless someone has // broken the encode setup lib. Guard it anyway. if (ci.floor_type[info.floorsubmap[submap]] != 1) { System.out.println("Error vorbis_block::mapping0_forward() - floor_type != floor1"); return; } // TODO - logfft float data off // trace of pcm before this function are dead on until here // call to // floor_posts[i][PACKETBLOBS/2] = floor1_fit( b.flr[info.floorsubmap[submap]], logmdct, logmask ); floor_posts[i][PACKETBLOBS / 2] = floor1_fit(b.flr[info.floorsubmap[submap]], logmdct, logfft); // are we managing bitrate? If so, perform two more fits for later rate tweaking (fits represent hi/lo) if (vorbis_bitrate_managed() && (floor_posts[i][PACKETBLOBS / 2] != null)) { // higher rate by way of lower noise curve b.psy[psy_look_offset]._vp_offset_and_mix(noise, tone, 2, logfft, mdct, logmdct); // floor_posts[i][PACKETBLOBS-1] = floor1_fit( vb, b.flr[info.floorsubmap[submap]], logmdct, logmask ); floor_posts[i][PACKETBLOBS - 1] = floor1_fit(b.flr[info.floorsubmap[submap]], logmdct, logfft); // lower rate by way of higher noise curve b.psy[psy_look_offset]._vp_offset_and_mix(noise, tone, 0, logfft, mdct, logmdct); // floor_posts[i][0] = floor1_fit( vb, b.flr[info.floorsubmap[submap]], logmdct, logmask ); floor_posts[i][0] = floor1_fit(b.flr[info.floorsubmap[submap]], logmdct, logfft); // we also interpolate a range of intermediate curves for intermediate rates for (k = 1; k < PACKETBLOBS / 2; k++) { floor_posts[i][k] = floor1_interpolate_fit(b.flr[info.floorsubmap[submap]], floor_posts[i][0], floor_posts[i][PACKETBLOBS / 2], k * 65536 / (PACKETBLOBS / 2)); } for (k = PACKETBLOBS / 2 + 1; k < PACKETBLOBS - 1; k++) { floor_posts[i][k] = floor1_interpolate_fit(b.flr[info.floorsubmap[submap]], floor_posts[i][PACKETBLOBS / 2], floor_posts[i][PACKETBLOBS - 1], (k - PACKETBLOBS / 2) * 65536 / (PACKETBLOBS / 2)); } } } vbi.ampmax = global_ampmax; // the next phases are performed once for vbr-only and PACKETBLOB times for bitrate managed modes. // 1) encode actual mode being used // 2) encode the floor for each channel, compute coded mask curve/res // 3) normalize and couple. // 4) encode residue // 5) save packet bytes to the packetblob vector // iterate over the many masking curve fits we've created // float **res_bundle=alloca(sizeof(*res_bundle)*vi.channels); // float **couple_bundle=alloca(sizeof(*couple_bundle)*vi.channels); // int *zerobundle=alloca(sizeof(*zerobundle)*vi.channels); // int **sortindex=alloca(sizeof(*sortindex)*vi.channels); // float **mag_memo; // int **mag_sort; float[][] res_bundle = new float[vi.channels][]; float[][] couple_bundle = new float[vi.channels][]; int couple_bundle_offset = 0; int[] zerobundle = new int[vi.channels]; int[][] sortindex = new int[vi.channels][]; float[][] mag_memo = null; int[][] mag_sort = null; if (info.coupling_steps > 0) { mag_memo = _vp_quantize_couple_memo(ci.psy_g_param, b.psy[psy_look_offset], info, gmdct); mag_sort = _vp_quantize_couple_sort(b.psy[psy_look_offset], info, mag_memo); hf_reduction(ci.psy_g_param, b.psy[psy_look_offset], info, mag_memo); } // memset( sortindex, 0, sizeof(*sortindex)*vi.channels); if (b.psy[psy_look_offset].vi.normal_channel_p > 0) { for (i = 0; i < vi.channels; i++) { float[] mdct = gmdct[i]; // sortindex[i] = alloca(sizeof(**sortindex)*n/2); sortindex[i] = new int[n / 2]; _vp_noise_normalize_sort(b.psy[psy_look_offset], mdct, sortindex[i]); } } for (k = (vorbis_bitrate_managed() ? 0 : PACKETBLOBS / 2); k <= (vorbis_bitrate_managed() ? PACKETBLOBS - 1 : PACKETBLOBS / 2); k++) { oggpack_buffer opb_local = vbi.packetblob[k]; // start out our new packet blob with packet type and mode // Encode the packet type opb_local.oggpack_write(0, 1); // Encode the modenumber // Encode frame mode, pre,post windowsize, then dispatch opb_local.oggpack_write(modenumber, b.modebits); if (W > 0) { opb_local.oggpack_write(lW, 1); opb_local.oggpack_write(nW, 1); } // encode floor, compute masking curve, sep out residue for (i = 0; i < vi.channels; i++) { int submap = info.chmuxlist[i]; float[] mdct = gmdct[i]; float[] res = pcm[i]; ilogmaskch[i] = new int[n / 2]; int[] ilogmask = ilogmaskch[i]; // int *ilogmask = ilogmaskch[i] = _vorbis_block_alloc(vb,n/2*sizeof(**gmdct)); nonzero[i] = opb_local.floor1_encode(this, b.flr[info.floorsubmap[submap]], floor_posts[i][k], ilogmask); _vp_remove_floor(b.psy[psy_look_offset], mdct, ilogmask, res, ci.psy_g_param.sliding_lowpass[W][k]); _vp_noise_normalize(b.psy[psy_look_offset], res, n / 2, sortindex[i]); } // our iteration is now based on masking curve, not prequant and coupling. Only one prequant/coupling step // quantize/couple // incomplete implementation that assumes the tree is all depth one, or no tree at all if (info.coupling_steps > 0) { _vp_couple(k, ci.psy_g_param, b.psy[psy_look_offset], info, pcm, mag_memo, mag_sort, ilogmaskch, nonzero, ci.psy_g_param.sliding_lowpass[W][k]); } // classify and encode by submap for (i = 0; i < info.submaps; i++) { int ch_in_bundle = 0; int[][] classifications; int resnum = info.residuesubmap[i]; for (j = 0; j < vi.channels; j++) { if (info.chmuxlist[j] == i) { zerobundle[ch_in_bundle] = 0; if (nonzero[j] > 0) zerobundle[ch_in_bundle] = 1; res_bundle[ch_in_bundle] = pcm[j]; couple_bundle[ch_in_bundle++] = pcm[j]; couple_bundle_offset = n / 2; } } // classifications = _residue_P[ci.residue_type[resnum]].class( vb, b.residue[resnum], couple_bundle, zerobundle, ch_in_bundle ); classifications = res2_class(b.residue[resnum], couple_bundle, couple_bundle_offset, zerobundle, ch_in_bundle); // _residue_P[ci.residue_type[resnum]].forward( vb, b.residue[resnum], couple_bundle, NULL, zerobundle, ch_in_bundle, classifications ); res2_forward(opb_local, b.residue[resnum], couple_bundle, couple_bundle_offset, null, zerobundle, ch_in_bundle, classifications); } // ok, done encoding. Mark this protopacket and prepare next. } } // finish taking in the block we just processed public boolean vorbis_bitrate_addblock() { vorbis_block_internal vbi = internal; private_state b = vd.backend_state; bitrate_manager_state bm = b.bms; vorbis_info vi = vd.vi; codec_setup_info ci = vi.codec_setup; bitrate_manager_info bi = ci.bi; int choice = new Double(Math.rint(bm.avgfloat)).intValue(); int this_bits = vbi.packetblob[choice].oggpack_bytes() * 8; // long int min_target_bits; // long int max_target_bits; // long int avg_target_bits; // long if (W > 0) { min_target_bits = bm.min_bitsper * bm.short_per_long; max_target_bits = bm.max_bitsper * bm.short_per_long; avg_target_bits = bm.avg_bitsper * bm.short_per_long; } else { min_target_bits = bm.min_bitsper; max_target_bits = bm.max_bitsper; avg_target_bits = bm.avg_bitsper; } int samples = ci.blocksizes[W] >> 1; int desired_fill = new Float(bi.reservoir_bits * bi.reservoir_bias).intValue(); // long if (bm.managed <= 0) { // not a bitrate managed stream, but for API simplicity, we'll buffer the packet to keep the code path clean if (bm.vb != null) return false; // one has been submitted without being claimed bm.vb = this; return true; } bm.vb = this; // look ahead for avg floater if (bm.avg_bitsper > 0) { float slew = 0.f; // double // long avg_target_bits = (vb->W?bm.avg_bitsper*bm.short_per_long:bm.avg_bitsper); float slewlimit = 15.f / bi.slew_damp; // double // choosing a new floater: // if we're over target, we slew down // if we're under target, we slew up // choose slew as follows: look through packetblobs of this frame // and set slew as the first in the appropriate direction that // gives us the slew we want. This may mean no slew if delta is already favorable. // Then limit slew to slew max if (bm.avg_reservoir + (this_bits - avg_target_bits) > desired_fill) { while (choice > 0 && this_bits > avg_target_bits && bm.avg_reservoir + (this_bits - avg_target_bits) > desired_fill) { choice--; this_bits = vbi.packetblob[choice].oggpack_bytes() * 8; } } else if (bm.avg_reservoir + (this_bits - avg_target_bits) < desired_fill) { while (choice + 1 < PACKETBLOBS && this_bits < avg_target_bits && bm.avg_reservoir + (this_bits - avg_target_bits) < desired_fill) { choice++; this_bits = vbi.packetblob[choice].oggpack_bytes() * 8; } } slew = new Double(Math.rint(choice - bm.avgfloat) / samples * vi.rate).floatValue(); if (slew < -slewlimit) slew = -slewlimit; if (slew > slewlimit) slew = slewlimit; choice = new Float(Math.rint(bm.avgfloat += slew / vi.rate * samples)).intValue(); this_bits = vbi.packetblob[choice].oggpack_bytes() * 8; } // enforce min(if used) on the current floater (if used) if (bm.min_bitsper > 0) { // do we need to force the bitrate up? if (this_bits < min_target_bits) { while (bm.minmax_reservoir - (min_target_bits - this_bits) < 0) { choice++; if (choice >= PACKETBLOBS) break; this_bits = vbi.packetblob[choice].oggpack_bytes() * 8; } } } // enforce max (if used) on the current floater (if used) if (bm.max_bitsper > 0) { // do we need to force the bitrate down? if (this_bits > max_target_bits) { while (bm.minmax_reservoir + (this_bits - max_target_bits) > bi.reservoir_bits) { choice--; if (choice < 0) break; this_bits = vbi.packetblob[choice].oggpack_bytes() * 8; } } } // Choice of packetblobs now made based on floater, and min/max requirements. Now boundary check extreme choices if (choice < 0) { // choosing a smaller packetblob is insufficient to trim bitrate. frame will need to be truncated int maxsize = (max_target_bits + (bi.reservoir_bits - bm.minmax_reservoir)) / 8; // long bm.choice = choice = 0; if (vbi.packetblob[choice].oggpack_bytes() > maxsize) { vbi.packetblob[choice].oggpack_writetrunc(maxsize * 8); this_bits = vbi.packetblob[choice].oggpack_bytes() * 8; } } else { int minsize = (min_target_bits - bm.minmax_reservoir + 7) / 8; // long if (choice >= PACKETBLOBS) choice = PACKETBLOBS - 1; bm.choice = choice; // prop up bitrate according to demand. pad this frame out with zeroes minsize -= vbi.packetblob[choice].oggpack_bytes(); while (minsize-- > 0) vbi.packetblob[choice].oggpack_write(0, 8); this_bits = vbi.packetblob[choice].oggpack_bytes() * 8; } // now we have the final packet and the final packet size. Update statistics // min and max reservoir if (bm.min_bitsper > 0 || bm.max_bitsper > 0) { if (max_target_bits > 0 && this_bits > max_target_bits) { bm.minmax_reservoir += (this_bits - max_target_bits); } else if (min_target_bits > 0 && this_bits < min_target_bits) { bm.minmax_reservoir += (this_bits - min_target_bits); } else { // inbetween; we want to take reservoir toward but not past desired_fill if (bm.minmax_reservoir > desired_fill) { if (max_target_bits > 0) { // logical bulletproofing against initialization state bm.minmax_reservoir += (this_bits - max_target_bits); if (bm.minmax_reservoir < desired_fill) bm.minmax_reservoir = desired_fill; } else { bm.minmax_reservoir = desired_fill; } } else { if (min_target_bits > 0) { // logical bulletproofing against initialization state bm.minmax_reservoir += (this_bits - min_target_bits); if (bm.minmax_reservoir > desired_fill) bm.minmax_reservoir = desired_fill; } else { bm.minmax_reservoir = desired_fill; } } } } // avg reservoir if (bm.avg_bitsper > 0) { // int avg_target_bits = (vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper); // long if (W > 0) avg_target_bits = bm.avg_bitsper * bm.short_per_long; else avg_target_bits = bm.avg_bitsper; bm.avg_reservoir += this_bits - avg_target_bits; } return true; } }