public OverlappingWindows(ISoundObj input, int length) { _input = input; _length = length; _overlap = 0.5; _window = new Hamming(length / 2, length / 2); }
public OverlappingWindows(ISoundObj input, int length, double overlap, CosWindow window) { _input = input; _length = length; _overlap = overlap; _window = window; }
public void Add(ISoundObj input, List<double> channelGains) { if (_inputs.Count == 0) { // Treat this as 'Input'... Input = input; } _inputs.Add(input); _channelGains.Add(channelGains); }
public void Add(ISoundObj input, List <double> channelGains) { if (_inputs.Count == 0) { // Treat this as 'Input'... Input = input; } _inputs.Add(input); _channelGains.Add(channelGains); }
public static FilterProfile Profile(ISoundObj impulse, SmoothingType type, double resolution) { uint nSR = impulse.SampleRate; uint nSR2 = nSR / 2; ushort nChannels = impulse.NumChannels; for (ushort c = 0; c < nChannels; c++) { // Read channel into a buffer SingleChannel channel = impulse.Channel(c); SoundBuffer buff = new SoundBuffer(channel); buff.ReadAll(); // And then double in length to prevent wraparound buff.PadTo(buff.Count * 2); // Pad to next higher power of two buff.PadToPowerOfTwo(); // Read out into array of complex Complex[][] data = buff.ToComplexArray(); Complex[] cdata = data[0]; // Then we're done with the buffer for this channel buff = null; GC.Collect(); // FFT in place Fourier.FFT(cdata.Length, cdata); int n = cdata.Length / 2; // Now we have an array of complex, from 0Hz to Nyquist and back again. // We really only care about the first half of the cdata buffer, but // treat it as circular anyway (i.e. wrap around for negative values). // // We're only working with magnitudes from here on, // so we can save some space by computing mags right away and storing them in the // real part of the complex array; then we can use the imaginary portion for the // smoothed data. for (int j = 0; j < cdata.Length; j++) { cdata[j].Re = cdata[j].Magnitude; cdata[j].Im = 0; } // Take a rectangular window of width (resolution)*(octave or ERB band) // Add up all magnitudes falling within this window // // Move the window forward by one thingummajig //double wMid = 0; // center of the window //double wLen = 0; } return(new FilterProfile()); // temp }
// Add another source public void Add(ISoundObj input, double gainUnits) { if (input != null && !_hasInput) { // Treat this as 'Input'... Input = input; _hasInput = true; } _inputs.Add(input); _gains.Add(gainUnits); _nChannels += (input == null) ? (ushort)1 : input.NumChannels; }
/// <summary> /// Calculate the weighted volume of a sound source. /// NB: this consumes lots of memory for long sources. /// </summary> /// <param name="src"></param> /// <param name="dbSPL"></param> /// <returns>Volume (units, not dB)</returns> public static double WeightedVolume(ISoundObj src, double dbSPL, double dbSPLBase) { double wv = 0; for (ushort c = 0; c < src.NumChannels; c++) { SingleChannel channel = src.Channel(c); wv += Loudness.WeightedVolume1(channel, dbSPL, dbSPLBase); } src.Reset(); wv = wv / src.NumChannels; return(wv); }
/// <summary> /// Apply a first-order low pass IIR /// </summary> /// <param name="fC">Cutoff frequency, Hz</param> /// <param name="input">Input</param> public IIR1LP(double fC, ISoundObj input) { uint fS = input.SampleRate; base.SampleRate = fS; base.Input = input; double w = Math.Tan(Math.PI * fC / fS); double a = 1 / (1 + w); _b0 = w * a; _b1 = _b0; _a1 = a * (w - 1); }
/// <summary> /// Constructor /// </summary> /// <param name="length">Length for Hilbert FIR (preferably odd)</param> public HilbertEnvelope(int length) { if (length % 2 == 0) { length++; } _length = length; base.NumChannels = 1; // Imaginary portion is generated by a Hilbert transform of the real input _i = new FastConvolver(new Hilbert(length)); // Real portion is a delayed version of the real input _r = new FastConvolver(new Dirac(length)); }
/// <summary> /// Compute the filter profile of an impulse. /// NOTE: very memory-intensive! /// </summary> /// <param name="impulse">The impulse file</param> /// <param name="fractionsOfERB">1.0 for ERB-spaced bands. Smaller for less smoothing</param> public FilterProfile(ISoundObj impulse, double fractionsOfERB) : base() { /* * double[] muff = magbands(impulse, 300); * * // smooth the muff * double[] smoo = ERB.smooth(muff, (int)(38 / fractionsOfERB)); * * // sample frequency response at ERB band centers * FilterProfile lfg = ERB.profile(smoo, impulse.SampleRate, fractionsOfERB); */ FilterProfile lfg = Smoothing.Profile(impulse, SmoothingType.OCTAVE, fractionsOfERB); AddRange(lfg); }
// Add another source. Note: gain is units, not db public void Add(ISoundObj input, double gainUnits) { if (_inputs.Count == 0) { // Treat this as 'Input'... Input = input; } if (_inputs.Count > 0) { if (input.NumChannels != NumChannels) { throw new Exception("Mixer inputs must have the same number of channels."); } } _inputs.Add(input); _gains.Add(gainUnits); }
/// <summary> /// Calculate the weighted volume of a *single channel* sound source. /// NB: this consumes lots of memory for long sources. /// </summary> /// <param name="src"></param> /// <param name="dbSPL"></param> /// <returns>Volume (units, not dB)</returns> public static double WeightedVolume1(ISoundObj src, double dbSPL, double dbSPLBase) { if (src.NumChannels != 1) { throw new ArgumentException("Requires single-channel"); } // Read channel into a buffer SoundBuffer buff = new SoundBuffer(src); buff.ReadAll(); // And then double in length to prevent wraparound buff.PadTo(buff.Count * 2); // Pad to next higher power of two buff.PadToPowerOfTwo(); int n = buff.Count; double wvImpulse = WeightedVolume2(buff, dbSPL, dbSPLBase); // compare with a Dirac pulse the same length CallbackSource dirac = new CallbackSource(1, src.SampleRate, delegate(long j) { if (j >= n) { return(null); } double v = 0; if (j == n / 2) { v = 1; } return(new Sample(v)); }); buff = new SoundBuffer(dirac); buff.ReadAll(); double wvDirac = WeightedVolume2(buff, dbSPL, dbSPLBase); buff = null; GC.Collect(); return(wvImpulse / wvDirac); }
/// <summary> /// Constructor /// </summary> /// <param name="input">The input</param> /// <param name="impulseFilter">The impulse</param> public FastConvolver(ISoundObj input, ISoundObj impulseFilter) { Input = input; impulse = impulseFilter; }
public SingleChannel(ISoundObj input, ushort nChannel) { Input = input; _nChannel = nChannel; }
public SoundBuffer(ISoundObj input) { _samples = new List<ISample>(DSPUtil.BUFSIZE); Input = input; }
/// <summary> /// Calculate the weighted volume of a *single channel* sound source. /// NB: this consumes lots of memory for long sources. /// </summary> /// <param name="src"></param> /// <param name="dbSPL"></param> /// <returns>Volume (units, not dB)</returns> public static double WeightedVolume1(ISoundObj src, double dbSPL, double dbSPLBase) { if (src.NumChannels != 1) { throw new ArgumentException("Requires single-channel"); } // Read channel into a buffer SoundBuffer buff = new SoundBuffer(src); buff.ReadAll(); // And then double in length to prevent wraparound buff.PadTo(buff.Count * 2); // Pad to next higher power of two buff.PadToPowerOfTwo(); int n = buff.Count; double wvImpulse = WeightedVolume2(buff, dbSPL, dbSPLBase); // compare with a Dirac pulse the same length CallbackSource dirac = new CallbackSource(1, src.SampleRate, delegate(long j) { if (j >= n) { return null; } double v = 0; if (j == n / 2) { v = 1; } return new Sample(v); }); buff = new SoundBuffer(dirac); buff.ReadAll(); double wvDirac = WeightedVolume2(buff, dbSPL, dbSPLBase); buff = null; GC.Collect(); return wvImpulse / wvDirac; }
static void FindPeaks(ISoundObj impulse) { // Input: a single-channel impulse // Find the peak positions: // - unfiltered // - filtered with various bandpass filters uint sr = impulse.SampleRate; ushort nc = impulse.NumChannels; double peakM = Math.Round(MathUtil.Metres(_peakPosL, sr), 2); double peakFt = Math.Round(MathUtil.Feet(_peakPosL, sr), 2); stderr.WriteLine(" Impulse peak at sample {0} ({1}m, {2}ft)", _peakPosL, peakM, peakFt); FilterImpulse fi; WaveWriter wri; FastConvolver co; fi = new FilterImpulse(2048, bandpass(400,sr), FilterInterpolation.COSINE, sr); co = new FastConvolver(impulse, fi); wri = new WaveWriter("bp_400.wav", nc, sr, 16, DitherType.NONE, WaveFormat.PCM); wri.Input = co; wri.Normalization = -1; wri.Run(); wri.Close(); fi = new FilterImpulse(2048, bandpass(6000, sr), FilterInterpolation.COSINE, sr); co = new FastConvolver(impulse, fi); wri = new WaveWriter("bp_6000.wav", nc, sr, 16, DitherType.NONE, WaveFormat.PCM); wri.Input = co; wri.Normalization = -1; wri.Run(); wri.Close(); // and fi = new FilterImpulse(2048, bandpass(160, sr), FilterInterpolation.COSINE, sr); co = new FastConvolver(impulse, fi); wri = new WaveWriter("bp_160.wav", nc, sr, 16, DitherType.NONE, WaveFormat.PCM); wri.Input = co; wri.Normalization = -1; wri.Run(); wri.Close(); fi = new FilterImpulse(2048, bandpass(2560, sr), FilterInterpolation.COSINE, sr); co = new FastConvolver(impulse, fi); wri = new WaveWriter("bp_2560.wav", nc, sr, 16, DitherType.NONE, WaveFormat.PCM); wri.Input = co; wri.Normalization = -1; wri.Run(); wri.Close(); fi = new FilterImpulse(2048, bandpass(18000, sr), FilterInterpolation.COSINE, sr); co = new FastConvolver(impulse, fi); wri = new WaveWriter("bp_18k.wav", nc, sr, 16, DitherType.NONE, WaveFormat.PCM); wri.Input = co; wri.Normalization = -1; wri.Run(); wri.Close(); }
public Padder(ISoundObj input, int pad) { Input = input; _pad = pad; }
public SoundBuffer(ISoundObj input) { _samples = new List <ISample>(DSPUtil.BUFSIZE); Input = input; }
public TwoChannel(ISoundObj input, ushort nChannel1, ushort nChannel2) { Input = input; _nChannel1 = nChannel1; _nChannel2 = nChannel2; }
public SingleChannel(ISoundObj input, ushort nChannel, bool okIfChannelNotFound) { Input = input; _nChannel = nChannel; _okIfChannelNotFound = okIfChannelNotFound; }
/// <summary> /// Constructor /// </summary> /// <param name="input">The input</param> /// <param name="nChannels">A zero-based list of channels which will be mapped from the input channels. /// Use -1 to map null output. /// e.g stereo input: params {1,0}: output is stereo with left and right swapped /// e.g. stereo input, params {-1,0,1,-1}: output is 4-channel with left in second channel and right in third. /// </param> public ChannelSwapper(ISoundObj input, params ushort[] channels) { Input = input; _channels = channels; }
public ChannelInvertor(ISoundObj input, params bool[] invert) { Input = input; _invert = invert; }
private static ISoundObj GetFlatnessFilter(string impulsePath, ISoundObj impulseObj, double nFlatness) { ISoundObj filter = null; if (impulseObj != null && impulsePath != null && nFlatness!=100) { uint sr = impulseObj.SampleRate; if (_debug) { Trace.WriteLine(FlatnessFilterName(impulsePath, sr, nFlatness)); } // Do we already have a cached flatness-filter? // (Building these things is expensive...) string sFile = FlatnessFilterPath(impulsePath, sr, nFlatness); if (File.Exists(sFile)) { try { filter = new WaveReader(sFile); } catch (Exception e) { if (_debug) { Trace.WriteLine("GetFlatnessFilter: " + e.Message); } } } if (filter == null) { // Generate a new flatness filter then return GenerateFlatnessFilter(impulsePath, impulseObj, nFlatness); } } return filter; }
static ISoundObj SlidingLowPass(ISoundObj impulse, int peakPos) { // Filter the impulse response with a sliding lowpass filter // - Take the first (peakpos) samples unchanged // - Take the next 2.5ms (~110 samples @ 44100) unchanged // - Fade to a lowpass-filtered version uint sampleRate = impulse.SampleRate; int startpos1 = (int)(sampleRate * 0.0025); // 2.5mS, 110 samples int startpos2 = (int)(sampleRate * 0.0050); int startpos3 = (int)(sampleRate * 0.0100); int startpos4 = (int)(sampleRate * 0.0200); int startpos5 = (int)(sampleRate * 0.0400); List<IEnumerator<ISample>> filters = null; int nFilter = 0; int nFilters = 0; int x = 0; int f0len = 0; ISoundObj filteredImpulse = new CallbackSource(1, sampleRate, delegate(long j) { if(j==0) { // initialize the list of filters impulse.Reset(); filters = new List<IEnumerator<ISample>>(6); ISoundObj f0 = LowPassFiltered(impulse, 20, 0); f0len = f0.Iterations; filters.Add(f0.Samples); // unfiltered filters.Add(LowPassFiltered(impulse, 640, -10).Samples); // LPF from 640Hz, kick in at +110 filters.Add(LowPassFiltered(impulse, 320, -20).Samples); // LPF from 320Hz, kick in at +220 (etc) filters.Add(LowPassFiltered(impulse, 160, -30).Samples); // +440 filters.Add(LowPassFiltered(impulse, 80, -40).Samples); // +880 filters.Add(LowPassFiltered(impulse, 40, -50).Samples); // +1760 = 40ms nFilter = 0; nFilters = filters.Count; } // Move the filters along a notch. // (or, right at the beginning, move all the filters along by a lot, compensating for their center) // For perf, we don't need to keep enumerating the filters we've finished using. bool moreData = true; int nSkip = 1; if (j == 0) { nSkip = (f0len/2)+1; } for (int skip = 0; skip < nSkip; skip++) { int nStart = (nFilter == 0) ? 0 : nFilter - 1; // Keep moving even the old filters along so mixing works later for (int f = nStart; f < nFilters; f++) { moreData &= filters[f].MoveNext(); } } if (!moreData) return null; // Decide which filter we'll use x++; if (j == peakPos + startpos1) { nFilter = 1; x = 0; } if (j == peakPos + startpos2) { nFilter = 2; x = 0; } if (j == peakPos + startpos3) { nFilter = 3; x = 0; } if (j == peakPos + startpos4) { nFilter = 4; x = 0; } if (j == peakPos + startpos5) { nFilter = 5; x = 0; } // Pick the sample from the current filter ISample s = filters[nFilter].Current; // For a short region after switch-over, mix slowly with the sample from the previous filter int w = 20; if (x < w && nFilter > 0) { ISample sPrev = filters[nFilter-1].Current; for (int c = 0; c < s.NumChannels; c++) { s[c] = ((double)x/w)*s[c] + ((double)(w-x)/w)*sPrev[c]; } } return s; }); return filteredImpulse; }
/// <summary> /// Create a circular buffer with a given size /// </summary> /// <param name="input">Input stream</param> /// <param name="bufsize">Size of the circular buffer</param> public CircularBuffer(ISoundObj input, uint bufsize) { _data = new ISample[bufsize]; _length = bufsize; Input = input; }
static SoundObj GetEQImpulse(ISoundObj mainImpulse, uint sampleRate, out string filterName) { DateTime dtStart = DateTime.Now; SoundObj filterImpulse = null; // Construct a string describing the filter string filterDescription = "EQ" + _eqBands + "_" + _inputSampleRate + "_" + _eqLoudness + "_" + FlatnessFilterPath(_impulsePath, sampleRate, _eqFlatness); bool nothingToDo = (_eqLoudness==0); nothingToDo &= (_impulsePath == null) || (_eqFlatness == 100); List<string> fgd = new List<string>(); foreach (FreqGain fg in _eqValues) { fgd.Add(fg.Freq + "@" + fg.Gain); nothingToDo &= (fg.Gain == 0); } filterDescription = filterDescription + String.Join("_", fgd.ToArray()); filterDescription = filterDescription + "_IM_" + _impulsePath; // Cached filters are named by hash of this string filterName = "EQ" + filterDescription.GetHashCode().ToString("x10").ToUpperInvariant(); if (nothingToDo) { Trace.WriteLine("EQ flat"); WriteJSON(_eqValues, null, null); return null; } else { Trace.WriteLine(filterName); } string filterFile = Path.Combine(_tempFolder, filterName + ".filter"); // Does the cached filter exist? if (File.Exists(filterFile)) { try { // Just read the cached EQ filter from disk filterImpulse = new WaveReader(filterFile); } catch (Exception e) { if (_debug) { Trace.WriteLine("GetEQImpulse1: " + e.Message); } } } if(filterImpulse==null) { // Construct a filter impulse from the list of EQ values SoundObj eqFilter = new FilterImpulse(0, _eqValues, FilterInterpolation.COSINE, _inputSampleRate); filterImpulse = eqFilter; ISoundObj qtFilter = GetQuietnessFilter(_inputSampleRate, _eqLoudness); if (qtFilter != null) { // Convolve the two, to create a EQ-and-loudness filter FastConvolver tmpConvolver = new FastConvolver(); tmpConvolver.partitions = 0; tmpConvolver.impulse = qtFilter; tmpConvolver.Input = eqFilter; filterImpulse = tmpConvolver; } ISoundObj ftFilter = GetFlatnessFilter(_impulsePath, mainImpulse, _eqFlatness); if (ftFilter != null) { // Convolve the two, to create a EQ-and-loudness filter FastConvolver tmpConvolver2 = new FastConvolver(); tmpConvolver2.partitions = 0; tmpConvolver2.impulse = filterImpulse; tmpConvolver2.Input = ftFilter; filterImpulse = tmpConvolver2; } // Blackman window to make the filter smaller? try { // Write the filter impulse to disk WaveWriter wri = new WaveWriter(filterFile); wri.Input = filterImpulse; wri.Format = WaveFormat.IEEE_FLOAT; wri.BitsPerSample = 64; wri.Run(); wri.Close(); if (_debug) { // DEBUG: Write the filter impulse as wav16 wri = new WaveWriter(filterFile + ".wav"); wri.Input = filterImpulse; wri.Format = WaveFormat.PCM; wri.BitsPerSample = 16; wri.Normalization = -1.0; wri.Dither = DitherType.NONE;//.TRIANGULAR; wri.Run(); wri.Close(); } // Write a JSON description of the filter WriteJSON(_eqValues, filterImpulse, filterName); } catch (Exception e) { if (_debug) { Trace.WriteLine("GetEQImpulse2: " + e.Message); } } } filterImpulse.Reset(); if (_debug) { TimeSpan ts = DateTime.Now.Subtract(dtStart); Trace.WriteLine("GetEQImpulse " + ts.TotalMilliseconds); } // Copy the filter's JSON description (if available) into "current.json" CopyJSON(filterName); return filterImpulse; }
public SampleBuffer(ISoundObj input) { Input = input; Reset(); }
/// <summary> /// Calculate the weighted volume of a sound source. /// NB: this consumes lots of memory for long sources. /// </summary> /// <param name="src"></param> /// <param name="dbSPL"></param> /// <returns>Volume (units, not dB)</returns> public static double WeightedVolume(ISoundObj src, double dbSPL, double dbSPLBase) { double wv = 0; for (ushort c = 0; c < src.NumChannels; c++) { SingleChannel channel = src.Channel(c); wv += Loudness.WeightedVolume1(channel, dbSPL, dbSPLBase); } src.Reset(); wv = wv / src.NumChannels; return wv; }
private static ISoundObj GenerateFlatnessFilter(string impulsePath, ISoundObj impulseObj, double nFlatness) { // Flatness-filters are single channel // nFlatness is from 0 to 100 // 0: follow the contours of the impulse // 100: completely flat DateTime dtStart = DateTime.Now; ISoundObj filterImpulse = null; uint nSR = impulseObj.SampleRate; uint nSR2 = nSR / 2; string sPath = FlatnessFilterPath(impulsePath, nSR, nFlatness); // Low flatness values (to 0) => very un-smooth // High flatness values (to 100) => very smooth double detail = (nFlatness / 50) + 0.05; // Get profile of the impulse FilterProfile lfg = new FilterProfile(impulseObj, detail); // Scale by the flatness values lfg = lfg * ((100 - nFlatness) / 100); // Invert lfg = lfg.Inverse(20); // Zero at HF lfg.Add(new FreqGain(nSR2 - 100, 0)); // Build the flatness filter filterImpulse = new FilterImpulse(8192, lfg, FilterInterpolation.COSINE, nSR); try { // Write the filter impulse to disk WaveWriter wri = new WaveWriter(sPath); wri.Input = filterImpulse; wri.Format = WaveFormat.IEEE_FLOAT; wri.BitsPerSample = 64; wri.Run(); wri.Close(); if (_debug) { // DEBUG: Write the flatness impulse as wav16 wri = new WaveWriter(sPath + ".wav"); wri.Input = filterImpulse; wri.Format = WaveFormat.PCM; wri.BitsPerSample = 16; wri.Normalization = -1.0; wri.Dither = DitherType.NONE;//.TRIANGULAR; wri.Run(); wri.Close(); } } catch (Exception e) { if (_debug) { Trace.WriteLine("GenerateFlatnessFilter: " + e.Message); } } impulseObj.Reset(); if (_debug) { TimeSpan ts = DateTime.Now.Subtract(dtStart); Trace.WriteLine("GenerateFlatnessFilter " + ts.TotalMilliseconds); } return filterImpulse; }
private bool _moreSamples = true; // there are samples in the source which we have not yet read public SampleEnum(ISoundObj obj) { _obj = obj; _nc = _obj.NumChannels; Reset(); }
public void Dispose() { _enum.Dispose(); _enum = null; _obj = null; }
public SoundObj(ISoundObj input) { Input = input; }
/// <summary> /// Constructor /// </summary> /// <param name="impulseFilter">The impulse</param> public FastConvolver(ISoundObj impulseFilter) { impulse = impulseFilter; }
// Add another source public void Add(ISoundObj input) { Add(input, new List<double>()); }
// Add another source public void Add(ISoundObj input) { Add(input, 1.0); }
public WindowedBuffer(ISoundObj input, CosWindow window, int start, int count) { _buff = new SoundBuffer(new SampleBuffer(input).Subset(start, count)); _buff.ApplyWindow(window); }
private static double[] magbands(ISoundObj impulse, double bins) { uint nSR = impulse.SampleRate; uint nSR2 = nSR / 2; int nn = (int)bins + 1; double[] muff = new double[nn]; ushort nChannels = impulse.NumChannels; for (ushort c = 0; c < nChannels; c++) { // Read channel into a buffer SingleChannel channel = impulse.Channel(c); SoundBuffer buff = new SoundBuffer(channel); buff.ReadAll(); // And then double in length to prevent wraparound buff.PadTo(buff.Count * 2); // Pad to next higher power of two buff.PadToPowerOfTwo(); // Read out into array of complex Complex[][] data = buff.ToComplexArray(); Complex[] cdata = data[0]; // Then we're done with the buffer for this channel buff = null; GC.Collect(); // FFT in place Fourier.FFT(cdata.Length, cdata); int n = cdata.Length / 2; // Drop the FFT magnitudes into the 'muff' array // according to an ERB-based scale (near-logarithmic). // Then smoothing is easy. double binw = (nSR2 / (double)n); int prevbin = 0; int nbin = 0; double v = 0; for (int j = 0; j < n; j++) { double f = (double)j * binw; // equiv freq, Hz int bin = (int)ERB.f2bin(f, nSR, bins); // the bin we drop this sample in v += cdata[j].Magnitude; nbin++; if ((bin > prevbin) || (j == n - 1)) { muff[prevbin] += (v / nbin); v = 0; nbin = 0; prevbin = bin; } } } // Now muff is sum(all channels) of average-magnitude-per-bin. // Divide it all by the number of channels, so our gains are averaged... for (int j = 0; j < muff.Length; j++) { muff[j] = muff[j] / nChannels; } return(muff); }
static void WriteImpulsePCM(ISoundObj stereoImpulse, string fileNameL, string fileNameR) { ISoundObj sb; WaveWriter writer; ushort bits = 32; int nTot = 196607; int nBef = 98300; // need padding before the sample. // window the sample first, then pad it. // Total length 65536, includes nPad and _peakPosL before impulse int nPad = nBef - _peakPosL; int usableLength = nTot - nPad; int windowCenter = usableLength / 2; int windowSide = _peakPosL * 2 / 3; int windowFlat = windowCenter - windowSide; ISoundObj ch = new SingleChannel(stereoImpulse, 0); ISoundObj wn = new BlackmanHarris(windowCenter, windowSide, windowFlat); wn.Input = ch; sb = new SampleBuffer(wn).PaddedSubset(-nPad, nTot); writer = new WaveWriter(fileNameL); writer.Input = sb; writer.Format = WaveFormat.IEEE_FLOAT; writer.BitsPerSample = bits; writer.SampleRate = _sampleRate; writer.Raw = true; writer.Normalization = -6.0; writer.NormalizationType = NormalizationType.PEAK_DBFS; writer.Run(); writer.Close(); double g = writer.Gain; writer = null; nPad = nBef - _peakPosR; usableLength = nTot - nPad; windowCenter = usableLength / 2; windowSide = _peakPosR * 2 / 3; windowFlat = windowCenter - windowSide; ch = new SingleChannel(stereoImpulse, 1); wn = new BlackmanHarris(windowCenter, windowSide, windowFlat); wn.Input = ch; sb = new SampleBuffer(wn).PaddedSubset(-nPad, nTot); writer = new WaveWriter(fileNameR); writer.Input = sb; writer.Format = WaveFormat.IEEE_FLOAT; writer.BitsPerSample = bits; writer.SampleRate = _sampleRate; writer.Raw = true; writer.Gain = g; writer.Run(); writer.Close(); writer = null; }
// Add another source public void Add(ISoundObj input) { Add(input, new List <double>()); }
static ISoundObj LowPassFiltered(ISoundObj input, double freqStart, double gainEnd) { uint sampleRate = input.SampleRate; FilterProfile lfg = new FilterProfile(); lfg.Add(new FreqGain(freqStart, 0)); lfg.Add(new FreqGain(0.499*sampleRate, gainEnd)); FastConvolver conv = new FastConvolver(); conv.Input = input; conv.impulse = new FilterImpulse(8192, lfg, FilterInterpolation.COSINE, sampleRate); return conv; }
/// <summary> /// Calculate the weighted volume of a sound source. /// NB: this consumes lots of memory for long sources. /// </summary> /// <param name="src"></param> /// <returns>Volume (units, not dB)</returns> public static double WeightedVolume(ISoundObj src) { return(WeightedVolume(src, 40, 0)); }
public static FilterProfile Profile(ISoundObj impulse, SmoothingType type, double resolution) { uint nSR = impulse.SampleRate; uint nSR2 = nSR / 2; ushort nChannels = impulse.NumChannels; for (ushort c = 0; c < nChannels; c++) { // Read channel into a buffer SingleChannel channel = impulse.Channel(c); SoundBuffer buff = new SoundBuffer(channel); buff.ReadAll(); // And then double in length to prevent wraparound buff.PadTo(buff.Count * 2); // Pad to next higher power of two buff.PadToPowerOfTwo(); // Read out into array of complex Complex[][] data = buff.ToComplexArray(); Complex[] cdata = data[0]; // Then we're done with the buffer for this channel buff = null; GC.Collect(); // FFT in place Fourier.FFT(cdata.Length, cdata); int n = cdata.Length / 2; // Now we have an array of complex, from 0Hz to Nyquist and back again. // We really only care about the first half of the cdata buffer, but // treat it as circular anyway (i.e. wrap around for negative values). // // We're only working with magnitudes from here on, // so we can save some space by computing mags right away and storing them in the // real part of the complex array; then we can use the imaginary portion for the // smoothed data. for (int j = 0; j < cdata.Length; j++) { cdata[j].Re = cdata[j].Magnitude; cdata[j].Im = 0; } // Take a rectangular window of width (resolution)*(octave or ERB band) // Add up all magnitudes falling within this window // // Move the window forward by one thingummajig //double wMid = 0; // center of the window //double wLen = 0; } return new FilterProfile(); // temp }
/// <summary> /// Calculate the weighted volume of a sound source. /// NB: this consumes lots of memory for long sources. /// </summary> /// <param name="src"></param> /// <returns>Volume (units, not dB)</returns> public static double WeightedVolume(ISoundObj src) { return WeightedVolume(src, 40, 0); }