All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.axet.libvorbis.Jenvelope_lookup Maven / Gradle / Ivy

There is a newer version: 2.1.0
Show newest version
package com.github.axet.libvorbis;

/**
PCM data envelope analysis and manipulation
 */
final class Jenvelope_lookup {
	private static final int   VE_PRE  = 16;
	static final int VE_WIN  = 4;
	static final int VE_POST = 2;
	private static final int   VE_AMP  = (VE_PRE + VE_POST - 1);
	//
	static final int VE_BANDS  = 7;
	private static final int   VE_NEARDC = 15;
	/** a bit less than short block */
	private static final int   VE_MINSTRETCH = 2;
	/** one-third full block */
	static final int VE_MAXSTRETCH = 12;
	//
	private static final class Jenvelope_filter_state {
		private final float[] ampbuf = new float[VE_AMP];
		private int   ampptr;

		private final float[] nearDC = new float[VE_NEARDC];
		private float nearDC_acc;
		private float nearDC_partialacc;
		private int   nearptr;
	}
	//
	private static final class Jenvelope_band {
		private int begin;
		private int end;
		private float[] window;
		private float total;
	}
	//
	int ch = 0;
	private int winlength = 0;
	int searchstep = 0;
	private float minenergy = 0.0f;

	private final Jmdct_lookup mdct = new Jmdct_lookup();
	private float[] mdct_win = null;

	final Jenvelope_band[]   band = new Jenvelope_band[VE_BANDS];
	Jenvelope_filter_state[] filter = null;
	int m_stretch = 0;

	boolean[] mark = null;

	int storage = 0;
	int current = 0;
	int curmark = 0;
	int cursor  = 0;
/*#if 0
	private static int seq = 0;
	private static long totalshift = -1024;
#endif */
	//
	Jenvelope_lookup() {
		for( int i = 0; i < VE_BANDS; i++ ) {
			band[i] = new Jenvelope_band();
		}
	}
	private final void clear() {
		ch = 0;
		winlength = 0;
		searchstep = 0;
		minenergy = 0.0f;

		mdct.clear();
		mdct_win = null;

		for( int i = 0; i < VE_BANDS; i++ ) {
			band[i] = null;
		}
		filter = null;
		m_stretch = 0;

		mark = null;

		storage = 0;
		current = 0;
		curmark = 0;
		cursor = 0;
	}

	// envelope.c
	final void _ve_envelope_init(final Jvorbis_info vi) {
		final Jcodec_setup_info ci = vi.codec_setup;
		final Jvorbis_info_psy_global gi = ci.psy_g_param;
		final int channels = vi.channels;
		int n = this.winlength = 128;
		this.searchstep = 64; /* not random */

		this.minenergy = gi.preecho_minenergy;
		this.ch = channels;
		this.storage = 128;
		this.cursor = ci.blocksizes[1] >> 1;
		float[] buff = new float[n];// java
		this.mdct_win = buff;
		this.mdct.mdct_init( n );

		for( int i = 0; i < n; i++ ) {
			final float s = (float) Math.sin( (double)i / (n - 1.) * Math.PI );
			buff[i] = s * s;
		}

		/* magic follows */
		this.band[0].begin = 2;  this.band[0].end = 4;
		this.band[1].begin = 4;  this.band[1].end = 5;
		this.band[2].begin = 6;  this.band[2].end = 6;
		this.band[3].begin = 9;  this.band[3].end = 8;
		this.band[4].begin = 13;  this.band[4].end = 8;
		this.band[5].begin = 17;  this.band[5].end = 8;
		this.band[6].begin = 22;  this.band[6].end = 8;

		for( int j = 0; j < VE_BANDS; j++ ) {
			final Jenvelope_band b = this.band[j];// java
			n = b.end;
			buff = new float[n];// java
			for( int i = 0; i < n; i++ ) {
				buff[i] = (float) Math.sin( ((double)i + .5) / (double)n * Math.PI );
				b.total += buff[i];
			}
			b.window = buff;
			b.total = 1.f / b.total;
		}

		this.filter = new Jenvelope_filter_state[VE_BANDS * channels];
		for ( int i = 0, ie = VE_BANDS * channels; i < ie; i++ ) {
			this.filter[i] = new Jenvelope_filter_state();
		}
		this.mark = new boolean[this.storage];

	}

	final void _ve_envelope_clear() {
		//int i;
		this.mdct.mdct_clear();
		//for( i = 0; i < VE_BANDS; i++ )
		//	e.band[i].window = null;
		//e.mdct_win = null;
		//e.filter = null;
		//e.mark = null;
		clear();
	}

	/** fairly straight threshhold-by-band based until we find something
	   that works better and isn't patented. */
	final int _ve_amp(
					final Jvorbis_info_psy_global gi,
					final float[] data, int data_offset,
					final Jenvelope_band[] bands,
					final Jenvelope_filter_state[] filters,
					final int filter_offset)
	{
		int n = this.winlength;
		int ret = 0;

		/* 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) */

		final float minV = this.minenergy;
		final float[] vec = new float[n];

		/* stretch is used to gradually lengthen the number of windows
		 considered prevoius-to-potential-trigger */
		int i = this.m_stretch >>> 1;
		final int stretch = VE_MINSTRETCH >= i ? VE_MINSTRETCH : i;
		float penalty = gi.stretch_penalty - (i - VE_MINSTRETCH);
		if( penalty < 0.f ) {
			penalty = 0.f;
		}
		if( penalty > gi.stretch_penalty ) {
			penalty = gi.stretch_penalty;
		}

		/*Janalysis._analysis_output_always("lpcm", seq2, data, data_offset, n, false, false,
			totalshift + pos * this.m_searchstep);*/

		/* window and transform */
		for( i = 0; i < n; i++ ) {
			vec[i] = data[data_offset++] * this.mdct_win[i];
		}
		this.mdct.mdct_forward( vec, vec );

		/*Janalysis._analysis_output_always("mdct", seq2, vec, 0, n / 2, false, true, 0);*/

		/* near-DC spreading function; this has nothing to do with
		 psychoacoustics, just sidelobe leakage and window size */
		float decay;
		{
			float temp = vec[0];// java
			temp *= temp;
			float v = vec[1];
			temp += .7f * v * v;
			v = vec[2];
			temp += .2f * v * v;
			final Jenvelope_filter_state flt = filters[filter_offset];
			final int ptr = flt.nearptr;

			/* the accumulation is regularly refreshed from scratch to avoid
			   floating point creep */
			if( ptr == 0 ) {
				decay = flt.nearDC_acc = flt.nearDC_partialacc + temp;
				flt.nearDC_partialacc = temp;
			} else {
				decay = flt.nearDC_acc += temp;
				flt.nearDC_partialacc += temp;
			}
			flt.nearDC_acc -= flt.nearDC[ptr];
			flt.nearDC[ptr] = temp;

			decay *= (1.f / (VE_NEARDC + 1));
			flt.nearptr++;
			if( flt.nearptr >= VE_NEARDC ) {
				flt.nearptr = 0;
			}
			decay = Jcodec.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 */
		n >>>= 1;
		for( i = 0; i < n; i += 2 ) {
			float val = vec[i];// java
			val *= val;
			final float v = vec[i + 1];// java
			val += v * v;
			val = Jcodec.todB( val ) * .5f;
			if( val < decay ) {
				val = decay;
			}
			if( val < minV ) {
				val = minV;
			}
			vec[i >> 1] = val;
			decay -= 8.f;
		}

		/*Janalysis._analysis_output_always("spread", seq2++, vec, 0, n / 2, false, false, 0);*/

		/* perform preecho/postecho triggering by band */
		for( int j = 0; j < VE_BANDS; j++ ) {
			float acc = 0.f;
			float valmax, valmin;
			final Jenvelope_band eb_j = bands[j];

			/* accumulate amplitude */
			final float w[] = eb_j.window;// java
			final int end = eb_j.end;// java
			for( i = 0, n = eb_j.begin; i < end; i++ ) {
				acc += vec[n++] * w[i];
			}

			acc *= eb_j.total;

			/* convert amplitude to delta */
			{
				final Jenvelope_filter_state filters_j = filters[filter_offset + j];
				final int ithis = filters_j.ampptr;
				float postmax, postmin, premax = -99999.f, premin = 99999.f;

				int p = ithis;
				p--;
				if( p < 0 ) {
					p += VE_AMP;
				}
				final float[] ampbuf = filters_j.ampbuf;// java
				postmin = postmax = ampbuf[p];
				if( postmax < acc ) {
					postmax = acc;
				}
				if( postmin > acc ) {
					postmin = acc;
				}

				for( i = 0; i < stretch; i++ ) {
					p--;
					if( p < 0 ) {
						p += VE_AMP;
					}
					final float val = ampbuf[p];
					premax = (premax >= val) ? premax : val;
					premin = (premin <= val) ? premin : val;
				}

				valmin = postmin - premin;
				valmax = postmax - premax;

				/*filters[j].markers[pos]=valmax;*/
				ampbuf[ithis] = acc;
				filters_j.ampptr++;
				if( filters_j.ampptr >= VE_AMP ) {
					filters_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);
	}

	final void _ve_envelope_shift(final int shift) {
		/* adjust for placing marks ahead of ve->current */
		final int smallsize = this.current / this.searchstep + VE_POST;
		final int smallshift = shift / this.searchstep;

		System.arraycopy( this.mark, smallshift, this.mark, 0, smallsize - smallshift );

/* #if 0
		for( i = 0; i < Jvorbis_dsp_state.VE_BANDS * e.ch; i++ )
			System.arraycopy( this.m_filter[i].markers, smallshift, this.m_filter[i].markers, 0, 1024 - smallshift );
		totalshift += shift;
#endif */

		this.current -= shift;
		if( this.curmark >= 0 ) {
			this.curmark -= shift;
		}
		this.cursor -= shift;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy