Inheritance: SoundObj, ISampleBuffer
Example #1
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
        }
Example #2
0
        private ISoundObj _buff()
        {
            if (double.IsNaN(_normalization))
            {
                return(_input);
            }
            // We've been asked to normalize
            SoundBuffer b = new SoundBuffer(_input);

            b.ReadAll();
            _gain = b.Normalize(_normalization, false);
            return(b);
        }
Example #3
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);
        }
Example #4
0
 public WindowedBuffer(ISoundObj input, CosWindow window, int start, int count)
 {
     _buff = new SoundBuffer(new SampleBuffer(input).Subset(start, count));
     _buff.ApplyWindow(window);
 }
Example #5
0
        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;
        }
Example #6
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;
        }
Example #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
        }
Example #8
0
        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);
        }
Example #9
0
        static void Main(string[] args)
        {
            // Find where this executable is launched from
            string[] cargs = Environment.GetCommandLineArgs();
            _thisFolder = Path.GetDirectoryName(cargs[0]);
            if (String.IsNullOrEmpty(_thisFolder))
            {
                _thisFolder = Environment.CurrentDirectory;
            }

            string appData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            _impulsesFolder = Path.GetFullPath(Path.Combine(appData, "InguzEQ" + slash + "Impulses" + slash));

            string[] inFiles = new string[4];
            string inL = "";
            string inR = "";
            if (!DisplayInfo())
            {
                return;
            }

            bool ok = (args.Length > 0);
            bool longUsage = false;

            for (int j = 0; ok && j < args.Length; j++)
            {
                string arg = args[j];
                switch (args[j].ToUpperInvariant())
                {
                    case "/?":
                    case "-?":
                    case "/H":
                    case "/HELP":
                        ok = false;
                        longUsage = true;
                        break;

                    case "/L":
                    case "/0":
                        inFiles[0] = args[++j];
                        _nInFiles = Math.Max(_nInFiles, 1);
                        break;

                    case "/R":
                    case "/1":
                        inFiles[1] = args[++j];
                        _nInFiles = Math.Max(_nInFiles, 2);
                        break;

                    case "/2":
                        inFiles[2] = args[++j];
                        _nInFiles = Math.Max(_nInFiles, 3);
                        break;
                    case "/3":
                        inFiles[3] = args[++j];
                        _nInFiles = Math.Max(_nInFiles, 4);
                        break;

                    case "/LENGTH":
                        _filterLen = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        if (_filterLen < 16)
                        {
                            throw new Exception("Length is too small.");
                        }
                        break;

                    case "/DBL":
                        _dbl = true;
                        break;

                    case "/PCM":
                        _pcm = true;
                        break;

                    case "/NODRC":
                        _noDRC = true;
                        break;

                    case "/NOSKEW":
                        _noSkew = true;
                        break;

                    case "/NONORM":
                        // No normalization of the impulse response (undocumented)
                        _noNorm = true;
                        break;

                    case "/SPLIT":
                        _split = true;
                        break;

                    case "/COPY":
                        _copy = true;
                        break;

                    case "/GAIN":
                        _gain = double.Parse(args[++j], CultureInfo.InvariantCulture);
                        break;

                    case "/ALL":
                        // Returns negative-time components as part of the impulse response
                        // (experimental, to be used for THD measurement)
                        _returnAll = true;
                        break;

                    case "/POWER":
                        // Raises sweep to power n
                        // (experimental, to be used for THD measurement)
                        _power = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        break;

                    case "/FMIN":
                        // (experimental, i.e. broken)
                        _fmin = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        _fminSpecified = true;
                        break;

                    case "/FMAX":
                        // (experimental, i.e. broken)
                        _fmax = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        _fmaxSpecified = true;
                        break;

                    case "/DIRECT":
                        // Create filtered (direct-sound) filters
                        _doDirectFilters = true;
                        break;

                    case "/NOSUB":
                        // Don't apply subsonic filter to the impulse response
                        _noSubsonicFilter = true;
                        break;

                    case "/NOOVER":
                        // Don't override DRC's settings for filter type and length
                        _noOverrideDRC = true;
                        break;

                    case "/KEEPTEMP":
                        // Undocumented
                        _keepTempFiles = true;
                        break;

                    case "/REFCH":
                        // Override the reference-channel detection
                        _refchannel = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        if (_refchannel<0 || _refchannel > _nInFiles - 1)
                        {
                            throw new Exception(String.Format("RefCh can only be from 0 to {0}.", _nInFiles-1));
                        }
                        break;

                    case "/ENV":
                        // Undocumented.  Save the Hilbert envelope
                        _env = true;
                        break;

                    case "-":
                        // ignore
                        break;

                    default:
                        ok = false;
                        break;
                }
            }
            if (!ok)
            {
                DisplayUsage(longUsage);
            }
            else
            {
                try
                {
                    if (!_noDRC)
                    {
                        if (!File.Exists(GetDRCExe()))
                        {
                            stderr.WriteLine("Denis Sbragion's DRC (http://drc-fir.sourceforge.net/) was not found.");
                            stderr.WriteLine("Only the impulse response will be calculated, not correction filters.");
                            stderr.WriteLine("");
                            _noDRC = true;
                        }
                    }
                    if (!_noDRC)
                    {
                        FileInfo[] drcfiles = new DirectoryInfo(_thisFolder).GetFiles("*.drc");
                        if (drcfiles.Length == 0)
                        {
                            stderr.WriteLine("No .drc files were found in the current folder.");
                            stderr.WriteLine("Only the impulse response will be calculated, not correction filters.");
                            stderr.WriteLine("");
                            _noDRC = true;
                        }
                    }

                    for(int i=0; i<_nInFiles; i++)
                    {
                        string inFile = inFiles[i];
                        if (String.IsNullOrEmpty(inFile))
                        {
                            stderr.WriteLine("Error: The {0} input file was not specified.", FileDescription(i));
                            return;
                        }
                        if (!File.Exists(inFile))
                        {
                            stderr.WriteLine("Error: The {0} input file {1} was not found.", FileDescription(i), inFile);
                            return;
                        }

                        for (int j = 0; j < i; j++)
                        {
                            if (inFile.Equals(inFiles[j]))
                            {
                                stderr.WriteLine("Warning: The same input file ({0}) was specified for both {1} and {2}!", inFile, FileDescription(j), FileDescription(i));
                                //stderr.WriteLine();
                            }
                        }
                    }

                    // Temporary
                    if (_nInFiles != 2)
                    {
                        stderr.WriteLine("Error: Two input files must be specified.");
                        return;
                    }
                    inL = inFiles[0];
                    inR = inFiles[1];
                    // end temporary

                    uint sampleRate;
                    List<SoundObj> impulses;
                    List<ISoundObj> filteredImpulses;
                    List<string> impDirects;
                    List<Complex[]> impulseFFTs;
                    List<double> maxs;

                    SoundObj impulseL;
                    SoundObj impulseR;
                    ISoundObj filteredImpulseL = null;
                    ISoundObj filteredImpulseR = null;

                    string impDirectL = null;
                    string impDirectR = null;

                    Complex[] impulseLFFT;
                    Complex[] impulseRFFT;
                    WaveWriter writer;

                    ISoundObj buff;
                    double g;

                    if (!_keepTempFiles)
                    {
                        _tempFiles.Add("rps.pcm");
                        _tempFiles.Add("rtc.pcm");
                    }

                    // Find the left impulse
                    stderr.WriteLine("Processing left measurement ({0})...", inL);
                    impulseL = Deconvolve(inL, out impulseLFFT, out _peakPosL);
                    sampleRate = impulseL.SampleRate;
                    _sampleRate = sampleRate;
                    double peakM = Math.Round(MathUtil.Metres(_peakPosL, sampleRate), 2);
                    double peakFt = Math.Round(MathUtil.Feet(_peakPosL, sampleRate), 2);
                    stderr.WriteLine("  Impulse peak at sample {0} ({1}m, {2}ft)", _peakPosL, peakM, peakFt);

                    // Write to PCM
                    string impFileL = Path.GetFileNameWithoutExtension(inL) + "_imp" + ".pcm";
                    if (!_keepTempFiles)
                    {
                        _tempFiles.Add(impFileL);
                    }
                    writer = new WaveWriter(impFileL);
                    writer.Input = impulseL;
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Raw = true;
                    writer.Run();
                    writer.Close();

                    // Write the impulseFFT to disk
                    int L = impulseLFFT.Length;
                    string impTempL = Path.GetFileNameWithoutExtension(inL) + "_imp" + ".dat";
                    _tempFiles.Add(impTempL);
                    writer = new WaveWriter(impTempL);
                    writer.Input = new CallbackSource(2, sampleRate, delegate(long j)
                    {
                        if (j >= L / 2)
                        {
                            return null;
                        }
                        Complex si = impulseLFFT[j]; // +impulseLFFT[L - j - 1];
                        ISample s = new Sample2();
                        s[0] = si.Magnitude;
                        s[1] = si.Phase / Math.PI;
                        return s;
                    });
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Raw = false;
                    writer.Run();
                    writer.Close();
                    writer = null;

                    impulseLFFT = null;
                    GC.Collect();

                    if (_doDirectFilters)
                    {
                        // Sliding low-pass filter over the impulse
                        stderr.WriteLine("  Filtering...");
                        filteredImpulseL = SlidingLowPass(impulseL, _peakPosL);

                        // Write PCM for the filtered impulse
                        impDirectL = Path.GetFileNameWithoutExtension(inL) + "_impfilt" + ".pcm";
                        if (!_keepTempFiles)
                        {
                            _tempFiles.Add(impDirectL);
                        }
                        writer = new WaveWriter(impDirectL);
                        writer.Input = filteredImpulseL;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.SampleRate = _sampleRate;
                        writer.BitsPerSample = 32;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;
                        filteredImpulseL.Reset();
                    }

                    GC.Collect();
                    stderr.WriteLine("  Deconvolution: left impulse done.");
                    stderr.WriteLine();

                    // Find the right impulse
                    stderr.WriteLine("Processing right measurement ({0})...", inR);
                    impulseR = Deconvolve(inR, out impulseRFFT, out _peakPosR);
                    peakM = Math.Round(MathUtil.Metres(_peakPosR, sampleRate), 2);
                    peakFt = Math.Round(MathUtil.Feet(_peakPosR, sampleRate), 2);
                    stderr.WriteLine("  Impulse peak at sample {0} ({1}m, {2}ft)", _peakPosR, peakM, peakFt);

                    // Write to PCM
                    string impFileR = Path.GetFileNameWithoutExtension(inR) + "_imp" + ".pcm";
                    if (!_keepTempFiles)
                    {
                        _tempFiles.Add(impFileR);
                    }
                    writer = new WaveWriter(impFileR);
                    writer.Input = impulseR;
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Raw = true;
                    writer.Run();
                    writer.Close();

                    // Write the impulseFFT magnitude to disk
                    L = impulseRFFT.Length;
                    string impTempR = Path.GetFileNameWithoutExtension(inR) + "_imp" + ".dat";
                    _tempFiles.Add(impTempR);
                    writer = new WaveWriter(impTempR);
                    writer.Input = new CallbackSource(2, impulseR.SampleRate, delegate(long j)
                    {
                        if (j >= L / 2)
                        {
                            return null;
                        }
                        Complex si = impulseRFFT[j]; // +impulseRFFT[L - j - 1];
                        ISample s = new Sample2();
                        s[0] = si.Magnitude;
                        s[1] = si.Phase / Math.PI;
                        return s;
                    });
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Raw = false;
                    writer.Run();
                    writer.Close();
                    writer = null;

                    impulseRFFT = null;
                    GC.Collect();

                    if (_doDirectFilters)
                    {
                        // Sliding low-pass filter over the impulse
                        stderr.WriteLine("  Filtering...");
                        filteredImpulseR = SlidingLowPass(impulseR, _peakPosR);

                        // Write PCM for the filtered impulse
                        impDirectR = Path.GetFileNameWithoutExtension(inR) + "_impfilt" + ".pcm";
                        if (!_keepTempFiles)
                        {
                            _tempFiles.Add(impDirectR);
                        }
                        writer = new WaveWriter(impDirectR);
                        writer.Input = filteredImpulseR;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;
                        filteredImpulseR.Reset();
                    }

                    GC.Collect();

                    stderr.WriteLine("  Deconvolution: right impulse done.");
                    stderr.WriteLine();

                    // Join the left and right impulse files (truncated at 65536) into a WAV
                    // and normalize loudness for each channel
                    stderr.WriteLine("Splicing and normalizing (1)");
                    ChannelSplicer longstereoImpulse = new ChannelSplicer();

                    // (Don't normalize each channel's volume separately if _returnAll, it's just too expensive)
                    if (_returnAll)
                    {
                        buff = impulseL;
                    }
                    else
                    {
                        buff = new SoundBuffer(new SampleBuffer(impulseL).Subset(0, 131071));
                        g = Loudness.WeightedVolume(buff);
                        (buff as SoundBuffer).ApplyGain(1 / g);
                    }
                    longstereoImpulse.Add(buff);

                    if (_returnAll)
                    {
                        buff = impulseR;
                    }
                    else
                    {
                        buff = new SoundBuffer(new SampleBuffer(impulseR).Subset(0, 131071));
                        g = Loudness.WeightedVolume(buff);
                        (buff as SoundBuffer).ApplyGain(1 / g);
                    }
                    longstereoImpulse.Add(buff);

                    ISoundObj stereoImpulse = longstereoImpulse;

                    _impulseFiles.Add("Impulse_Response_Measured.wav: stereo impulse response from measurements");
                    writer = new WaveWriter("Impulse_Response_Measured.wav");
                    writer.Input = longstereoImpulse;
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Normalization = -1;
                    writer.Raw = false;
                    writer.Run();
                    writer.Close();
                    writer = null;

                    if (_env)
                    {
                        // Also save the Hilbert envelope
                        HilbertEnvelope env = new HilbertEnvelope(8191);
                        env.Input = longstereoImpulse;
                        _impulseFiles.Add("Impulse_Response_Envelope.wav: Hilbert envelope of the impulse response");
                        writer = new WaveWriter("Impulse_Response_Envelope.wav");
                        writer.Input = env;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Normalization = -1;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;
                    }

                    if (_dbl)
                    {
                        // Create DBL files for Acourate
                        _impulseFiles.Add("PulseL.dbl: impulse response, raw data (64-bit float), left channel ");
                        _impulseFiles.Add("PulseR.dbl: impulse response, raw data (64-bit float), right channel");
                        _impulseFiles.Add("  (use skew=" + (_peakPosL - _peakPosR) + " for time alignment)");
                        WriteImpulseDBL(stereoImpulse, "PulseL.dbl", "PulseR.dbl");
                    }

                    if (_pcm)
                    {
                        // Create PCM files for Octave (etc)
                        _impulseFiles.Add("LUncorrected.pcm: impulse response, raw data (32-bit float), left channel");
                        _impulseFiles.Add("RUncorrected.pcm: impulse response, raw data (32-bit float), right channel");
                        WriteImpulsePCM(stereoImpulse, "LUncorrected.pcm", "RUncorrected.pcm");
                    }

                    stereoImpulse = null;
                    longstereoImpulse = null;
                    buff = null;
                    GC.Collect();

                    if (_doDirectFilters)
                    {
                        // Same for the filtered impulse response
                        stderr.WriteLine("Splicing and normalizing (2)");
                        ChannelSplicer longstereoImpulseF = new ChannelSplicer();

                        buff = new SoundBuffer(new SampleBuffer(filteredImpulseL).Subset(0, 131071));
                        double gL = Loudness.WeightedVolume(buff);
                        (buff as SoundBuffer).ApplyGain(1 / gL);
                        longstereoImpulseF.Add(buff);
                        FilterProfile lfgDirectL = new FilterProfile(buff, 0.5);

                        buff = new SoundBuffer(new SampleBuffer(filteredImpulseR).Subset(0, 131071));
                        double gR = Loudness.WeightedVolume(buff);
                        (buff as SoundBuffer).ApplyGain(1 / gR);
                        longstereoImpulseF.Add(buff);
                        FilterProfile lfgDirectR = new FilterProfile(buff, 0.5);

                        _impulseFiles.Add("Impulse_Response_Filtered.wav: approximation to direct-sound impulse response");
                        writer = new WaveWriter("Impulse_Response_Filtered.wav");
                        writer.Input = longstereoImpulseF;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Normalization = -1;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        double gg = writer.Gain;
                        writer = null;
                        longstereoImpulseF = null;

                        ChannelSplicer longstereoImpulseD = new ChannelSplicer();

                        Mixer diffuse = new Mixer();
                        diffuse.Add(impulseL, 1.0);
                        diffuse.Add(filteredImpulseL, -1.0);
                        buff = new SoundBuffer(new SampleBuffer(diffuse).Subset(0, 131071));
                        (buff as SoundBuffer).ApplyGain(1 / gL);
                        longstereoImpulseD.Add(buff);
                        FilterProfile lfgDiffuseL = new FilterProfile(buff, 0.5);

                        diffuse = new Mixer();
                        diffuse.Add(impulseR, 1.0);
                        diffuse.Add(filteredImpulseR, -1.0);
                        buff = new SoundBuffer(new SampleBuffer(diffuse).Subset(0, 131071));
                        (buff as SoundBuffer).ApplyGain(1 / gR);
                        longstereoImpulseD.Add(buff);
                        FilterProfile lfgDiffuseR = new FilterProfile(buff, 0.5);

                        _impulseFiles.Add("Impulse_Response_Diffuse.wav: approximation to diffuse-field remnant");
                        writer = new WaveWriter("Impulse_Response_Diffuse.wav");
                        writer.Input = longstereoImpulseD;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Gain = gg;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;

                        // Filter the diffuse-field curve against double the diffuse-field curve
                        FilterImpulse fiDiffuse = new FilterImpulse(8192, HRTF.diffuseDiff0() * 2, FilterInterpolation.COSINE, sampleRate);
                        FastConvolver co = new FastConvolver(longstereoImpulseD, fiDiffuse);
                        SoundBuffer buffd = new SoundBuffer(co);
                        _impulseFiles.Add("Impulse_Response_Diffuse_Comp.wav: filtered diffuse-field remnant");
                        writer = new WaveWriter("Impulse_Response_Diffuse_Comp.wav");
                        writer.Input = buffd.Subset(4096);
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Gain = gg;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;

                        longstereoImpulseD = null;

                        bool any = false;
                        string jsonFile = "Diff.json";
                        FileStream fs = new FileStream(jsonFile, FileMode.Create);
                        StreamWriter sw = new StreamWriter(fs);
                        sw.WriteLine("{");
                        FilterProfile lfgDiffL = lfgDirectL - lfgDiffuseL;
                        if (lfgDiffL != null)
                        {
                            if (any) sw.WriteLine(",");
                            any = true;
                            sw.Write(lfgDiffL.ToJSONString("DiffL", "Diffuse field relative to direct, left channel"));
                        }
                        FilterProfile lfgDiffR = lfgDirectR - lfgDiffuseR;
                        if (lfgDiffR != null)
                        {
                            if (any) sw.WriteLine(",");
                            any = true;
                            sw.Write(lfgDiffR.ToJSONString("DiffR", "Diffuse field relative to direct, right channel"));
                        }
                        sw.WriteLine("}");
                        sw.Close();
                        fs.Close();
                    }
                    buff = null;
                    GC.Collect();

                    System.Console.Error.WriteLine();

                    if (!_noDRC)
                    {
                        // Analyze the freq response
                        // and create targets
                        // target_full.txt and target_half.txt
                        stderr.WriteLine("Analyzing response curves.");
                        Prep(impTempL, impTempR, "Impulse_Response_Measured.wav", "NoCorrection");

                        // Call DRC to create the filters
                        // then splice the DRC left & right output files together
                        stderr.WriteLine("Preparing for DRC.");
                        if (DoDRC(impFileL, impFileR, impDirectL, impDirectR, _peakPosL, _peakPosR, "Impulse_Response_Measured.wav", "Impulse_Response_Filtered.wav"))
                        {
                            stderr.WriteLine("Success!");
                        }
                    }

                    // Report names of the impulse files created
                    if (_impulseFiles.Count == 0)
                    {
                        System.Console.Error.WriteLine("No impulse response files were created.");
                    }
                    if (_impulseFiles.Count > 0)
                    {
                        System.Console.Error.WriteLine("Impulse response files were created:");
                        foreach (string f in _impulseFiles)
                        {
                            string s = "  " + f;
                            System.Console.Error.WriteLine(s);
                        }
                    }

                    // Report names of the filter files created
                    if (_filterFiles.Count == 0 && !_noDRC)
                    {
                        System.Console.Error.WriteLine("No correction filter files were created.");
                    }
                    if (_filterFiles.Count > 0)
                    {
                        System.Console.Error.WriteLine("Correction filter files were created:");
                        foreach (string f in _filterFiles)
                        {
                            string s = "  " + f;
                            if (_copy)
                            {
                                try
                                {
                                    File.Copy(f, Path.Combine(_impulsesFolder, f), true);
                                    s += " (copied)";
                                }
                                catch (Exception e)
                                {
                                    s += " (not copied: " + e.Message + ")";
                                }
                            }
                            System.Console.Error.WriteLine(s);
                        }
                    }
                    if (_peakPosL == _peakPosR)
                    {
                        System.Console.Error.WriteLine();
                        System.Console.Error.WriteLine("Zero time difference between channels.  Are you sure the recordings are correct?");
                    }
                }
                catch (Exception e)
                {
                    stderr.WriteLine();
                    stderr.WriteLine(e.Message);
                    stderr.WriteLine(e.StackTrace);
                }
                finally
                {
                    foreach (string tempFile in _tempFiles)
                    {
                        try
                        {
                            File.Delete(tempFile);
                        }
                        catch (Exception) { /* ignore */ }
                    }
                }
            }
            stderr.Flush();
        }
Example #10
0
        static SoundObj Deconvolve(string infile, out Complex[] impulseFFT, out int peakpos)
        {
            WaveReader reader = new WaveReader(infile);
            ushort nChannels = reader.NumChannels;
            uint sampleRate = reader.SampleRate;

            CallbackSource cs;
            SingleChannel[] channels = new SingleChannel[2];
            Complex[][][] data = new Complex[2][][];
            double[] stdDev = new double[2];
            double[] maxLogs = new double[2];
            double[] maxs = new double[2];
            double[] avgLog = new double[2];

            Complex[] swp_data = null;
            Complex[] mea_data = null;

            if (_fmax == int.MaxValue)
            {
                _fmax = (int)sampleRate / 2;
            }
            bool isEstimatedSweepRange = false;

            if (nChannels != 2)
            {
                throw new Exception("Input must have two channels.");
            }

            peakpos = 0;
            int cSwp = 0;
            int cMea = 1;
            int L = 0;
            int Nh = 0;
            double mx;

            double max = 0;
            double maxLog = 0;
            double stdev = 0;
            double avg = 0;

            for (int iteration = 1; iteration <= 2; iteration++)
            {
                // Read and FFT all the data
                // one channel at a time
                for (ushort c = 0; c < 2; c++)
                {
                    SingleChannel channel = reader.Channel(c);
                    Complex[] cdata;

                    SoundBuffer buff = new SoundBuffer(channel);
                    buff.ReadAll();

                    if (iteration==2 && _split)
                    {
                        // Split up the input file
                        string infile2 = Path.ChangeExtension(Path.GetFileName(infile), ".PCM");
                        if (c == cSwp)
                        {
                            WaveWriter wri = new WaveWriter("refchannel_" + infile2, 1, channel.SampleRate, 32, DitherType.NONE, WaveFormat.IEEE_FLOAT);
                            wri.Input = buff;
                            wri.Run(); wri.Close();
                        }
                        if (c == cMea)
                        {
                            WaveWriter wri = new WaveWriter("sweep_" + infile2, 1, channel.SampleRate, 32, DitherType.NONE, WaveFormat.IEEE_FLOAT);
                            wri.Input = buff;
                            wri.Run(); wri.Close();
                        }
                    }

                    // 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
                    data[c] = buff.ToComplexArray();

                    // Then we're done with the buffer for this channel
                    buff = null;
                    GC.Collect();

                    cdata = data[c][0];

                    if (iteration==2 && c==cSwp && _power > 0)
                    {
                        // Deconvolve against a power of the sweep,
                        // for distortion measurement of harmonic _power
                        Complex p = new Complex((double)_power,0);
                        for (int j = 0; j < cdata.Length; j++)
                        {
                            cdata[j].Pow(p);
                        }
                    }

                    // FFT in place
                    Fourier.FFT(cdata.Length, cdata);

                    if (false && iteration==1)
                    {
                        // write the fft magnitudes to disk
                        cs = new CallbackSource(1, sampleRate, delegate(long j)
                        {
                            if (j >= cdata.Length)
                            {
                                return null;
                            }
                            Complex si = cdata[j];
                            Sample s = new Sample(1);
                            double f = (double)j * sampleRate / cdata.Length;
                            s[0] = mag(sampleRate, f, si.Magnitude);
                            return s;
                        });
                        // cs.SampleRate = sampleRate;
                        // cs.NumChannels = 1;
                        WaveWriter writer = new WaveWriter("fft_" + c + "_" + infile);
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Input = cs;
                        writer.Normalization = -3;
                        writer.Run();
                        writer.Close();
                    }

                    // Take a slice of the FFT, std dev of log(|fft|),
                    // the lower value should be the sweep
                    int n3 = cdata.Length / 4;
                    int n1 = n3 / 2;
                    int n2 = n1 + n3;
                    get_stddev(sampleRate, n1, n2, cdata, out max, out maxLog, out stdev, out avg);

                    maxs[c] = max;
                    maxLogs[c] = maxLog;
                    stdDev[c] = stdev;
                    avgLog[c] = avg;

                    // Trace.WriteLine("Channel {0} standard deviation {1}", c, stdDev[c]);
                }
                GC.Collect();

                if (iteration == 1)
                {
                    if (stdDev[1] < stdDev[0])
                    {
                        if (_refchannel == -1)
                        {
                            cSwp = 1;
                            cMea = 0;
                            stderr.WriteLine("  Right channel seems to be the sweep");
                        }
                        else
                        {
                            stderr.WriteLine("  Right channel seems to be the sweep");
                            stderr.WriteLine("  But you said use refchannel {0}, so using that.", _refchannel);
                            cSwp = _refchannel;
                            cMea = (_nInFiles - 1) - _refchannel;
                        }
                    }
                    else
                    {
                        if (_refchannel == -1)
                        {
                            stderr.WriteLine("  Left channel seems to be the sweep");
                        }
                        else
                        {
                            stderr.WriteLine("  Left channel seems to be the sweep");
                            stderr.WriteLine("  But you said use refchannel {0}, so using that.", _refchannel);
                            cSwp = _refchannel;
                            cMea = (_nInFiles - 1) - _refchannel;
                        }
                    }
                }

                swp_data = data[cSwp][0];
                mea_data = data[cMea][0];

                L = swp_data.Length;
                Nh = L / 2;

                // stderr.WriteLine("avgLog=" + avgLog[cSwp] + " maxLog=" + maxLogs[cSwp]);

                double hz1 = L / sampleRate;
                if (false && _fmin == 0)
                {
                    isEstimatedSweepRange = true;
                    // Working back from 100Hz,
                    // Look for the first range where average of a 1Hz window
                    // is less than 0.7* average for the sweep itself
                    int kmin = (int)(hz1 * 100);
                    _fmin = 100;
                    while (kmin > 0)
                    {
                        get_stddev(sampleRate, kmin, (int)(kmin + hz1), swp_data, out max, out maxLog, out stdev, out avg);
                        if (avg < avgLog[cSwp] * 0.5)
                        {
                            break;
                        }
                        kmin -= (int)hz1;
                        _fmin--;
                    }
                    // stderr.WriteLine("avg/2: kmin=" + kmin + ", _fmin=" + _fmin);
                }

                if (false && _fmax == sampleRate / 2)
                {
                    isEstimatedSweepRange = true;
                    // Working forward from (say) 15kHz,
                    // Look for the first range where average of a 100Hz window
                    // is less than 0.7* average for the sweep itself
                    int kmax = (int)(hz1 * 10000);
                    _fmax = 10000;
                    get_stddev(sampleRate, kmax, (int)(kmax + 100 * hz1), swp_data, out max, out maxLog, out stdev, out avg);
                    double stdTest = stdev;
                    while (kmax < L / 2)
                    {
                        get_stddev(sampleRate, kmax, (int)(kmax + 100 * hz1), swp_data, out max, out maxLog, out stdev, out avg);
                        if (avg < avgLog[cSwp] * 0.5)
                        {
                            break;
                        }
                        kmax += (int)(100 * hz1);
                        _fmax += 100;
                    }
                    // stderr.WriteLine("StdDev Peak: kmax=" + kmax + ", _fmax=" + _fmax + ", sdev=" + stdev + " ref " + stdTest + " (1Hz=" + hz1 + "), avgLog=" + avg);
                }

                if (!_noSubsonicFilter)
                {
                    // Filter LF from the measurement data
                    // to avoid spurious stuff below 15Hz
                    int s1 = (int)(7 * hz1);
                    int s2 = (int)(15 * hz1);
                    for (int j = 0; j < s1; j++)
                    {
                        mea_data[j].Set(0, 0);
                        mea_data[swp_data.Length - j - 1].Set(0, 0);
                    }
                    for (int j = s1; j < s2; j++)
                    {
                        double n = (double)(j - s1) / (s2 - s1);
                        double m = 0.5 * (1 + Math.Cos(Math.PI * (1 + n)));
                        mea_data[j].mul(m);
                        mea_data[swp_data.Length - j - 1].mul(m);
                    }
                }

                // Divide in complex domain
                for (int j = 0; j < swp_data.Length; j++)
                {
                    swp_data[j].idiv(mea_data[j]);
                    // Make a copy in mea_data, we'll need it later
                    mea_data[j] = swp_data[j];
                }

                // IFFT to get the impulse response
                Fourier.IFFT(swp_data.Length, swp_data);

                // Scan the imp to find maximum
                mx = 0;
                int mp = 0;
                for (int j = 0; j < sampleRate; j++)
                {
                    Complex si = swp_data[j];
                    double mg = Math.Abs(si.Magnitude);
                    if (mg > mx) { mx = mg; mp = j; }
                }
                // Look one sample backwards from max position
                // to find the likely "pulse peak" if within 30% of max
                peakpos = mp;
                if (mp>0 && swp_data[mp - 1].Magnitude / mx > 0.7)
                {
                    peakpos = mp - 1;
                }
            }

            // stderr.WriteLine("  {0} range {1}Hz to {2}Hz", isEstimatedSweepRange ? "Estimated sweep" : "Sweep", _fmin, _fmax);
            if (_fmaxSpecified && _fminSpecified)
            {
                HackSweep(swp_data, mea_data, peakpos, L, sampleRate);
            }
            else
            {
                Fourier.FFT(swp_data.Length, swp_data);
            }

            // Window the extremities of the whole response, finally?

            if (true)
            {
                // Make a copy in mea_data, we'll need it later
                for (int j = 0; j < swp_data.Length; j++)
                {
                    mea_data[j] = swp_data[j];
                }

                // Return FFT of impulse
                impulseFFT = mea_data;
            }

            // IFFT to get the impulse response
            Fourier.IFFT(swp_data.Length, swp_data);

            // Scan the imp to find maximum
            mx = 0;
            for (int j = 0; j < sampleRate; j++)
            {
                Complex si = swp_data[j];
                double mg = Math.Abs(si.Magnitude);
                if (mg > mx) mx = mg;
            }

            if (_noNorm)
            {
                mx = 1.0;
            }

            // Yield the first half (normalized) as result
            cs = new CallbackSource(1, sampleRate, delegate(long j)
            {
                if (j > (_returnAll ? L-1 : L / 2))
                {
                    return null;
                }
                Complex si = swp_data[j];
                Sample s = new Sample(si.Re / mx);
                return s;
            });
            cs.SampleRate = sampleRate;
            cs.NumChannels = 1;

            return cs;
        }
Example #11
0
        static ISoundObj Splice(string infileL, int peakPosL, string infileR, int peakPosR, string outfile)
        {
            //int tmp1;
            //bool tmp2;
            WaveReader reader1 = new WaveReader(infileL, WaveFormat.IEEE_FLOAT, 32, 1);
            //            reader1.Skip(peakPosL, out tmp1, out tmp2);
            SoundBuffer buff1 = new SoundBuffer(reader1);
            buff1.ReadAll();

            // Normalize loudness for each channel
            double g = Loudness.WeightedVolume(buff1);
            buff1.ApplyGain(1/g);

            WaveReader reader2 = new WaveReader(infileR, WaveFormat.IEEE_FLOAT, 32, 1);
            //            reader2.Skip(peakPosR, out tmp1, out tmp2);
            SoundBuffer buff2 = new SoundBuffer(reader2);
            buff2.ReadAll();

            g = Loudness.WeightedVolume(buff2);
            buff2.ApplyGain(1/g);

            ChannelSplicer splicer = new ChannelSplicer();
            splicer.Add(buff1);
            splicer.Add(buff2);

            // General-purpose:
            // Find the extremities of the DRC impulse,
            // window asymmetrically.
            //
            // But, since we specifically used linear-phase filters on target and mic,
            // we know that the impulse is centered.
            // We want an impulse length (_filterLen)
            // so window the 2048 samples at each end (which are pretty low to begin with - less than -100dB)

            ISoundObj output;
            int nCount = (int)(buff1.Count / 2);
            if (nCount > _filterLen/2)
            {
                BlackmanHarris bhw = new BlackmanHarris(nCount, 2048, (nCount / 2) - 2048);
                bhw.Input = splicer;
                SampleBuffer sb = new SampleBuffer(bhw);
                output = sb.Subset(_filterLen/2, _filterLen);
            }
            else
            {
                output = splicer;
            }

            ISoundObj result = output;
            if (!_noSkew)
            {
                // Apply skew to compensate for time alignment in the impulse responses
                Skewer skewer = new Skewer(true);
                skewer.Input = output;
                skewer.Skew = peakPosL - peakPosR;
                result = skewer;
            }

            WaveWriter writer = new WaveWriter(outfile);
            writer.Input = result;
            writer.Dither = DitherType.NONE;
            writer.Format = WaveFormat.IEEE_FLOAT;
            writer.BitsPerSample = 32;
            writer.SampleRate = _sampleRate;
            writer.NumChannels = splicer.NumChannels;
            if (Double.IsNaN(_gain))
            {
                writer.Normalization = 0;
            }
            else
            {
                writer.Gain = MathUtil.gain(_gain);
            }
            writer.Run();
            writer.Close();
            reader1.Close();
            reader2.Close();

            return result;
        }
Example #12
0
        static void Prep(string infileL, string infileR, string stereoImpulseFile, string outFile)
        {
            // Input files are complex
            // 0=mag, 1=phase/pi (so it looks OK in a wave editor!)
            // FFTs of the room impulse response

            // Take two half-FFT-of-impulse WAV files
            // Average them, into an array

            int n;
            SoundBuffer buff;
            WaveWriter wri;
            //          NoiseGenerator noise;
            FastConvolver conv;

            /*
            // Convolve noise with the in-room impulse
            noise = new NoiseGenerator(NoiseType.WHITE_FLAT, 2, (int)131072, stereoImpulse.SampleRate, 1.0);
            conv = new FastConvolver();
            conv.impulse = stereoImpulse;
            conv.Input = noise;

            wri = new WaveWriter("ImpulseResponse_InRoom.wav");
            wri.Input = conv;
            wri.Format = WaveFormat.IEEE_FLOAT;
            wri.BitsPerSample = 32;
            wri.Normalization = 0;
            wri.Run();
            wri.Close();
            wri = null;
            conv = null;
            */

            WaveReader rdrL = new WaveReader(infileL);
            buff = new SoundBuffer(rdrL);
            n = (int)buff.ReadAll();
            uint sampleRate = buff.SampleRate;
            uint nyquist = sampleRate / 2;

            double binw = (nyquist / (double)n);

            WaveReader rdrR = new WaveReader(infileR);

            IEnumerator<ISample> enumL = buff.Samples;
            IEnumerator<ISample> enumR = rdrR.Samples;

            // For easier processing and visualisation
            // read this in to an ERB-scale (not quite log-scale) array
            // then we can smooth by convolving with a single half-cosine.
            //

            int nn = (int)ERB.f2bin(nyquist, sampleRate) + 1;

            double[] muff = new double[nn];

            int prevbin = 0;
            int nbin = 0;
            double v = 0;
            int j = 0;
            while (true)
            {
                double f = (double)j * binw;    // equiv freq, Hz

                int bin = (int)ERB.f2bin(f, sampleRate); // the bin we drop this sample in
                if (bin > nn)
                {
                    // One of the channels has more, but we're overrun so stop now
                    break;
                }

                j++;
                bool more = false;
                more |= enumL.MoveNext();
                more |= enumR.MoveNext();
                if (!more)
                {
                    muff[prevbin] = v / nbin;
                    break;
                }

                v += enumL.Current[0];  // magnitude
                v += enumR.Current[0];  // magnitude
                nbin++;

                if (bin > prevbin)
                {
                    muff[prevbin] = v / nbin;
                    v = 0;
                    nbin = 0;
                    prevbin = bin;
                }
            }

            double[] smoo = ERB.smooth(muff, 38);

            // Pull out the freq response at ERB centers
            FilterProfile lfg = ERB.profile(smoo, sampleRate);

            // Write this response as a 'target' file
            /*
            FileStream fs = new FileStream("target_full.txt", FileMode.Create);
            StreamWriter sw = new StreamWriter(fs, Encoding.ASCII);
            foreach (FreqGain fg in lfg)
            {
                sw.WriteLine("{0} {1:f4}", Math.Round(fg.Freq), fg.Gain);
            }
            sw.Close();
            */
            /*
            fs = new FileStream("target_half.txt", FileMode.Create);
            sw = new StreamWriter(fs, Encoding.ASCII);
            foreach (FreqGain fg in lfg)
            {
                sw.WriteLine("{0} {1:f4}", Math.Round(fg.Freq), fg.Gain/2);
            }
            sw.Close();
            */

            // Create a filter to invert this response
            FilterProfile ifg = new FilterProfile();
            foreach (FreqGain fg in lfg)
            {
                ifg.Add(new FreqGain(fg.Freq, -fg.Gain));
            }

            ISoundObj filterImpulse = new FilterImpulse(0, ifg, FilterInterpolation.COSINE, sampleRate);
            filterImpulse.SampleRate = sampleRate;

            // Write the filter impulse to disk
            string sNoCorr = outFile + ".wav";
            wri = new WaveWriter(sNoCorr);
            wri.Input = filterImpulse; // invertor;
            wri.Format = WaveFormat.IEEE_FLOAT;
            wri.BitsPerSample = 32;
            wri.SampleRate = _sampleRate;
            wri.Normalization = -1;
            wri.Run();
            wri.Close();
            _filterFiles.Add(sNoCorr);

            /*
            // Convolve noise with the NoCorrection filter
            noise = new NoiseGenerator(NoiseType.WHITE_FLAT, 2, (int)131072, stereoImpulse.SampleRate, 1.0);
            conv = new FastConvolver();
            conv.impulse = invertor;
            conv.Input = noise;

            wri = new WaveWriter("NoCorrection_Test.wav");
            wri.Input = conv;
            wri.Format = WaveFormat.IEEE_FLOAT;
            wri.BitsPerSample = 32;
            wri.SampleRate = _sampleRate;
            wri.Normalization = 0;
            wri.Run();
            wri.Close();
            wri = null;
            conv = null;
            */

            // Convolve this with the in-room impulse response

            WaveReader rea = new WaveReader(outFile + ".wav");
            conv = new FastConvolver();
            conv.impulse = rea;
            conv.Input = new WaveReader(stereoImpulseFile);
            wri = new WaveWriter(outFile + "_TestConvolution.wav");
            wri.Input = conv;
            wri.Format = WaveFormat.PCM;
            wri.Dither = DitherType.TRIANGULAR;
            wri.BitsPerSample = 16;
            wri.SampleRate = _sampleRate;
            wri.Normalization = -1;
            wri.Run();
            wri.Close();
            rea.Close();
            wri = null;
            conv = null;
        }
Example #13
0
 public WindowedBuffer(ISoundObj input, CosWindow window, int start, int count)
 {
     _buff = new SoundBuffer(new SampleBuffer(input).Subset(start, count));
     _buff.ApplyWindow(window);
 }
Example #14
0
        static SoundObj GetMainImpulse(out string actualPath)
        {
            DateTime dtStart = DateTime.Now;
            if (_impulsePath == "") _impulsePath = null;
            if (_impulsePath == "-") _impulsePath = null;
            if (_matrixFilter == "") _matrixFilter = null;
            if (_matrixFilter == "-") _matrixFilter = null;
            if (_bformatFilter == "") _bformatFilter = null;
            if (_bformatFilter == "-") _bformatFilter = null;
            Trace.WriteLine("Impulse {0}, matrix {1}", CleanPath(_dataFolder, _impulsePath), CleanPath(_dataFolder, _matrixFilter));

            // note: we window the room correction impulse if it's too long
            WaveReader impulseReader = null;
            SoundObj impulseObj = null;
            actualPath = null;

            if (!String.IsNullOrEmpty(_impulsePath))
            {
                impulseReader = GetAppropriateImpulseReader(_impulsePath, out actualPath);
            }
            if (impulseReader != null)
            {
                if (impulseReader.Iterations > _maxImpulseLength)
                {
                    // This impulse is too long.
                    // Trim it to length.
                    int hwid = _maxImpulseLength / 2;
                    int qwid = _maxImpulseLength / 4;
                    SoundBuffer buff = new SoundBuffer(impulseReader);
                    buff.ReadAll();
                    int center = buff.MaxPos();
                    BlackmanHarris wind;
                    int startpos;
                    if (center < hwid)
                    {
                        wind = new BlackmanHarris(center, qwid, qwid);
                        startpos = 0;
                    }
                    else
                    {
                        wind = new BlackmanHarris(hwid, qwid, qwid);
                        startpos = center - hwid;
                    }
                    //                        int startpos = center < hwid ? 0 : (center - hwid);
                    wind.Input = buff.Subset(startpos, _maxImpulseLength);
                    impulseObj = wind;
                }
                else
                {
                    impulseObj = impulseReader;
                }
            }

            if (_debug)
            {
                TimeSpan ts = DateTime.Now.Subtract(dtStart);
                Trace.WriteLine("GetMainImpulse " + ts.TotalMilliseconds);
            }
            return impulseObj;
        }
Example #15
0
        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);
        }
Example #16
0
 private ISoundObj _buff()
 {
     if (double.IsNaN(_normalization))
     {
         return _input;
     }
     // We've been asked to normalize
     SoundBuffer b = new SoundBuffer(_input);
     b.ReadAll();
     _gain = b.Normalize(_normalization, false);
     return b;
 }