Inheritance: SoundObj
Beispiel #1
0
        static bool ExecDRC(string drcfile, string target, string outfile, string infileL, string infileR, int peakPosL, int peakPosR, string stereoImpulseFile)
        {
            GC.Collect();
            bool ok = false;
            string args;
            FastConvolver conv;
            WaveWriter wri;
            if (!File.Exists(drcfile))
            {
                stderr.WriteLine();
                stderr.WriteLine("{0} not found.", drcfile);
                return ok;
            }

            string tmpL;
            string tmpR;
            tmpL = Path.GetFileNameWithoutExtension(infileL) + ".tmp";
            tmpR = Path.GetFileNameWithoutExtension(infileR) + ".tmp";
            _tempFiles.Add(tmpL);
            _tempFiles.Add(tmpR);

            stderr.WriteLine("Exec DRC for {0}, left channel", drcfile);
            stderr.WriteLine();
            ok = RunDRC(drcfile, infileL, target, tmpL, peakPosL, out args);

            if (ok)
            {
                stderr.WriteLine();
                stderr.WriteLine("Exec DRC for {0}, right channel", drcfile);
                stderr.WriteLine();
                ok = RunDRC(drcfile, infileR, target, tmpR, peakPosR, out args);
            }

            if (ok)
            {
                stderr.WriteLine();
                if (_noSkew)
                {
                    stderr.WriteLine("Creating stereo filter {0}", outfile + ".wav" );
                }
                else
                {
                    stderr.WriteLine("Creating stereo filter {0} (skew {1} samples)", outfile + ".wav", peakPosR - peakPosL);
                }
                ISoundObj stereoFilter = Splice(tmpL, peakPosL, tmpR, peakPosR, outfile + ".wav");
                stderr.WriteLine();

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

                wri = new WaveWriter(drcfile + "_Test.wav");
                wri.Input = conv;
                wri.Format = WaveFormat.IEEE_FLOAT;
                wri.BitsPerSample = 32;
                wri.SampleRate = _sampleRate;
                wri.Normalization = -1;
                wri.Run();
                wri.Close();
                wri = null;
                conv = null;
                noise = null;
                 * */

                // Convolve filter with the in-room impulse response
                WaveReader rea = new WaveReader(stereoImpulseFile);
                conv = new FastConvolver();
                conv.impulse = rea;
                conv.Input = stereoFilter;

                if (_pcm)
                {
                    _impulseFiles.Add("LCorrected_" + outfile + ".pcm: corrected test convolution, raw data (32-bit float), left channel");
                    _impulseFiles.Add("RCorrected_" + outfile + ".pcm: corrected test convolution, raw data (32-bit float), right channel");
                    WriteImpulsePCM(conv, "LCorrected_" + outfile + ".pcm", "RCorrected_" + outfile + ".pcm");
                }

                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;
                rea = null;
                conv = null;

                GC.Collect();
            }
            return ok;
        }
Beispiel #2
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;
        }
Beispiel #3
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;
        }
Beispiel #4
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;
        }
Beispiel #5
0
        static ISoundObj GetSignalGenerator(double dBfs, out string desc)
        {
            double gain = MathUtil.gain(dBfs);
            ISoundObj signalGenerator = null;
            Sequencer seq;
            string description = "Unknown";
            switch (_siggen)
            {
                case "IDENT":
                    // Left-right identification: embedded resource
                    Assembly ass = Assembly.GetExecutingAssembly();
                    foreach (string s in ass.GetManifestResourceNames())
                    {
                        if (s.Contains("LeftRight"))
                        {
                            Stream res = ass.GetManifestResourceStream(s);
                            WaveReader rdr = new WaveReader(res);
                            // The stream is stereo, but we want to alternate
                            seq = new Sequencer();

                            for (int j = 0; j < 10; j++)
                            {
                                seq.Add(rdr, new List<double>(new double[] { gain, 0 }));
                                seq.Add(new NoiseGenerator(NoiseType.SILENCE, 2, 1.0, _inputSampleRate, 0.0, false));
                                seq.Add(rdr, new List<double>(new double[] { 0, gain }));
                                seq.Add(new NoiseGenerator(NoiseType.SILENCE, 2, 1.0, _inputSampleRate, 0.0, false));
                            }

                            signalGenerator = seq;
                            break;
                        }
                    }
                    /*
                    // Left-right identification signal: morse code
                    MorseCode envL = new MorseCode(" " + _sigparamA, 10, true);
                    ISoundObj sigL = new SweepGenerator(1, envL.LengthSeconds * 5, 220, 7040, _inputSampleRate, 0, false, gain, true);
                    envL.Input = sigL;

                    MorseCode envR = new MorseCode(" " + _sigparamB, 10, true);
                    ISoundObj sigR = new SweepGenerator(1, envR.LengthSeconds * 5, 7040, 220, _inputSampleRate, 0, false, gain, true);
                    envR.Input = sigR;

                    signalGenerator = new ChannelSplicer();
                    (signalGenerator as ChannelSplicer).Add(envL);
                    (signalGenerator as ChannelSplicer).Add(envR);
                    */
                    description = String.Format("Left/Right channel identification");
                    break;

                case "SWEEP":
                    seq = new Sequencer();
                    if (_sigparam1 == 0)
                    {
                        _sigparam1 = 45;
                    }
                    int lengthSamples = (int)(_sigparam1 * _inputSampleRate);
                    if (lengthSamples < 8388608)
                    {
                        // High-accuracy logarithmic sweep starting at 10Hz
                        int fade = (int)(_inputSampleRate / 20);
                        FFTSweepGenerator sg = new FFTSweepGenerator(2, lengthSamples, 10, _inputSampleRate / 2, _inputSampleRate, gain, false);
                        seq.Add(sg);
                        description = String.Format("Logarithmic sine sweep 10Hz to {0}Hz in {1} seconds", _inputSampleRate / 2, _sigparam1);
                    }
                    else
                    {
                        // Simple logarithmic sweep starting at 10Hz, windowed (uses much less memory!)
                        int fade = (int)(_inputSampleRate / 20);
                        BlackmanHarris bhwf = new BlackmanHarris(lengthSamples / 2, fade, (int)((lengthSamples / 2) - fade));
                        SweepGenerator sg = new SweepGenerator(2, lengthSamples, 10, _inputSampleRate / 2, _inputSampleRate, gain, false);
                        bhwf.Input = sg;
                        seq.Add(bhwf);
                        description = String.Format("Log sine sweep 10Hz to {0}Hz in {1} seconds", _inputSampleRate / 2, _sigparam1);
                    }
                    // Follow by 3 seconds of silence
                    seq.Add(new NoiseGenerator(NoiseType.SILENCE, 2, 3.0, _inputSampleRate, 0.0, false));
                    signalGenerator = seq;
                    break;

                case "SINE":
                    signalGenerator = new SineGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz sine", _sigparam1);
                    break;

                case "QUAD":
                    signalGenerator = new SineQuadGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz quadrature", _sigparam1);
                    break;

                case "SQUARE":
                    signalGenerator = new SquareGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz non-bandlimited square", _sigparam1);
                    break;

                case "BLSQUARE":
                    signalGenerator = new BandLimitedSquareGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz bandlimited square", _sigparam1);
                    break;

                case "TRIANGLE":
                    signalGenerator = new TriangleGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz non-bandlimited triangle", _sigparam1);
                    break;

                case "BLTRIANGLE":
                    signalGenerator = new BandLimitedTriangleGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz bandlimited triangle", _sigparam1);
                    break;

                case "SAWTOOTH":
                    signalGenerator = new SawtoothGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz non-bandlimited sawtooth", _sigparam1);
                    break;

                case "BLSAWTOOTH":
                    signalGenerator = new BandLimitedSawtoothGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz bandlimited sawtooth", _sigparam1);
                    break;

                case "WHITE":
                    signalGenerator = new NoiseGenerator(NoiseType.WHITE, 2, int.MaxValue, _inputSampleRate, gain, true);
                    description = String.Format("White noise");
                    break;

                case "PINK":
                    bool mono = (_sigparam1 != 0 ? true : false);
                    signalGenerator = new NoiseGenerator(NoiseType.PINK, 2, int.MaxValue, _inputSampleRate, gain, mono);
                    description = String.Format("Pink noise {0}", mono ? "(mono)" : "(stereo)" );
                    break;

                case "INTERMODULATION":
                    double n = 1;
                    description = String.Format("Intermodulation test {0}Hz", _sigparam1);
                    if (_sigparam2 != 0) { n++; description = description + " + " + _sigparam2 + "Hz"; }
                    if (_sigparam3 != 0) { n++; description = description + " + " + _sigparam3 + "Hz"; }
                    signalGenerator = new Mixer();
                    (signalGenerator as Mixer).Add(new SineGenerator(2, _inputSampleRate, _sigparam1, gain), 1/n);
                    if (_sigparam2 != 0) (signalGenerator as Mixer).Add(new SineGenerator(2, _inputSampleRate, _sigparam2, gain), 1 / n);
                    if (_sigparam3 != 0) (signalGenerator as Mixer).Add(new SineGenerator(2, _inputSampleRate, _sigparam3, gain), 1 / n);
                    break;

                case "SHAPEDBURST":
                    description = String.Format("{0}Hz windowed (Blackman) over {1} cycles", _sigparam1, _sigparam2);
                    throw new NotImplementedException();
                    //break;

                default:
                    _siggen = null;
                    break;
            }
            if (IsSigGenEQ())
            {
                if (IsSigGenEQBoth())
                {
                    description = description + ", with EQ processing";
                }
                else if (IsSigGenEQL())
                {
                    description = description + ", with EQ processing in left channel";
                }
                else if (IsSigGenEQR())
                {
                    description = description + ", with EQ processing in right channel";
                }
                else
                {
                    description = description + ", with EQ processing";
                }
            }
            desc = description;
            return signalGenerator;
        }
Beispiel #6
0
        private void init()
        {
            _running = true;

            ComputeImpulseFFT();

            nChannels = NumChannels;
            // Size for work area
            N = _impulseLength;
            nImpulseChannels = _impulse.NumChannels;
            Nh = N << 1;

            K = (_partitions <= 0) ? N : (N / _partitions);
            L = MathUtil.NextPowerOfTwo(K << 1);
            P = (int)Math.Ceiling((double)N / (double)K);

            //              Trace.WriteLine("{0} partitions of size {1}, chunk size {2}", P, L, K);

            // Allocate buffers for the source and output
            src    = new Complex[nChannels][];
            output = new Complex[nChannels][];
            for (ushort c = 0; c < nChannels; c++)
            {
                src[c]    = new Complex[Nh];
                output[c] = new Complex[Nh];
            }

            // For partitioned convolution, allocate arrays for the fft accumulators
            if (_partitions > 0)
            {
                accum = new Complex[nChannels][][];
                for (ushort c = 0; c < nChannels; c++)
                {
                    accum[c] = new Complex[P][];
                    for (p = 0; p < P; p++)
                    {
                        accum[c][p] = new Complex[L];
                    }
                }
            }

            if (IsPersistTail)
            {
                // If there's any *unprocessed* leftover from a previous convolution,
                // load it into prevTailSamples before we begin
                if (System.IO.File.Exists(_persistFile))
                {
                    WaveReader tailReader = null;
                    try
                    {
                        tailReader      = new WaveReader(_persistFile);
                        prevTailSamples = new List <ISample>(tailReader.Iterations);
                        //                            Trace.WriteLine("Read {0} tail samples", tailReader.Iterations);
                        if (tailReader.NumChannels == NumChannels)
                        {
                            foreach (ISample s in tailReader)
                            {
                                prevTailSamples.Add(s);
                            }
                        }
                        prevTailEnum = prevTailSamples.GetEnumerator();
                    }
                    catch (Exception e)
                    {
                        Trace.WriteLine("Could not read tail {0}: {1}", _persistFile.Replace(_persistPath, ""), e.Message);
                    }
                    // finally...
//                    ThreadPool.QueueUserWorkItem(delegate(object o)
//                    {
                    try
                    {
                        if (tailReader != null)
                        {
                            tailReader.Close();
                            tailReader = null;
                        }
                        System.IO.File.Delete(_persistFile);
                    }
                    catch (Exception)
                    {
                        // ignore, we're done
                    }
//                    });
                }
            }
//              Trace.WriteLine("{0} partitions of size {1}, chunk size {2}", P, L, K);
        }
Beispiel #7
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 #8
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 #9
0
        static WaveReader TryGetAppropriateImpulseReader(string filePath, bool allowResample, out string actualPath)
        {
            WaveReader rdr = null;
            bool isExtensibleFormat = false;
            WaveFormat format = WaveFormat.ANY;
            bool needResample = false;
            string resamplePath = GetResampledImpulsePath(filePath);
            actualPath = null;

            // Check the impulse file.
            if (File.Exists(filePath))
            {
                try
                {
                    rdr = new WaveReader(filePath);
                    actualPath = filePath;
                    if (rdr.SampleRate != 0 && rdr.SampleRate != _inputSampleRate)
                    {
                        Trace.WriteLine("Can't use {0}: its sample rate is {1} not {2}", CleanPath(_dataFolder, filePath), rdr.SampleRate, _inputSampleRate);
                        isExtensibleFormat = (rdr.FormatEx != null);
                        format = rdr.Format;
                        rdr.Close();
                        rdr = null;
                        actualPath = null;
                        needResample = true;
                    }
                }
                catch (Exception e)
                {
                    Trace.WriteLine("Can't use {0}: {1}", CleanPath(_dataFolder, filePath), e.Message);
                }
            }
            else
            {
                Trace.WriteLine("Can't use {0}: not found", CleanPath(_dataFolder, filePath));
            }

            if (rdr == null)
            {
                // No luck there.  Check for an already-resampled version.
                if (File.Exists(resamplePath))
                {
                    try
                    {
                        rdr = new WaveReader(resamplePath);
                        if (rdr.SampleRate != 0 && rdr.SampleRate != _inputSampleRate)
                        {
                            // Oh dear! This shouldn't happen; after all, the resampled file's name is designed to match sample rates
                            Trace.WriteLine("Can't use {0}: its sample rate is {1} not {2}", CleanPath(_dataFolder, resamplePath), rdr.SampleRate, _inputSampleRate);
                            rdr.Close();
                            rdr = null;
                        }
                        else
                        {
                            actualPath = resamplePath;
                            Trace.WriteLine("Using resampled version {0} instead", CleanPath(_dataFolder, resamplePath));
                            needResample = false;
                        }
                    }
                    catch (Exception e)
                    {
                        Trace.WriteLine("Can't use {0}: {1}", CleanPath(_dataFolder, resamplePath), e.Message);
                    }
                }
                else if(allowResample)
                {
                    Trace.WriteLine("Can't use {0}: not found", CleanPath(_dataFolder, resamplePath));
                }
            }

            if (needResample && allowResample)
            {
                // If 'sox' is available,
                // use it to resample the filter we're trying to use
                //  sox <input.wav> -r <new_sample_rate> <output.wav> polyphase
                //
                // The sox application might be either
                // - in the same folder as this application (quite likely...), or
                // - somewhere else on the path
                // and it might be called "sox" or "sox.exe", depending on the operating system (of course).
                //

                if (true || isExtensibleFormat)
                {
                    // sox can't handle the WaveFormatExtensible file type, so we need to save a clean copy of the file first
                    // ALSO: sox goes very wrong if the original is near 0dBfs
                    // so we normalize ALWAYS before resample.
                    rdr = new WaveReader(filePath);
                    string newFile = "RE" + filePath.GetHashCode().ToString("x10").ToUpperInvariant() + ".wav";
                    string newPath = Path.Combine(_tempFolder, newFile);
                    WaveWriter tempWriter = new WaveWriter(newPath);
                    tempWriter.Input = rdr;
                    tempWriter.Format = rdr.Format;
                    tempWriter.BitsPerSample = rdr.BitsPerSample;
                    tempWriter.Normalization = -6;
                    tempWriter.Run();
                    tempWriter.Close();
                    rdr.Close();
                    rdr = null;
                    filePath = newPath;
                }

                // _soxExe = "sox";
                string exeName = _soxExe;
                if(File.Exists(Path.Combine(_pluginFolder, _soxExe + ".exe")))
                {
                    exeName = "\"" + Path.Combine(_pluginFolder, _soxExe + ".exe") + "\"";
                }
                else if (File.Exists(Path.Combine(_pluginFolder, _soxExe)))
                {
                    exeName = Path.Combine(_pluginFolder, _soxExe);
                }

                // _soxFmt = "\"{0}\" -r {1} \"{2}\" polyphase";
                string soxArgs = String.Format(_soxFmt, filePath, _inputSampleRate, resamplePath);

                Trace.WriteLine("Trying {0} {1}", exeName, soxArgs);

                System.Diagnostics.Process soxProcess = new System.Diagnostics.Process();
                System.Diagnostics.ProcessStartInfo soxInfo = new System.Diagnostics.ProcessStartInfo();
                soxInfo.Arguments = soxArgs;
                soxInfo.FileName = exeName;
                soxInfo.UseShellExecute = false;
                soxInfo.RedirectStandardError = true;

                soxProcess.StartInfo = soxInfo;
                try
                {
                    soxProcess.Start();

                    // Wait for the sox process to finish!
                    string err = soxProcess.StandardError.ReadToEnd();
                    soxProcess.WaitForExit(500);
                    if (soxProcess.HasExited)
                    {
                        int n = soxProcess.ExitCode;
                        if (n != 0)
                        {
                            Trace.WriteLine("No, that didn't seem to work: {0}", err);
                        }
                        else
                        {
                            Trace.WriteLine("Yes, that seemed to work.");
                            rdr = TryGetAppropriateImpulseReader(resamplePath, false, out actualPath);
                        }
                    }
                }
                catch (Exception e)
                {
                    Trace.WriteLine("That didn't seem to work: {0}", e.Message);
                }
            }
            if (rdr == null)
            {
                Trace.WriteLine("No suitable impulse found ({0}).", filePath);
            }
            return rdr;
        }
Beispiel #10
0
        static void LoadImpulse()
        {
            DateTime dtStart = DateTime.Now;
            string theImpulsePath = null;
            string theEQImpulseName = null;
            ISoundObj main = GetMainImpulse(out theImpulsePath);
            uint sr = (main == null ? _inputSampleRate : main.SampleRate);
            if (sr == 0)
            {
                if (_debug) { Trace.WriteLine("oops: no sample rate!"); }
                sr = 44100;
            }
            ISoundObj eq = GetEQImpulse(main, sr, out theEQImpulseName);
            ISoundObj combinedFilter = null;

            if (main == null && eq != null)
            {
                combinedFilter = eq;
            }
            else if (main != null && eq == null)
            {
                combinedFilter = main;
            }
            else if (main != null && eq != null)
            {
                // Check whether we have (and can load) a cached version of the combined filter
                string tempString = theEQImpulseName + "_" + theImpulsePath;
                string filterName = "CC" + tempString.GetHashCode().ToString("x10").ToUpperInvariant();
                string filterFile = Path.Combine(_tempFolder, filterName + ".filter");
                if (_debug)
                {
                    Trace.WriteLine(filterName);
                }
                if (File.Exists(filterFile))
                {
                    try
                    {
                        // Just read the cached EQ filter from disk
                        combinedFilter = new WaveReader(filterFile);
                    }
                    catch (Exception e)
                    {
                        if (_debug)
                        {
                            Trace.WriteLine("LoadImpulse1: " + e.Message);
                        }
                    }
                }

                if (combinedFilter == null)
                {
                    // Convolve the room-correction impulse with the EQ impulse to make just one.
                    // (this is quite slow)
                    FastConvolver temp = new FastConvolver();
                    temp.partitions = 0;
                    temp.impulse = eq;
                    temp.Input = main;
                    combinedFilter = temp;

                    try
                    {
                        // Write the combined impulse
                        temp.Reset();
                        WaveWriter tempWriter = new WaveWriter(filterFile);
                        tempWriter.Format = WaveFormat.IEEE_FLOAT;
                        tempWriter.BitsPerSample = 64;
                        tempWriter.Input = temp;
                        tempWriter.Run();
                        tempWriter.Close();

                        if (_debug)
                        {
                            // DEBUG: Write the combined impulse as WAV16
                            temp.Reset();
                            tempWriter = new WaveWriter(filterFile + ".wav");
                            tempWriter.Format = WaveFormat.PCM;
                            tempWriter.BitsPerSample = 16;
                            tempWriter.Gain = 0.1;
                            tempWriter.Dither = DitherType.NONE;//.TRIANGULAR;
                            tempWriter.Input = temp;
                            tempWriter.Run();
                            tempWriter.Close();
                        }
                    }
                    catch (Exception e)
                    {
                        if (_debug)
                        {
                            Trace.WriteLine("LoadImpulse2: " + e.Message);
                        }
                    }
                }
            }

            _MainConvolver.impulse = combinedFilter;

            if (combinedFilter != null)
            {
                // Calculate loudness-adjusted volume of each channel of the impulse
                _impulseVolumes.Clear();
                for (ushort j = 0; j < combinedFilter.NumChannels; j++)
                {
                    double v = Loudness.WeightedVolume(combinedFilter.Channel(j));
                    _impulseVolumes.Add(v);
                    if (_debug)
                    {
                        Trace.WriteLine("WV{0}: {1}", j, v);
                    }
                }
            }
            combinedFilter = null;

            if (_debug)
            {
                TimeSpan ts = DateTime.Now.Subtract(dtStart);
                Trace.WriteLine("Loadmpulse " + ts.TotalMilliseconds);
            }
        }
Beispiel #11
0
        static void InguzDSP(bool doRun)
        {
            DateTime dtStartRun = DateTime.Now;
            string sigdesc = null;

            // We need a few convolvers, of course
            if (_slow)
            {
                _MatrixConvolver = new SlowConvolver();
                _MainConvolver = new SlowConvolver();
                _EQConvolver = new SlowConvolver();
            }
            else
            {
                _MatrixConvolver = new FastConvolver();
                _MainConvolver = new FastConvolver();
                _EQConvolver = new FastConvolver();
            }

            // Shuffler
            _widthShuffler = new Shuffler();

            // Two skewers
            _depthSkewer = new Skewer(true);
            _skewSkewer = new Skewer(true);

            // Writer
            if (_outPath == null)
            {
                _writer = new WaveWriter();  // stdout
            }
            else
            {
                _writer = new WaveWriter(_outPath);
            }
            _writer.NumChannels = _outChannels;
            if (_debug)
            {
                TimeSpan ts = DateTime.Now.Subtract(dtStartRun);
                Trace.WriteLine("Setup " + ts.TotalMilliseconds);
            }

            /*
            DateTime exp = DSPUtil.DSPUtil.EXPIRY;
            if (exp != null)
            {
                if (DateTime.Now.CompareTo(exp) >= 0)
                {
                    Trace.WriteLine("**** THIS EVALUATION VERSION EXPIRED {0}", DSPUtil.DSPUtil.EXPIRY);
                    Trace.WriteLine("**** SEE http://www.inguzaudio.com/DSP/ FOR DETAILS");
                    _MatrixConvolver.Enabled = false;
                    _MainConvolver.Enabled = false;
                    _EQConvolver.Enabled = false;
                    Show("", "InguzDSP has expired.", 2);
                }
                else
                {
                    Trace.WriteLine("This evaluation version will expire {0}", DSPUtil.DSPUtil.EXPIRY);
                }
            }
            */

            // Read the configuration file
            doRun = LoadConfig2();

            // Do any cleanup required before we start
            CleanUp();

            // The main convolver should persist and re-use its leftovers between invocations
            // under this user's (=squeezebox's) ID
            _MainConvolver.partitions = _partitions;
            if (_tail && !IsSigGenNonEQ())
            {
                _MainConvolver.PersistPath = _tempFolder;
                _MainConvolver.PersistTail = _userID;
            }

            // Construct a second convolver for the "tone" (EQ) control.
            _EQConvolver.partitions = _partitions;
            if (_tail && !IsSigGenNonEQ())
            {
                _EQConvolver.PersistPath = _tempFolder;
                _EQConvolver.PersistTail = _userID + ".eq";
            }

            // Make a reader
            // _inPath is the data stream
            WaveReader inputReader = null;
            bool ok = false;
            try
            {
                if (_inStream != null)
                {
                    inputReader = new WaveReader(_inStream);
                }
                else if (_isRawIn)
                {
                    inputReader = new WaveReader(_inPath, _rawtype, _rawbits, _rawchan, _startTime);
                }
                else
                {
                    inputReader = new WaveReader(_inPath, _startTime);
                }
                inputReader.BigEndian = _bigEndian;
                ok = true;
            }
            catch (Exception e)
            {
                Trace.WriteLine("Unable to read: " + e.Message);
                // Just stop (no need to report the stack)
            }

            if (ok)
            {
                if (inputReader.IsSPDIF)
                {
                    // The wave file is just a SPDIF (IEC61937) stream, we shouldn't touch it
                    _inputSPDIF = true;
                    _isBypass = true;
                }
                if (_isBypass)
                {
                    // The settings file says bypass, we shouldn't touch it
                    _gain = 0;
                    _dither = DitherType.NONE;
                }

                uint sr = _inputSampleRate; // Yes, the commandline overrides the source-file...
                if (sr == 0)
                {
                    sr = inputReader.SampleRate;
                }
                if (sr == 0)
                {
                    sr = 44100;
                }
                _inputSampleRate = sr;

                if (WaveFormatEx.AMBISONIC_B_FORMAT_IEEE_FLOAT.Equals(inputReader.FormatEx) ||
                    WaveFormatEx.AMBISONIC_B_FORMAT_PCM.Equals(inputReader.FormatEx))
                {
                    _isBFormat = true;
                }

            }

            ISoundObj source = inputReader;
            if (IsSigGen())
            {
                // Signal-generator source instead of music.
                _isBFormat = false;
                source = GetSignalGenerator(-12, out sigdesc);
                Show("Test signal", sigdesc, 20);
            }

            if (IsSigGenNonEQ() || _isBypass)
            {
                // Signal-generator mode.  Overrides everything else!
                _writer.Input = source;
            }
            else
            {
                if (ok)
                {
                    // Load the room correction impulse to the convolver
                    // (NB: don't do this until we've created the reader, otherwise we can't be user of the samplerate yet...)
                    LoadImpulse();
                    GC.Collect();
                }

                if (ok && _isBFormat)
                {
                    source = DecodeBFormat(source);
                }

                if (ok)
                {
                    ISoundObj nextSrc;

                    // Perform width (matrix) processing on the signal
                    // - Shuffle the channels
                    // - Convolve if there's a filter
                    // - Apply gain to boost or cut the 'side' channel
                    // - Shuffle back
                    _widthShuffler.Input = source;
                    nextSrc = _widthShuffler;

                    // Use a convolver for the matrix filter
                    if (_matrixFilter != null)
                    {
                        LoadMatrixFilter();
                        _MatrixConvolver.Input = TwoChannel(nextSrc);
                        nextSrc = _MatrixConvolver as ISoundObj;
                    }

                    //                if (_depth != 0)
                    //                {
                    //                    // Time-alignment between the LR and MS
                    //                    _depthSkewer.Input = nextSrc;
                    //                    nextSrc = _depthSkewer;
                    //                }

                    // Shuffle back again
                    Shuffler shMSLR = new Shuffler();
                    shMSLR.Input = nextSrc;
                    nextSrc = shMSLR;

                    // Do the room-correction convolution
                    _MainConvolver.Input = TwoChannel(shMSLR);
                    nextSrc = _MainConvolver;

                    if (_skew != 0)
                    {
                        // time-alignment between left and right
                        _skewSkewer.Input = nextSrc;
                        nextSrc = _skewSkewer;
                    }

                    // Splice EQ and non-EQ channels
                    if (IsSigGenEQ())
                    {
                        ChannelSplicer splice = new ChannelSplicer();
                        if (IsSigGenEQL())
                        {
                            splice.Add(new SingleChannel(nextSrc, 0));
                        }
                        else
                        {
                            splice.Add(new SingleChannel(source, 0));
                        }
                        if (IsSigGenEQR())
                        {
                            splice.Add(new SingleChannel(nextSrc, 1));
                        }
                        else
                        {
                            splice.Add(new SingleChannel(source, 1));
                        }
                        nextSrc = splice;
                    }

                    // Process externally with aften or equivalent?
                    if (_aftenNeeded && !_isBypass)
                    {
                        nextSrc = AftenProcess(nextSrc);
                        _outFormat = WaveFormat.PCM;
                        _outBits = 16;
                        _dither = DitherType.NONE;
                    }

                    // Finally pipe this to the writer
                    _writer.Input = nextSrc;
                }
            }
            if (ok)
            {
                //dt = System.DateTime.Now;       // time to here is approx 300ms

                // Dither and output raw-format override anything earlier in the chain
                _writer.Dither = _isBypass ? DitherType.NONE : _dither;
                _writer.Raw = _isRawOut;
                _writer.Format = (_outFormat == WaveFormat.ANY) ? inputReader.Format : _outFormat;
                _writer.BitsPerSample = (_outBits == 0 || _isBypass) ? inputReader.BitsPerSample : _outBits;
                _writer.SampleRate = (_outRate == 0 || _isBypass) ? _inputSampleRate : _outRate;
                SetWriterGain();
                if (IsSigGen())
                {
                    Trace.WriteLine("Test signal: {0}, -12dBfs, {1}/{2} {3} {4}", sigdesc, _writer.BitsPerSample, _writer.SampleRate, _writer.Format, _writer.Dither);
                }
                string amb1 = "";
                string amb2 = "";
                if(_isBFormat)
                {
                    amb1 = "B-Format ";
                    amb2 = _ambiType + " ";
                }
                string big = inputReader.BigEndian ? "(Big-Endian) " : "";
                if (_inputSPDIF)
                {
                    Trace.WriteLine("Stream is SPDIF-wrapped; passing through");
                }
                else if (_isBypass)
                {
                    Trace.WriteLine("Processing is disabled; passing through");
                }
                Trace.WriteLine("{0}/{1} {2}{3} {4}=> {5}/{6} {7}{8} {9}, gain {10} dB", inputReader.BitsPerSample, _inputSampleRate, amb1, inputReader.Format, big, _writer.BitsPerSample, _writer.SampleRate, amb2, _writer.Format, _writer.Dither, _gain);

                TimeSpan elapsedInit = System.DateTime.Now.Subtract(dtStartRun);
                int n = _writer.Run();

                TimeSpan elapsedTotal = System.DateTime.Now.Subtract(dtStartRun);
                double realtime = n / _writer.SampleRate;
                double runtime = elapsedTotal.TotalMilliseconds / 1000;
                Trace.WriteLine("{0} samples, {1} ms ({2} init), {3} * realtime, peak {4} dBfs", n, elapsedTotal.TotalMilliseconds, elapsedInit.TotalMilliseconds, Math.Round(realtime / runtime, 4), Math.Round(_writer.dbfsPeak, 4));

                StopConfigListening();

                _writer.Close();
            }
        }