/* * 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 static org.xiph.libvorbis.vorbis_constants.integer_constants.*; class envelope_lookup { int ch; int winlength; int searchstep; float minenergy; mdct_lookup mdct; float[] mdct_win; envelope_band[] band; // VE_BANDS; envelope_filter_state[] filter; // *filter; int stretch; int[] mark; int storage; int current; int curmark; int cursor; public envelope_lookup(int _ch, int _winlength, int _searchstep, float _minenergy, mdct_lookup _mdct, float[] _mdct_win, envelope_band[] _band, envelope_filter_state[] _filter, int _stretch, int[] _mark, int _storage, int _current, int _curmark, int _cursor) { ch = _ch; winlength = _winlength; searchstep = _searchstep; minenergy = _minenergy; mdct = _mdct; mdct_win = new float[_mdct_win.length]; System.arraycopy(_mdct_win, 0, mdct_win, 0, _mdct_win.length); band = new envelope_band[VE_BANDS]; System.arraycopy(_band, 0, band, 0, _band.length); filter = _filter; stretch = _stretch; mark = new int[_mark.length]; System.arraycopy(_mark, 0, mark, 0, _mark.length); storage = _storage; current = _current; curmark = _curmark; cursor = _cursor; } public envelope_lookup(envelope_lookup src) { this(src.ch, src.winlength, src.searchstep, src.minenergy, src.mdct, src.mdct_win, src.band, src.filter, src.stretch, src.mark, src.storage, src.current, src.curmark, src.cursor); } public envelope_lookup(vorbis_info _vi) { ch = _vi.channels; winlength = 128; searchstep = 64; // not random minenergy = _vi.codec_setup.psy_g_param.preecho_minenergy; int n = winlength; storage = 128; cursor = _vi.codec_setup.blocksizes[1] / 2; // mdct_win = _ogg_calloc(n,sizeof(*mdct_win)); mdct_win = new float[n]; mdct = new mdct_lookup(); mdct.mdct_init(n); for (int i = 0; i < n; i++) { mdct_win[i] = new Double(Math.sin(i / (n - 1.) * M_PI)).floatValue(); mdct_win[i] *= mdct_win[i]; } band = new envelope_band[VE_BANDS]; for (int i = 0; i < VE_BANDS; i++) band[i] = new envelope_band(); band[0].begin = 2; band[0].end = 4; band[1].begin = 4; band[1].end = 5; band[2].begin = 6; band[2].end = 6; band[3].begin = 9; band[3].end = 8; band[4].begin = 13; band[4].end = 8; band[5].begin = 17; band[5].end = 8; band[6].begin = 22; band[6].end = 8; for (int j = 0; j < VE_BANDS; j++) { n = band[j].end; // band[j].window = _ogg_malloc(n*sizeof(*band[0].window)); band[j].window = new float[n]; for (int i = 0; i < n; i++) { band[j].window[i] = new Double(Math.sin((i + .5) / n * M_PI)).floatValue(); band[j].total += band[j].window[i]; } band[j].total = 1.0f / band[j].total; } // filter=_ogg_calloc(VE_BANDS*ch,sizeof(*filter)); // mark=_ogg_calloc(storage,sizeof(*mark)); filter = new envelope_filter_state[VE_BANDS * ch]; for (int i = 0; i < VE_BANDS * ch; i++) filter[i] = new envelope_filter_state(); mark = new int[storage]; } public int _ve_amp(vorbis_info_psy_global gi, float[] data, int offset1, envelope_band[] bands, envelope_filter_state[] filters, int offset2, int pos) { int n = winlength; int ret = 0; int i, j; float decay; /* we want to have a 'minimum bar' for energy, else we're just * basing blocks on quantization noise that outweighs the signal * itself (for low power signals) */ float minV = minenergy; // float *vec=alloca(n*sizeof(*vec)); float[] vec = new float[n]; /* stretch is used to gradually lengthen the number of windows * considered prevoius-to-potential-trigger */ // local OOP variable rename stretch int stretch_local = Math.max(VE_MINSTRETCH, stretch / 2); float penalty = gi.stretch_penalty - (stretch / 2 - VE_MINSTRETCH); if (penalty < 0.f) penalty = 0.f; if (penalty > gi.stretch_penalty) penalty = gi.stretch_penalty; /*_analysis_output_always("lpcm",seq2,data,n,0,0,totalshift+pos*ve->searchstep);*/ // window and transform for (i = 0; i < n; i++) vec[i] = data[offset1 + i] * mdct_win[i]; mdct.mdct_forward(vec, vec); // mdct.out( "", null ); /*_analysis_output_always("mdct",seq2,vec,n/2,0,1,0); */ /* near-DC spreading function; this has nothing to do with * psychoacoustics, just sidelobe leakage and window size */ float temp = vec[0] * vec[0] + .7f * vec[1] * vec[1] + .2f * vec[2] * vec[2]; int ptr = filters[offset2].nearptr; // the accumulation is regularly refreshed from scratch to avoid floating point creep if (ptr == 0) { decay = filters[offset2].nearDC_acc = filters[offset2].nearDC_partialacc + temp; filters[offset2].nearDC_partialacc = temp; } else { decay = filters[offset2].nearDC_acc += temp; filters[offset2].nearDC_partialacc += temp; } filters[offset2].nearDC_acc -= filters[offset2].nearDC[ptr]; filters[offset2].nearDC[ptr] = temp; decay *= (1. / (VE_NEARDC + 1)); filters[offset2].nearptr++; if (filters[offset2].nearptr >= VE_NEARDC) filters[offset2].nearptr = 0; decay = todB(decay) * .5f - 15.f; /* perform spreading and limiting, also smooth the spectrum. yes, the MDCT results * in all real coefficients, but it still *behaves* like real/imaginary pairs */ for (i = 0; i < n / 2; i += 2) { float val = vec[i] * vec[i] + vec[i + 1] * vec[i + 1]; val = todB(val) * .5f; if (val < decay) val = decay; if (val < minV) val = minV; vec[i >> 1] = val; decay -= 8.; } /*_analysis_output_always("spread",seq2++,vec,n/4,0,0,0);*/ // perform preecho/postecho triggering by band for (j = 0; j < VE_BANDS; j++) { float acc = 0.f; float valmax; float valmin; // accumulate amplitude for (i = 0; i < bands[j].end; i++) acc += vec[i + bands[j].begin] * bands[j].window[i]; acc *= bands[j].total; // convert amplitude to delta // local OOP variable rename // protected variable name this int p, _this = filters[offset2 + j].ampptr; float postmax, postmin, premax = -99999.f, premin = 99999.f; p = _this; p--; if (p < 0) p += VE_AMP; postmax = Math.max(acc, filters[offset2 + j].ampbuf[p]); postmin = Math.min(acc, filters[offset2 + j].ampbuf[p]); for (i = 0; i < stretch_local; i++) { p--; if (p < 0) p += VE_AMP; premax = Math.max(premax, filters[offset2 + j].ampbuf[p]); premin = Math.min(premin, filters[offset2 + j].ampbuf[p]); } valmin = postmin - premin; valmax = postmax - premax; /*filters[j].markers[pos]=valmax;*/ filters[offset2 + j].ampbuf[_this] = acc; filters[offset2 + j].ampptr++; if (filters[offset2 + j].ampptr >= VE_AMP) filters[offset2 + j].ampptr = 0; // look at min/max, decide trigger if (valmax > gi.preecho_thresh[j] + penalty) { ret |= 1; ret |= 4; } if (valmin < gi.postecho_thresh[j] - penalty) ret |= 2; } return ret; } public void _ve_envelope_shift(int shift) { int smallsize = current / searchstep + VE_POST; // adjust for placing marks ahead of ve->current int smallshift = shift / searchstep; // memmove(e->mark,e->mark+smallshift,(smallsize-smallshift)*sizeof(*e->mark)); System.arraycopy(mark, smallshift, mark, 0, smallsize - smallshift); current -= shift; if (curmark >= 0) curmark -= shift; cursor -= shift; } }