public void PadToPowerOfTwo() { // Ensure that the buffer's total length is a power of two. int n = MathUtil.NextPowerOfTwo(_samples.Count); PadTo(n); }
/// <summary> /// Create a linear-phase filter impulse. /// </summary> /// <param name="nSize">Size: determines length of the final filter - suggest 4096 or 8192 (or 0 to auto-set)</param> /// <param name="coefficients">List of {frequency Hz, gain dB}</param> /// <param name="interpolation">type of interpolation (cosine)</param> public FilterImpulse(int nSize, FilterProfile coefficients, FilterInterpolation interpolation, uint sampleRate) { if (sampleRate == 0) { throw new Exception("Sample rate cannot be zero"); } _nSamples = nSize; _coeffs = coefficients; _int = interpolation; SampleRate = sampleRate; // Check the coefficients are sorted and non-overlapping _coeffs.Sort(new FreqGainComparer()); FilterProfile co = new FilterProfile(); _allZero = true; double prevFreq = -1; double freqDiff = double.MaxValue; foreach (FreqGain fg in _coeffs) { if (fg.Freq > prevFreq && fg.Freq >= 0) { co.Add(fg); freqDiff = Math.Min(freqDiff, fg.Freq - prevFreq); prevFreq = fg.Freq; } if (fg.Gain != 0) { _allZero = false; } } _coeffs = co; if (_nSamples == 0) { // Make up a length from 4k to 32k, based on the closest-together frequencies _nSamples = _allZero ? 1024 : Math.Max(4096, (int)Math.Min(32768.0, 327680 / freqDiff)); } _nSamples = MathUtil.NextPowerOfTwo(_nSamples); }
private static IEnumerator <double> WhiteFlat(int length) { // Generate "truly white" noise (flat spectrum) // by generating random-phase flat-magnitude in the frequency domain, then IFFT. // This noise is periodic - length (next power of 2 from 'n') int n = MathUtil.NextPowerOfTwo(length); Complex[] data = new Complex[n]; double logn = Math.Log(n); for (int j = 0; j < n; j++) { // 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(logn * re, logn * im); } // IFFT Fourier.IFFT((int)n, data); // Return the real component int k = 0; while (true) { yield return(data[k].Re * logn); k++; if (k >= n) { k = 0; } } }
private bool ComputePartitionedImpulseFFT() { if (_impulse == null) { return(false); } ushort nChannels; if (_input == null) { nChannels = _impulse.NumChannels; } else { nChannels = _input.NumChannels; } ushort p; int N = _impulseLength; int Nh = N << 1; int K = (int)(N / _partitions); int L = MathUtil.NextPowerOfTwo(K << 1); int P = (int)Math.Ceiling((double)N / (double)K); // Initialize the arrays of impulse-FFTs _PartitionedImpulseFFT = new Complex[nChannels][][]; Complex[][] tmp = new Complex[nChannels][]; for (ushort c = 0; c < nChannels; c++) { tmp[c] = new Complex[Nh]; _PartitionedImpulseFFT[c] = new Complex[P][]; for (p = 0; p < P; p++) { _PartitionedImpulseFFT[c][p] = new Complex[L]; } } // Read all samples from the impulse, & fft in blocks of size K (padded to L, L~=2K) int i = 0; int n = 0; p = 0; foreach (ISample sample in _impulse) { // Reading a segment of the impulse (into src[]) n++; if (n > _impulseLength) { break; } // source gave us more data than we'd expected for (ushort c = 0; c < _impulse.NumChannels; c++) { tmp[c][i + K] = new Complex(sample[c], 0); // TBD } i++; if (i >= K) { // Do the fft of this segment of the impulse, into fftImpulse[] for (ushort c = 0; c < nChannels; c++) { if (c >= _impulse.NumChannels) { // Handle the case where impulse has only one channel, but input (hence nChannels) is wider; // we already computed the fft in _PartitionedImpulseFFT[0][p], so just duplicate it. // In fact we can just ref the whole buffer -- no need even to make a deep copy _PartitionedImpulseFFT[c][p] = _PartitionedImpulseFFT[0][p]; } else { Array.Copy(tmp[c], _PartitionedImpulseFFT[c][p], L); Fourier.FFT(L, _PartitionedImpulseFFT[c][p]); } } i = 0; p++; } } _impulseLengthOrig = n - 1; if (p < P) { // Fill any extra space with nulls for (int ii = i; ii < K; ii++) { for (ushort c = 0; c < _impulse.NumChannels; c++) { tmp[c][ii + K] = new Complex(0, 0); // TBD } } // FFT the last segment for (ushort c = 0; c < nChannels; c++) { if (c >= _impulse.NumChannels) { // Handle the case where impulse has only one channel, but input (hence nChannels) is wider; // we already computed the fft in _PartitionedImpulseFFT[0][p], so just duplicate it. // In fact we can just ref the whole buffer -- no need even to make a deep copy _PartitionedImpulseFFT[c][p] = _PartitionedImpulseFFT[0][p]; } else { Array.Copy(tmp[c], _PartitionedImpulseFFT[c][p], L); Fourier.FFT(L, _PartitionedImpulseFFT[c][p]); } } } return(true); }
private void init() { _running = true; ComputeImpulseFFT(); nChannels = NumChannels; // Size for work area N = _impulseLength; nImpulseChannels = _impulse.NumChannels; Nh = N << 1; K = (_partitions <= 0) ? N : (N / _partitions); L = MathUtil.NextPowerOfTwo(K << 1); P = (int)Math.Ceiling((double)N / (double)K); // Trace.WriteLine("{0} partitions of size {1}, chunk size {2}", P, L, K); // Allocate buffers for the source and output src = new Complex[nChannels][]; output = new Complex[nChannels][]; for (ushort c = 0; c < nChannels; c++) { src[c] = new Complex[Nh]; output[c] = new Complex[Nh]; } // For partitioned convolution, allocate arrays for the fft accumulators if (_partitions > 0) { accum = new Complex[nChannels][][]; for (ushort c = 0; c < nChannels; c++) { accum[c] = new Complex[P][]; for (p = 0; p < P; p++) { accum[c][p] = new Complex[L]; } } } if (IsPersistTail) { // If there's any *unprocessed* leftover from a previous convolution, // load it into prevTailSamples before we begin if (System.IO.File.Exists(_persistFile)) { WaveReader tailReader = null; try { tailReader = new WaveReader(_persistFile); prevTailSamples = new List <ISample>(tailReader.Iterations); // Trace.WriteLine("Read {0} tail samples", tailReader.Iterations); if (tailReader.NumChannels == NumChannels) { foreach (ISample s in tailReader) { prevTailSamples.Add(s); } } prevTailEnum = prevTailSamples.GetEnumerator(); } catch (Exception e) { Trace.WriteLine("Could not read tail {0}: {1}", _persistFile.Replace(_persistPath, ""), e.Message); } // finally... // ThreadPool.QueueUserWorkItem(delegate(object o) // { try { if (tailReader != null) { tailReader.Close(); tailReader = null; } System.IO.File.Delete(_persistFile); } catch (Exception) { // ignore, we're done } // }); } } // Trace.WriteLine("{0} partitions of size {1}, chunk size {2}", P, L, K); }
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); }