public unsafe void SampleBlockConversions()
        {
            SampleBlock reference16Bit;

            using (MediaFoundationReader reference16BitReader = new MediaFoundationReader(TestConstant.ReferenceFilePath16Bit))
            {
                byte[] buffer16Bit = new byte[reference16BitReader.Length];
                int    bytesRead   = reference16BitReader.Read(buffer16Bit, 0, buffer16Bit.Length);
                reference16Bit = new SampleBlock(buffer16Bit, bytesRead, SampleTypeExtensions.FromBitsPerSample(reference16BitReader.WaveFormat.BitsPerSample));
            }

            SampleBlock reference24Bit;

            using (MediaFoundationReader reference24BitReader = new MediaFoundationReader(TestConstant.ReferenceFilePath24Bit))
            {
                byte[] buffer24Bit = new byte[reference24BitReader.Length];
                int    bytesRead   = reference24BitReader.Read(buffer24Bit, 0, buffer24Bit.Length);
                reference24Bit = new SampleBlock(buffer24Bit, bytesRead, SampleTypeExtensions.FromBitsPerSample(reference24BitReader.WaveFormat.BitsPerSample));
            }

            Assert.IsTrue(reference16Bit.Int16Samples == reference24Bit.SamplesInUse);

            SampleBlock reference16BitAsDouble = reference16Bit.ConvertTo(SampleType.Double);
            SampleBlock reference16BitAsQ31    = reference16Bit.ConvertTo(SampleType.Int32);
            SampleBlock reference24BitAsDouble = reference24Bit.ConvertTo(SampleType.Double);
            SampleBlock reference24BitAsQ31    = reference24Bit.ConvertTo(SampleType.Int32);

            Assert.IsTrue(reference16Bit.Int16Samples == reference16BitAsDouble.DoubleSamples);
            Assert.IsTrue(reference16Bit.Int16Samples == reference16BitAsQ31.Int32Samples);
            Assert.IsTrue(reference16Bit.Int16Samples == reference24BitAsDouble.DoubleSamples);
            Assert.IsTrue(reference16Bit.Int16Samples == reference24BitAsQ31.Int32Samples);

            for (int sample = 0; sample < reference16Bit.Int16Samples; ++sample)
            {
                Assert.IsTrue(((int)reference16Bit.Int16s[sample] << TestConstant.ShiftBetween16BitSamplesAndQ31) == reference16BitAsQ31.Int32s[sample]);
                Assert.IsTrue((int)reference16BitAsDouble.Doubles[sample] == reference16BitAsQ31.Int32s[sample]);

                Assert.IsTrue((reference24Bit.GetInt24AsInt32(sample) << TestConstant.ShiftBetween24BitSamplesAndQ31) == reference24BitAsQ31.Int32s[sample]);
                Assert.IsTrue((int)reference24BitAsDouble.Doubles[sample] == reference24BitAsQ31.Int32s[sample]);
            }
        }
        private unsafe void VerifyWaveFilesEquivalent(string actualFilePath, string expectedFilePath, double expectedToActualScaleFactor, double sampleMatchTolerance, bool verifySampleSize)
        {
            // load data in files
            SampleBlock actual;
            WaveFormat  actualFormat;

            using (MediaFoundationReader actualReader = new MediaFoundationReader(actualFilePath))
            {
                byte[] buffer    = new byte[actualReader.Length];
                int    bytesRead = actualReader.Read(buffer, 0, buffer.Length);
                actual       = new SampleBlock(buffer, bytesRead, SampleTypeExtensions.FromBitsPerSample(actualReader.WaveFormat.BitsPerSample));
                actualFormat = actualReader.WaveFormat;
            }

            SampleBlock expected;
            WaveFormat  expectedFormat;

            using (MediaFoundationReader expectedReader = new MediaFoundationReader(expectedFilePath))
            {
                byte[] buffer    = new byte[expectedReader.Length];
                int    bytesRead = expectedReader.Read(buffer, 0, buffer.Length);
                expected       = new SampleBlock(buffer, bytesRead, SampleTypeExtensions.FromBitsPerSample(expectedReader.WaveFormat.BitsPerSample));
                expectedFormat = expectedReader.WaveFormat;
            }

            // check data format matches
            Assert.IsTrue(actual.SamplesInUse == expected.SamplesInUse);
            if (verifySampleSize)
            {
                Assert.IsTrue(actual.SampleType == expected.SampleType);
                Assert.IsTrue(actualFormat.AverageBytesPerSecond == expectedFormat.AverageBytesPerSecond);
                Assert.IsTrue(actualFormat.BitsPerSample == expectedFormat.BitsPerSample);
                Assert.IsTrue(actualFormat.BlockAlign == expectedFormat.BlockAlign);
            }
            Assert.IsTrue(actualFormat.Channels == expectedFormat.Channels);
            Assert.IsTrue(actualFormat.Encoding == expectedFormat.Encoding);
            Assert.IsTrue(actualFormat.ExtraSize == expectedFormat.ExtraSize);
            Assert.IsTrue(actualFormat.SampleRate == expectedFormat.SampleRate);

            double largestMismatch          = 0.0;
            double maxExpectedValue         = Math.Pow(2.0, expected.SampleType.BitsPerSample() - 1) - 1;
            double minExpectedValue         = -Math.Pow(2.0, expected.SampleType.BitsPerSample() - 1);
            double mismatchStandardDevation = 0.0;

            using (FileStream samplesStream = new FileStream(String.Format("{0} - {1}.csv", Path.GetFileNameWithoutExtension(actualFilePath), Path.GetFileNameWithoutExtension(expectedFilePath)), FileMode.Create))
            {
                using (StreamWriter samplesWriter = new StreamWriter(samplesStream))
                {
                    samplesWriter.WriteLine("actual,expected");
                    int clippedSamples    = 0;
                    int mismatchedSamples = 0;
                    int samples           = expected.SamplesInUse;
                    for (int sample = 0; sample < samples; ++sample)
                    {
                        // read samples as ints and convert to doubles regardless of sample type in files
                        double actualSample;
                        switch (actual.SampleType)
                        {
                        case SampleType.Int16:
                            actualSample = actual.Int16s[sample];
                            break;

                        case SampleType.Int24:
                            actualSample = actual.GetInt24AsInt32(sample);
                            break;

                        case SampleType.Int32:
                            actualSample = actual.Int32s[sample];
                            break;

                        default:
                            throw new NotSupportedException(String.Format("Unhandled sample type {0}.", expected.SampleType));
                        }
                        actualSample = actualSample / expectedToActualScaleFactor;

                        double expectedSample;
                        switch (expected.SampleType)
                        {
                        case SampleType.Int16:
                            expectedSample = expected.Int16s[sample];
                            break;

                        case SampleType.Int24:
                            expectedSample = expected.GetInt24AsInt32(sample);
                            break;

                        case SampleType.Int32:
                            expectedSample = expected.Int32s[sample];
                            break;

                        default:
                            throw new NotSupportedException(String.Format("Unhandled sample type {0}.", expected.SampleType));
                        }
                        samplesWriter.WriteLine("{0},{1}", actualSample, expectedSample);

                        // it's OK if the expected data is clipped and the actual data isn't
                        if (expectedSample >= maxExpectedValue && actualSample > expectedSample)
                        {
                            ++clippedSamples;
                            continue;
                        }
                        if (expectedSample <= minExpectedValue && actualSample < expectedSample)
                        {
                            ++clippedSamples;
                            continue;
                        }

                        // check samples for equivalence
                        // standard deviation calculation is naive as it assumes the average mismatch is zero
                        double mismatch = Math.Abs(expectedSample - actualSample);
                        mismatchStandardDevation += mismatch * mismatch;
                        if (mismatch > largestMismatch)
                        {
                            largestMismatch = mismatch;
                        }
                        if (mismatch > sampleMatchTolerance)
                        {
                            ++mismatchedSamples;
                        }
                    }

                    mismatchStandardDevation = Math.Sqrt(mismatchStandardDevation / (double)expected.SamplesInUse);
                    this.TestContext.WriteLine("Largest mismatch in samples between {0} and {1} was {2:0.0} with a standard deviation of {3:0.0}.", actualFilePath, expectedFilePath, largestMismatch, mismatchStandardDevation);
                    this.TestContext.WriteLine(">>> {0} samples exceeded threshold of {1:0.0}.", mismatchedSamples, sampleMatchTolerance);
                    Assert.IsTrue(mismatchedSamples == 0, "{0} of {1} ({2:#0.0%}) samples did not match within tolerance {3}.", mismatchedSamples, samples, (double)mismatchedSamples / (double)samples, sampleMatchTolerance);
                    if (verifySampleSize)
                    {
                        Assert.IsTrue(clippedSamples < 0.0001 * samples, "{0} (1:#0.000%) samples were clipped; this is unexpectedly high.", clippedSamples, (double)clippedSamples / (double)samples);
                    }
                }
            }

            // check metadata
            Tag actualMetadata;

            using (FileStream inputMetadataStream = new FileStream(actualFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                using (TagFile inputTagFile = TagFile.Create(new StreamFileAbstraction(inputMetadataStream.Name, inputMetadataStream, inputMetadataStream)))
                {
                    actualMetadata = inputTagFile.Tag;
                }
            }

            Tag expectedMetadata;

            using (FileStream outputMetadataStream = new FileStream(expectedFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                using (TagFile outputTagFile = TagFile.Create(new StreamFileAbstraction(outputMetadataStream.Name, outputMetadataStream, outputMetadataStream)))
                {
                    expectedMetadata = outputTagFile.Tag;
                }
            }

            Assert.AreEqual(expectedMetadata.Album, actualMetadata.Album, String.Format("Expected artist '{0}' but got '{1}'.", expectedMetadata.Album, actualMetadata.Album));
            Assert.AreEqual(expectedMetadata.AmazonId, actualMetadata.AmazonId, String.Format("Expected Amazon ID '{0}' but got '{1}'.", expectedMetadata.AmazonId, actualMetadata.AmazonId));
            Assert.AreEqual(expectedMetadata.Conductor, actualMetadata.Conductor, String.Format("Expected conductor '{0}' but got '{1}'.", expectedMetadata.Conductor, actualMetadata.Conductor));
            Assert.AreEqual(expectedMetadata.Copyright, actualMetadata.Copyright, String.Format("Expected copyright '{0}' but got '{1}'.", expectedMetadata.Copyright, actualMetadata.Copyright));
            Assert.AreEqual(expectedMetadata.Disc, actualMetadata.Disc, String.Format("Expected disc '{0}' but got '{1}'.", expectedMetadata.Disc, actualMetadata.Disc));
            Assert.AreEqual(expectedMetadata.DiscCount, actualMetadata.DiscCount, String.Format("Expected disc count '{0}' but got '{1}'.", expectedMetadata.DiscCount, actualMetadata.DiscCount));
            Assert.AreEqual(expectedMetadata.Grouping, actualMetadata.Grouping, String.Format("Expected grouping '{0}' but got '{1}'.", expectedMetadata.Grouping, actualMetadata.Grouping));
            Assert.AreEqual(expectedMetadata.Lyrics, actualMetadata.Lyrics, String.Format("Expected lyrics '{0}' but got '{1}'.", expectedMetadata.Lyrics, actualMetadata.Lyrics));
            Assert.AreEqual(expectedMetadata.JoinedAlbumArtists, actualMetadata.JoinedAlbumArtists, String.Format("Expected artists '{0}' but got '{1}'.", expectedMetadata.JoinedAlbumArtists, actualMetadata.JoinedAlbumArtists));
            Assert.AreEqual(expectedMetadata.JoinedComposers, actualMetadata.JoinedComposers, String.Format("Expected composers '{0}' but got '{1}'.", expectedMetadata.JoinedComposers, actualMetadata.JoinedComposers));
            Assert.AreEqual(expectedMetadata.JoinedGenres, actualMetadata.JoinedGenres, String.Format("Expected genres '{0}' but got '{1}'.", expectedMetadata.JoinedGenres, actualMetadata.JoinedGenres));
            Assert.AreEqual(expectedMetadata.JoinedPerformersSort, actualMetadata.JoinedPerformersSort, String.Format("Expected performers '{0}' but got '{1}'.", expectedMetadata.JoinedPerformersSort, actualMetadata.JoinedPerformersSort));
            // Pictures is not checked
            Assert.AreEqual(expectedMetadata.Title, actualMetadata.Title, String.Format("Expected title '{0}' but got '{1}'.", expectedMetadata.Title, actualMetadata.Title));
            Assert.AreEqual(expectedMetadata.Track, actualMetadata.Track, String.Format("Expected track '{0}' but got '{1}'.", expectedMetadata.Track, actualMetadata.Track));
            Assert.AreEqual(expectedMetadata.TrackCount, actualMetadata.TrackCount, String.Format("Expected track count '{0}' but got '{1}'.", expectedMetadata.TrackCount, actualMetadata.TrackCount));
            Assert.AreEqual(expectedMetadata.Year, actualMetadata.Year, String.Format("Expected year '{0}' but got '{1}'.", expectedMetadata.Year, actualMetadata.Year));
        }