Beispiel #1
0
 public OverlappingWindows(ISoundObj input, int length)
 {
     _input = input;
     _length = length;
     _overlap = 0.5;
     _window = new Hamming(length / 2, length / 2);
 }
Beispiel #2
0
 public OverlappingWindows(ISoundObj input, int length, double overlap, CosWindow window)
 {
     _input = input;
     _length = length;
     _overlap = overlap;
     _window = window;
 }
Beispiel #3
0
 public OverlappingWindows(ISoundObj input, int length)
 {
     _input   = input;
     _length  = length;
     _overlap = 0.5;
     _window  = new Hamming(length / 2, length / 2);
 }
Beispiel #4
0
 public OverlappingWindows(ISoundObj input, int length, double overlap, CosWindow window)
 {
     _input   = input;
     _length  = length;
     _overlap = overlap;
     _window  = window;
 }
Beispiel #5
0
 public void Add(ISoundObj input, List<double> channelGains)
 {
     if (_inputs.Count == 0)
     {
         // Treat this as 'Input'...
         Input = input;
     }
     _inputs.Add(input);
     _channelGains.Add(channelGains);
 }
Beispiel #6
0
 public void Add(ISoundObj input, List <double> channelGains)
 {
     if (_inputs.Count == 0)
     {
         // Treat this as 'Input'...
         Input = input;
     }
     _inputs.Add(input);
     _channelGains.Add(channelGains);
 }
Beispiel #7
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
        }
Beispiel #8
0
 // 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;
 }
Beispiel #9
0
        /// <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);
        }
Beispiel #10
0
        /// <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);
        }
Beispiel #11
0
        /// <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);
        }
Beispiel #13
0
 // 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);
 }
Beispiel #14
0
        /// <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);
        }
Beispiel #15
0
 /// <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;
 }
Beispiel #16
0
 public SingleChannel(ISoundObj input, ushort nChannel)
 {
     Input     = input;
     _nChannel = nChannel;
 }
Beispiel #17
0
 public SoundBuffer(ISoundObj input)
 {
     _samples = new List<ISample>(DSPUtil.BUFSIZE);
     Input = input;
 }
 // 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;
 }
Beispiel #19
0
        /// <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;
        }
Beispiel #20
0
        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();
        }
Beispiel #21
0
 public Padder(ISoundObj input, int pad)
 {
     Input = input;
     _pad  = pad;
 }
Beispiel #22
0
 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;
 }
 public SingleChannel(ISoundObj input, ushort nChannel)
 {
     Input = input;
     _nChannel = nChannel;
 }
 /// <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;
 }
Beispiel #28
0
 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;
 }
Beispiel #29
0
        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;
        }
Beispiel #30
0
 /// <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;
 }
Beispiel #31
0
        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;
        }
Beispiel #32
0
 public SampleBuffer(ISoundObj input)
 {
     Input = input;
     Reset();
 }
Beispiel #33
0
 /// <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;
 }
Beispiel #34
0
 public TwoChannel(ISoundObj input, ushort nChannel1, ushort nChannel2)
 {
     Input      = input;
     _nChannel1 = nChannel1;
     _nChannel2 = nChannel2;
 }
Beispiel #35
0
        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;
        }
Beispiel #36
0
        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();
        }
Beispiel #37
0
 public SampleBuffer(ISoundObj input)
 {
     Input = input;
     Reset();
 }
Beispiel #38
0
 public void Dispose()
 {
     _enum.Dispose();
     _enum = null;
     _obj  = null;
 }
Beispiel #39
0
 // 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);
 }
Beispiel #40
0
 public SoundObj(ISoundObj input)
 {
     Input = input;
 }
Beispiel #41
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="impulseFilter">The impulse</param>
 public FastConvolver(ISoundObj impulseFilter)
 {
     impulse = impulseFilter;
 }
Beispiel #42
0
 // Add another source
 public void Add(ISoundObj input)
 {
     Add(input, new List<double>());
 }
Beispiel #43
0
 // Add another source
 public void Add(ISoundObj input)
 {
     Add(input, 1.0);
 }
Beispiel #44
0
 /// <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;
 }
Beispiel #45
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);
        }
Beispiel #47
0
        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;
        }
Beispiel #48
0
 // Add another source
 public void Add(ISoundObj input)
 {
     Add(input, new List <double>());
 }
Beispiel #49
0
 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;
 }
Beispiel #50
0
 public ChannelInvertor(ISoundObj input, params bool[] invert)
 {
     Input   = input;
     _invert = invert;
 }
Beispiel #51
0
 /// <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));
 }
Beispiel #52
0
        /// <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));
        }
Beispiel #53
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
        }
Beispiel #54
0
 public SingleChannel(ISoundObj input, ushort nChannel, bool okIfChannelNotFound)
 {
     Input                = input;
     _nChannel            = nChannel;
     _okIfChannelNotFound = okIfChannelNotFound;
 }
Beispiel #55
0
 /// <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);
 }
Beispiel #56
0
 /// <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;
 }
Beispiel #57
0
 public WindowedBuffer(ISoundObj input, CosWindow window, int start, int count)
 {
     _buff = new SoundBuffer(new SampleBuffer(input).Subset(start, count));
     _buff.ApplyWindow(window);
 }
Beispiel #58
0
 public Padder(ISoundObj input, int pad)
 {
     Input = input;
     _pad = pad;
 }
Beispiel #59
0
 /// <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);
 }
 // Add another source
 public void Add(ISoundObj input)
 {
     Add(input, 1.0);
 }