public double Normalize(double dBfs, bool doIt) { // Make sure the whole buffer is scaled double gain = 0; double max = 0; for (int j = 0; j < _samples.Count; j++) { ISample s = _samples[j]; for (int c = 0; c < s.NumChannels; c++) { max = Math.Max(max, Math.Abs(s[c])); } } if (max == 0) { return(0); } gain = MathUtil.gain(dBfs) / max; if (doIt) { ApplyGain(gain); } return(gain); }
double mag(double f) { double ff = f - _startFreq; if (ff <= 0) { return((Math.Cos(Math.PI * ff / _startFreq) + 1) / 2); } double dbNow = 1 - (10 * Math.Log10(f / _startFreq)); double gainNow = MathUtil.gain(dbNow); return(gainNow); }
private static double WeightedVolume2(SoundBuffer src, double dbSPL, double dbSPLBase) { double v = 0; uint sr = src.SampleRate; // Read buffer into array of complex Complex[][] data = src.ToComplexArray(); // We only have a single channel Complex[] cdata = data[0]; // FFT in place Fourier.FFT(cdata.Length, cdata); // Calculate magnitude, weighted by 80-phon loudness, for each loudness band. // These are the ISO measured points: FilterProfile lfg; if (dbSPLBase == 0) { lfg = SPL(dbSPL); } else { lfg = DifferentialSPL(dbSPL, dbSPLBase); } // lfg.Add(new FreqGain(sr / 2, lfg[lfg.Count - 1].Gain)); // Cover the ISO measured range (only...) int nStart = (int)(lfg[0].Freq * (long)cdata.Length / sr); int nEnd = (int)(lfg[lfg.Count - 1].Freq * (long)cdata.Length / sr); // Just use linear interpolation (on a dB scale; linear freq scale) of gain between each measured point int nfg = 0; int startp = nStart; int endp = (int)(lfg[nfg + 1].Freq * (long)cdata.Length / sr); // endpoint of this band double dB1 = lfg[nfg].Gain; // SPL of the ISO223 curve at this freq double dB2 = lfg[nfg + 1].Gain; // ...and the next point double vThisBand = 0; int nThisBand = 0; for (int j = nStart; j < nEnd; j++) { if (j > endp) { if (nThisBand > 0) { v += Math.Sqrt(vThisBand / nThisBand); // RMS } while (j >= endp) { nfg++; startp = j; endp = (int)(lfg[nfg + 1].Freq * (long)cdata.Length / sr); dB1 = lfg[nfg].Gain; dB2 = lfg[nfg + 1].Gain; } vThisBand = 0; nThisBand = 0; } Complex c = cdata[j]; double dbHere = dB1 + ((dB2 - dB1) * (double)(j - startp) / (double)(endp - startp)); vThisBand += (c.Re * c.Re) / MathUtil.gain(dbHere); nThisBand++; } if (nThisBand > 0) { v += Math.Sqrt(vThisBand / nThisBand); } return(v); }
public LinearEnvelope(double dBstartGain, double dBendGain, int nSamples) { _startGain = MathUtil.gain(dBstartGain); _endGain = MathUtil.gain(dBendGain); _nSamples = nSamples; }
private static IEnumerator <double> Arbitrary(int length, FilterProfile coeffs, uint sampleRate) { // Generate random-phase with specified magnitudes in the frequency domain, then IFFT. // This noise is periodic - length (next power of 2 from 'n') int l = MathUtil.NextPowerOfTwo(length); Complex[] data = new Complex[l]; int n = 0; double freq1 = coeffs[n].Freq * 2 * l / sampleRate; double freq2 = coeffs[n + 1].Freq * 2 * l / sampleRate; double gain1 = coeffs[n].Gain; double gain2 = coeffs[n + 1].Gain; double logn = Math.Log(l); for (int j = 0; j < l; j++) { double gainDb; double gain; if (j > freq2) { // Move to the next coefficient n++; if (n < coeffs.Count - 1) { freq1 = coeffs[n].Freq * 2 * l / sampleRate; freq2 = coeffs[n + 1].Freq * 2 * l / sampleRate; gain1 = coeffs[n].Gain; gain2 = coeffs[n + 1].Gain; } } if (j < freq1) { gainDb = gain1; } else if (j > freq2) { gainDb = gain2; } else { // Raised Cosine: 0.5* ( cos(phi) + 1 ), from phi=pi to 2pi // double frac = (double)(j - freq1) / (double)(freq2 - freq1); double ph = Math.PI * (1 + frac); double rcos = (1 + Math.Cos(ph)) / 2; gainDb = gain1 + rcos * (gain2 - gain1); } gain = MathUtil.gain(gainDb); // Create a random phase value from 0 to 2pi double phi = NextRandom() * 2 * Math.PI; // Magnitude is 1, so just trig double re = Math.Cos(phi); double im = Math.Sin(phi); data[j] = new Complex(gain * logn * re, gain * logn * im); } // IFFT Fourier.IFFT((int)l, data); // Return the real component int k = 0; while (true) { yield return(data[k].Re * logn); k++; if (k >= l) { k = 0; } } }
ISoundObj CalculateSweep() { // Per http://www.anselmgoertz.de/Page10383/Monkey_Forest_dt/Manual_dt/aes-swp-english.pdf int fftSize = MathUtil.NextPowerOfTwo(_lengthSamples * 2); int N = fftSize / 2; // Center the sweep in time to reduce impact of its extremities double fNyq = _sr / 2; double FStart = _startFreq; double FEnd = _endFreq; double SStart = (N - _lengthSamples) / 2; double TStart = SStart / _sr; double TEnd = TStart + _lengthSecs; _B = (TEnd - TStart) / Math.Log(FEnd / FStart); _A = TStart - _B * Math.Log(FStart); // Make the complex spectrum //double ph = 0; double df = (double)_sr / N; double phiNyq = phi(fNyq); double phiAdj = phiNyq % (2 * Math.PI); if (!_gotdata) { _data = new Complex[fftSize]; fixed(Complex *cdata = _data) { for (int j = 0; j < N; j++) { int m = j + 1; double f = (double)m * _sr / fftSize; double ph = phi(f) - (f / fNyq) * phiAdj; double v = mag(f); double Re = Math.Cos(ph) * v; double Im = Math.Sin(ph) * v; _data[j] = new Complex(Re, Im); } Fourier.IFFT(fftSize, _data, Math.Sqrt(fftSize) * _gain * MathUtil.gain(20)); } // Look for values beyond the end // whose magnitude greater than our allowed threshold; // if present, window then ifft then start again. // Below doesn't seem to converge well // so just window and be done /* * double threshold = MathUtil.gain(-90); * bool iterate = true; * while (iterate) * { * iterate = false; * for (n = (int)(TEnd * sr + SStart * 2); n < fftSize; n++) * { * if (_data[n].Magnitude > threshold) * { * iterate = true; * break; * } * } * if (iterate) * { * Blackman bh = new Blackman((int)(_lengthSamples / 2 + SStart), _lengthSamples / 200, _lengthSamples / 2); * bh.Input = cbr; * n=0; * foreach (ISample s in bh) * { * _data[n++] = new Complex(s[0],0); * } * Fourier.FFT(fftSize, _data); * for (n = 0; n < N; n++) * { * int m = n + 1; * double f = (double)m * sr / fftSize; * double ph = _data[n].Phase; * double v = mag(f); * double Re = Math.Cos(ph) * v; * double Im = Math.Sin(ph) * v; * _data[n] = new Complex(Re, Im); * } * Fourier.IFFT(fftSize, _data); * } * } */ CosWindow bh = new Hamming((int)(_lengthSamples / 2 + SStart), (int)(SStart), _lengthSamples / 2); IEnumerator <double> gains = bh.Gains; for (int j = 0; j < N; j++) { gains.MoveNext(); double g = gains.Current; _data[j].mul(g); _data[fftSize - j - 1].mul(g); } _gotdata = true; } ComplexBufferReader cbr = new ComplexBufferReader(_data, 0, N); //bh.Input = cbr; return(cbr); }