public TrackGain(int sampleRate) { if (!ReplayGain.IsSupportedFormat(sampleRate)) { throw new NotSupportedException("Unsupported sampling rate"); } this.freqIndex = ReplayGain.FreqInfos.IndexOf(i => i.SampleRate == sampleRate); this.gainData = new GainData(); this.lInPreBuf = new double[ReplayGain.MAX_ORDER * 2]; this.lStepBuf = new double[ReplayGain.MAX_SAMPLES_PER_WINDOW + ReplayGain.MAX_ORDER]; this.lOutBuf = new double[ReplayGain.MAX_SAMPLES_PER_WINDOW + ReplayGain.MAX_ORDER]; this.rInPreBuf = new double[ReplayGain.MAX_ORDER * 2]; this.rStepBuf = new double[ReplayGain.MAX_SAMPLES_PER_WINDOW + ReplayGain.MAX_ORDER]; this.rOutBuf = new double[ReplayGain.MAX_SAMPLES_PER_WINDOW + ReplayGain.MAX_ORDER]; this.sampleWindow = (int)Math.Ceiling(sampleRate * ReplayGain.RMS_WINDOW_TIME); this.lInPre = new CPtr <double>(lInPreBuf, ReplayGain.MAX_ORDER); this.lStep = new CPtr <double>(lStepBuf, ReplayGain.MAX_ORDER); this.lOut = new CPtr <double>(lOutBuf, ReplayGain.MAX_ORDER); this.rInPre = new CPtr <double>(rInPreBuf, ReplayGain.MAX_ORDER); this.rStep = new CPtr <double>(rStepBuf, ReplayGain.MAX_ORDER); this.rOut = new CPtr <double>(rOutBuf, ReplayGain.MAX_ORDER); }
private void FilterButter(CPtr <double> input, CPtr <double> output, long nSamples, double[] aKernel, double[] bKernel) { while (nSamples-- != 0) { output[0] = input[0] * bKernel[0] - output[-1] * aKernel[1] + input[-1] * bKernel[1] - output[-2] * aKernel[2] + input[-2] * bKernel[2]; ++output; ++input; } }
/// <summary> /// Disposes the resources used to calculate ReplayGain, but doesn't clear the result of the analysis. /// </summary> public void Dispose() { this.lInPreBuf = null; this.lStepBuf = null; this.lOutBuf = null; this.rInPreBuf = null; this.rStepBuf = null; this.rOutBuf = null; this.lInPre = new CPtr <double>(); this.lStep = new CPtr <double>(); this.lOut = new CPtr <double>(); this.rInPre = new CPtr <double>(); this.rStep = new CPtr <double>(); this.rOut = new CPtr <double>(); }
private void AnalyzeSamples(CPtr <double> leftSamples, CPtr <double> rightSamples) { int numSamples = leftSamples.Length; CPtr <double> curLeft; CPtr <double> curRight; long batchSamples = numSamples; long curSamples; long curSamplePos = 0; if (numSamples < ReplayGain.MAX_ORDER) { Array.Copy(leftSamples.Array, 0, this.lInPreBuf, ReplayGain.MAX_ORDER, numSamples); Array.Copy(rightSamples.Array, 0, this.rInPreBuf, ReplayGain.MAX_ORDER, numSamples); } else { Array.Copy(leftSamples.Array, 0, this.lInPreBuf, ReplayGain.MAX_ORDER, ReplayGain.MAX_ORDER); Array.Copy(rightSamples.Array, 0, this.rInPreBuf, ReplayGain.MAX_ORDER, ReplayGain.MAX_ORDER); } while (batchSamples > 0) { curSamples = batchSamples > this.sampleWindow - this.totSamp ? this.sampleWindow - this.totSamp : batchSamples; if (curSamplePos < ReplayGain.MAX_ORDER) { curLeft = this.lInPre + curSamplePos; curRight = this.rInPre + curSamplePos; if (curSamples > ReplayGain.MAX_ORDER - curSamplePos) { curSamples = ReplayGain.MAX_ORDER - curSamplePos; } } else { curLeft = leftSamples + curSamplePos; curRight = rightSamples + curSamplePos; } FilterYule(curLeft, this.lStep + this.totSamp, curSamples, ReplayGain.FreqInfos[this.freqIndex].AYule, ReplayGain.FreqInfos[this.freqIndex].BYule); FilterYule(curRight, this.rStep + this.totSamp, curSamples, ReplayGain.FreqInfos[this.freqIndex].AYule, ReplayGain.FreqInfos[this.freqIndex].BYule); FilterButter(this.lStep + this.totSamp, this.lOut + this.totSamp, curSamples, ReplayGain.FreqInfos[this.freqIndex].AButter, ReplayGain.FreqInfos[this.freqIndex].BButter); FilterButter(this.rStep + this.totSamp, this.rOut + this.totSamp, curSamples, ReplayGain.FreqInfos[this.freqIndex].AButter, ReplayGain.FreqInfos[this.freqIndex].BButter); curLeft = this.lOut + this.totSamp; // Get the squared values curRight = this.rOut + this.totSamp; for (long i = curSamples % 16; i-- != 0;) { this.lSum += Sqr(curLeft[0]); ++curLeft; this.rSum += Sqr(curRight[0]); ++curRight; } for (long i = curSamples / 16; i-- != 0;) { this.lSum += Sqr(curLeft[0]) + Sqr(curLeft[1]) + Sqr(curLeft[2]) + Sqr(curLeft[3]) + Sqr(curLeft[4]) + Sqr(curLeft[5]) + Sqr(curLeft[6]) + Sqr(curLeft[7]) + Sqr(curLeft[8]) + Sqr(curLeft[9]) + Sqr(curLeft[10]) + Sqr(curLeft[11]) + Sqr(curLeft[12]) + Sqr(curLeft[13]) + Sqr(curLeft[14]) + Sqr(curLeft[15]); curLeft += 16; this.rSum += Sqr(curRight[0]) + Sqr(curRight[1]) + Sqr(curRight[2]) + Sqr(curRight[3]) + Sqr(curRight[4]) + Sqr(curRight[5]) + Sqr(curRight[6]) + Sqr(curRight[7]) + Sqr(curRight[8]) + Sqr(curRight[9]) + Sqr(curRight[10]) + Sqr(curRight[11]) + Sqr(curRight[12]) + Sqr(curRight[13]) + Sqr(curRight[14]) + Sqr(curRight[15]); curRight += 16; } batchSamples -= curSamples; curSamplePos += curSamples; this.totSamp += curSamples; if (this.totSamp == this.sampleWindow) { // Get the Root Mean Square (RMS) for this set of samples double val = ReplayGain.STEPS_PER_DB * 10.0 * Math.Log10((this.lSum + this.rSum) / this.totSamp * 0.5 + 1.0e-37); int ival = (int)val; if (ival < 0) { ival = 0; } if (ival >= this.gainData.Accum.Length) { ival = this.gainData.Accum.Length - 1; } this.gainData.Accum[ival]++; this.lSum = this.rSum = 0.0; if (this.totSamp > int.MaxValue) { throw new OverflowException("Too many samples! Change to long and recompile!"); } Array.Copy(this.lOutBuf, (int)this.totSamp, this.lOutBuf, 0, ReplayGain.MAX_ORDER); Array.Copy(this.rOutBuf, (int)this.totSamp, this.rOutBuf, 0, ReplayGain.MAX_ORDER); Array.Copy(this.lStepBuf, (int)this.totSamp, this.lStepBuf, 0, ReplayGain.MAX_ORDER); Array.Copy(this.rStepBuf, (int)this.totSamp, this.rStepBuf, 0, ReplayGain.MAX_ORDER); this.totSamp = 0; } if (this.totSamp > this.sampleWindow) { // somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow throw new Exception("Gain analysis error!"); } } if (numSamples < ReplayGain.MAX_ORDER) { Array.Copy(this.lInPreBuf, numSamples, this.lInPreBuf, 0, ReplayGain.MAX_ORDER - numSamples); Array.Copy(this.rInPreBuf, numSamples, this.rInPreBuf, 0, ReplayGain.MAX_ORDER - numSamples); Array.Copy(leftSamples.Array, leftSamples.Index, this.lInPreBuf, ReplayGain.MAX_ORDER - numSamples, numSamples); Array.Copy(rightSamples.Array, rightSamples.Index, this.rInPreBuf, ReplayGain.MAX_ORDER - numSamples, numSamples); } else { Array.Copy(leftSamples.Array, leftSamples.Index + numSamples - ReplayGain.MAX_ORDER, this.lInPreBuf, 0, ReplayGain.MAX_ORDER); Array.Copy(rightSamples.Array, rightSamples.Index + numSamples - ReplayGain.MAX_ORDER, this.rInPreBuf, 0, ReplayGain.MAX_ORDER); } }