public void FileCreationAndPreservation()
        {
            string inputFileName = "fileCreationAndPreservation.wav";
            string inputFilePath = Path.Combine(Environment.CurrentDirectory, inputFileName);

            if (!File.Exists(inputFilePath))
            {
                File.Copy(TestConstant.SourceFilePath16Bit, inputFilePath);
            }

            CrossTimeEngine engine         = new CrossTimeEngine(TestConstant.DefaultConfigurationFile, this);
            string          outputFileName = String.Format("{0}{1}.wav", Path.GetFileNameWithoutExtension(inputFilePath), engine.Configuration.Output.CollidingFileNamePostfix);

            this.RemoveExistingOutputFiles(outputFileName);
            DateTime startUtc = DateTime.UtcNow;

            // process file and output file should be created
            engine.Configuration.Filters.Clear();
            engine.FilterFiles(Environment.CurrentDirectory, inputFileName, Environment.CurrentDirectory);
            DateTime firstOutputFileWriteTimeUtc = File.GetLastWriteTimeUtc(outputFileName);

            Assert.IsTrue(firstOutputFileWriteTimeUtc > startUtc);

            // since no filters are configured the output file should be a clone of the input
            this.VerifyWaveFilesEquivalent(outputFileName, inputFileName, 1.0, 0.0, true);

            // filter again and the output file should not be touched
            engine.FilterFiles(Environment.CurrentDirectory, inputFileName, Environment.CurrentDirectory);
            DateTime secondOutputFileWriteTimeUtc = File.GetLastWriteTimeUtc(outputFileName);

            Assert.AreEqual(firstOutputFileWriteTimeUtc, secondOutputFileWriteTimeUtc, "Output file {0} was touched after initial write at {1} by second write at {2}.", inputFilePath, firstOutputFileWriteTimeUtc, secondOutputFileWriteTimeUtc);
        }
        private void FilterFirstTrackInLibrary(SampleType dataPathSampleType, SampleType outputSampleType, string configFilePath)
        {
            CrossTimeEngine dspEngine = new CrossTimeEngine(configFilePath, this);

            string musicFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
            string firstArtistPath = Directory.GetDirectories(musicFolderPath).First();
            string firstAlbumPath  = Directory.GetDirectories(firstArtistPath).First();
            string firstTrackPath  = Directory.GetFiles(firstAlbumPath, "*.flac").First();

            this.TestContext.WriteLine("Load and buffer");
            DateTime loadStartedUtc           = DateTime.UtcNow;
            MediaFoundationReader inputStream = new MediaFoundationReader(firstTrackPath);

            using (SampleBuffer inputBuffer = new SampleBuffer(inputStream))
            {
                DateTime loadStoppedUtc = DateTime.UtcNow;
                this.TestContext.WriteLine("{0}", (loadStoppedUtc - loadStartedUtc).ToString(Constant.ElapsedTimeFormat));

                this.TestContext.WriteLine("Warmup iterations");
                for (int warmup = 0; warmup < 10; ++warmup)
                {
                    this.FilterStream(dspEngine.Configuration, inputBuffer, dataPathSampleType, outputSampleType);
                }

                this.TestContext.WriteLine("Running iterations");
                for (int iteration = 0; iteration < 25; ++iteration)
                {
                    this.FilterStream(dspEngine.Configuration, inputBuffer, dataPathSampleType, outputSampleType);
                }
            }
        }
        public void InverseAllpassAndWav()
        {
            // remove any existing output files to guarantee test runs on up to date data
            string outputFileName16Bit  = "inverseAllpassAndWav.44100.16.double.wav";
            string outputFileName24Bit  = "inverseAllpassAndWav.44100.24.double.wav";
            string outputFileNameQ31x63 = "inverseAllpassAndWav.44100.24.Q31_32x64.wav";

            this.RemoveExistingOutputFiles(outputFileName16Bit, outputFileName24Bit, outputFileNameQ31x63);

            // run test cases
            // check 24 bit cases
            CrossTimeEngine engine = new CrossTimeEngine(TestConstant.DefaultConfigurationFile, this);

            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, outputFileName24Bit);
            this.VerifyWaveFilesEquivalent(outputFileName24Bit, TestConstant.ReferenceFilePath24Bit, TestConstant.ReferenceFileScaleFactor, 1.5 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);

            // check cross bitness case - cases in the same bitness are less likely to catch conversion and 24 bit handling defects
            // Scale factor is bit length times anti-clipping gain.
            // Tolerance here may seem loose but comparing 24 bit samples to their truncated 16 bit form is inexact.
            this.VerifyWaveFilesEquivalent(outputFileName24Bit, TestConstant.ReferenceFilePath16Bit, 0.3162 * 256.0, 40.5 * TestConstant.Q23ToQ31TruncationErrorIncrease, false);

            // check fixed point
            engine.Configuration.Engine.Precision = FilterPrecision.Q31_32x64;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, outputFileNameQ31x63);
            this.VerifyWaveFilesEquivalent(outputFileNameQ31x63, TestConstant.ReferenceFilePath24Bit, TestConstant.ReferenceFileScaleFactor, 1.6 * TestConstant.Q23ToQ31TruncationErrorIncrease, false);

            // check 16 bit cases
            double adjustmentToUnityGain = -engine.Configuration.Engine.ReverseTimeAntiClippingAttenuationInDB;

            engine.Configuration.Engine.Precision = FilterPrecision.Double;
            engine.Configuration.Engine.ReverseTimeAntiClippingAttenuationInDB += adjustmentToUnityGain;
            Filter firstReverseTimeFilter = engine.Configuration.Filters.FirstOrDefault(filter => filter.TimeDirection == TimeDirection.Reverse);

            if (firstReverseTimeFilter != null)
            {
                firstReverseTimeFilter.AdjustGain(adjustmentToUnityGain);
            }
            engine.Configuration.Output.BitsPerSample = 16;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, outputFileName16Bit);
            this.VerifyWaveFilesEquivalent(outputFileName16Bit, TestConstant.ReferenceFilePath16Bit, 1.0, 1.0 * TestConstant.Q15ToQ31TruncationErrorIncrease, true);
        }
        public void ForwardTimeCrossoverAndEQ()
        {
            // setup
            string midrangeFileNameDouble                 = "crossoverAndEQ.midrange.44100.24.double.wav";
            string midrangeFileNameQ31                    = "crossoverAndEQ.midrange.44100.24.Q31.wav";
            string midrangeFileNameQ31_32x64              = "crossoverAndEQ.midrange.44100.24.Q31_32x64.wav";
            string midrangeFileNameQ31Adaptive            = "crossoverAndEQ.midrange.44100.24.Q31Adaptive.wav";
            string tweeterFileNameDouble                  = "crossoverAndEQ.tweeter.44100.24.double.wav";
            string tweeterFileNameQ31                     = "crossoverAndEQ.tweeter.44100.24.Q31.wav";
            string tweeterFileNameQ31_32x64               = "crossoverAndEQ.tweeter.44100.24.Q31_32x64.wav";
            string tweeterFileNameQ31Adaptive             = "crossoverAndEQ.tweeter.44100.24.Q31.wav";
            string subwooferFileNameDouble                = "crossoverAndEQ.subwoofer.44100.24.double.wav";
            string subwooferFileNameQ31_32x64             = "crossoverAndEQ.subwoofer.44100.24.Q31_32x64.wav";
            string subwooferFileNameQ31_64x64             = "crossoverAndEQ.subwoofer.44100.24.Q31_64x64.wav";
            string subwooferFileNameQ31Adaptive           = "crossoverAndEQ.subwoofer.44100.24.Q31.wav";
            string subwooferLinearizedFileNameDouble      = "crossoverEQAndLinearization.subwoofer.44100.24.double.wav";
            string subwooferLinearizedFileNameQ31Adaptive = "crossoverEQAndLinearization.subwoofer.44100.24.Q31.wav";

            this.RemoveExistingOutputFiles(midrangeFileNameDouble, midrangeFileNameQ31, midrangeFileNameQ31_32x64, midrangeFileNameQ31Adaptive,
                                           tweeterFileNameDouble, tweeterFileNameQ31, tweeterFileNameQ31_32x64, tweeterFileNameQ31Adaptive,
                                           subwooferFileNameDouble, subwooferFileNameQ31_32x64, subwooferFileNameQ31_64x64, subwooferFileNameQ31Adaptive,
                                           subwooferLinearizedFileNameDouble, subwooferLinearizedFileNameQ31Adaptive);
            CrossTimeEngine engine = new CrossTimeEngine(TestConstant.DefaultConfigurationFile, this);

            // midrange
            engine.Configuration.Filters.Clear();
            // 200Hz LR6 highpass
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Highpass, 200.0, 0.0, 0.5, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Highpass, 200.0, 0.0, 1.0, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Highpass, 200.0, 0.0, 1.0, TimeDirection.Forward));
            // 1700Hz LR6 lowpass
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Lowpass, 1700.0, 0.0, 0.5, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Lowpass, 1700.0, 0.0, 1.0, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Lowpass, 1700.0, 0.0, 1.0, TimeDirection.Forward));
            // dipole EQ
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Peaking, 150.0, 15.0, 0.5, TimeDirection.Forward));
            // driver EQ
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Peaking, 550.0, 4.0, 1.0, TimeDirection.Forward));

            engine.Configuration.Engine.Precision = FilterPrecision.Double;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, midrangeFileNameDouble);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, midrangeFileNameQ31);
            this.VerifyWaveFilesEquivalent(midrangeFileNameQ31, midrangeFileNameDouble, 1.0, 56.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31_32x64;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, midrangeFileNameQ31_32x64);
            this.VerifyWaveFilesEquivalent(midrangeFileNameQ31_32x64, midrangeFileNameDouble, 1.0, 6.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31Adaptive;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, midrangeFileNameQ31Adaptive);
            this.VerifyWaveFilesEquivalent(midrangeFileNameQ31Adaptive, midrangeFileNameDouble, 1.0, 6.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);

            // tweeter
            engine.Configuration.Filters.Clear();
            // 1700Hz LR6 highpass
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Highpass, 1700.0, 0.0, 0.5, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Highpass, 1700.0, 0.0, 1.0, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Highpass, 1700.0, 0.0, 1.0, TimeDirection.Forward));
            // dipole EQ
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Peaking, 1400.0, 5.0, 1.9, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Peaking, 3800.0, -2.0, 3.0, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Peaking, 7300.0, 3.5, 2.0, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Peaking, 13000.0, -3.5, 4.0, TimeDirection.Forward));

            Trace.TraceInformation(String.Empty);
            engine.Configuration.Engine.Precision = FilterPrecision.Double;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, tweeterFileNameDouble);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, tweeterFileNameQ31);
            this.VerifyWaveFilesEquivalent(tweeterFileNameQ31, tweeterFileNameDouble, 1.0, 1.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31_32x64;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, tweeterFileNameQ31_32x64);
            this.VerifyWaveFilesEquivalent(tweeterFileNameQ31_32x64, tweeterFileNameDouble, 1.0, 1.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31Adaptive;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, tweeterFileNameQ31Adaptive);
            this.VerifyWaveFilesEquivalent(tweeterFileNameQ31Adaptive, tweeterFileNameDouble, 1.0, 1.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);

            // subwoofer
            engine.Configuration.Filters.Clear();
            // 200Hz LR6 lowpass
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Lowpass, 200.0, 0.0, 0.5, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Lowpass, 200.0, 0.0, 1.0, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Lowpass, 200.0, 0.0, 1.0, TimeDirection.Forward));
            // dipole EQ
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Peaking, 39.0, 14.0, 0.5, TimeDirection.Forward));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Peaking, 180.0, 2.0, 1.0, TimeDirection.Forward));
            // forward time phase linearization
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Allpass, 150.0, 0.0, 0.5 * Math.Sqrt(2.0), TimeDirection.Forward));

            Trace.TraceInformation(String.Empty);
            engine.Configuration.Engine.Precision = FilterPrecision.Double;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, subwooferFileNameDouble);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31_32x64;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, subwooferFileNameQ31_32x64);
            this.VerifyWaveFilesEquivalent(subwooferFileNameQ31_32x64, subwooferFileNameDouble, 1.0, 37.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31_64x64;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, subwooferFileNameQ31_64x64);
            this.VerifyWaveFilesEquivalent(subwooferFileNameQ31_64x64, subwooferFileNameDouble, 1.0, 1.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31Adaptive;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, subwooferFileNameQ31Adaptive);
            this.VerifyWaveFilesEquivalent(subwooferFileNameQ31Adaptive, subwooferFileNameDouble, 1.0, 28.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);

            // subwoofer with reverse time phase linearization
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Allpass, 200.0, 0.0, 1.0, TimeDirection.Reverse));
            engine.Configuration.Filters.Add(this.CreateBiquad(FilterType.Allpass, 39.0, 0.0, 0.5 * Math.Sqrt(2.0), TimeDirection.Reverse));

            engine.Configuration.Engine.Precision = FilterPrecision.Double;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, subwooferLinearizedFileNameDouble);
            engine.Configuration.Engine.Precision = FilterPrecision.Q31Adaptive;
            engine.FilterFiles(Environment.CurrentDirectory, TestConstant.SourceFilePath16Bit, subwooferLinearizedFileNameQ31Adaptive);
            this.VerifyWaveFilesEquivalent(subwooferLinearizedFileNameQ31Adaptive, subwooferLinearizedFileNameDouble, 1.0, 28.0 * TestConstant.Q23ToQ31TruncationErrorIncrease, true);
        }