【问题标题】:Reversing a .wav file反转 .wav 文件
【发布时间】:2015-05-05 00:11:04
【问题描述】:

我正在尝试在 C# 中反转 .wav 文件。 我基本上是在读取一个转发的 .wav 文件,并将格式/元数据字节直接复制到一个新的字节数组中。我将其余的“音频数据”字节反向复制到新的字节数组中。然后我用这个反向字节数组创建一个新流并将其写入文件。

但是,当我播放这个反向的 .wav 文件时,虽然我可以听到反向的单词,但音量要高得多,并且背景噪音很大。

我怎样才能完全反转 .wav 文件以使音量相同且没有背景噪音?

class Program
{
    static void Main(string[] args)
    {
        string localFolder = @"C:\Users\name\folder1";
        string forwardsWavFilePath = Path.Combine(localFolder, "forwardsFile.wav");
        FileStream forwardsWavFileStream = new FileStream(forwardsWavFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
        byte[] forwardsWavFileStreamByteArray = new byte[forwardsWavFileStream.Length];
        int numberOfBytesReadIntoForwardsWavFileStreamByteArray = forwardsWavFileStream.Read(forwardsWavFileStreamByteArray, 0, (int)forwardsWavFileStream.Length);

        int startIndexOfDataChunk = getStartIndexOfDataChunk(forwardsWavFileStreamByteArray);

        byte[] reversedWavFileStreamByteArray = new byte[forwardsWavFileStreamByteArray.Length];

        populateHeaderAndFormatChunk(forwardsWavFileStreamByteArray, startIndexOfDataChunk, reversedWavFileStreamByteArray);

        populateDataChunkInReverse(forwardsWavFileStreamByteArray, startIndexOfDataChunk, reversedWavFileStreamByteArray);

        string reversedWavFilePath = Path.Combine(localFolder, "reversedFile.wav");
        FileStream reversedFileStream = new FileStream(reversedWavFilePath, FileMode.Create, FileAccess.Write, FileShare.Write);
        reversedFileStream.Write(reversedWavFileStreamByteArray, 0, reversedWavFileStreamByteArray.Length);
    }

    private static void populateDataChunkInReverse(byte[] forwardsWavFileStreamByteArray, int startIndexOfAudioData, byte[] reversedWavFileStreamByteArray)
    {
        for (int i = forwardsWavFileStreamByteArray.Length - 1; i >= startIndexOfAudioData; i--)
        {
            reversedWavFileStreamByteArray[forwardsWavFileStreamByteArray.Length - 1 + startIndexOfAudioData - i] = forwardsWavFileStreamByteArray[i];
        }
    }

    private static void populateHeaderAndFormatChunk(byte[] forwardsWavFileStreamByteArray, int startIndexOfAudioData, byte[] reversedWavFileStreamByteArray)
    {
        for (int i = 0; i < startIndexOfAudioData; i++)
        {
            reversedWavFileStreamByteArray[i] = forwardsWavFileStreamByteArray[i];
        }
    }

    private static int getStartIndexOfDataChunk(byte[] forwardsWavFileStreamByteArray)
    {
        int startIndexOfAudioData = 12;
        int charDAsciiDecimalCode = 100; //'d'
        int charAAsciiDecimalCode = 97;  //'a'
        int charTAsciiDecimalCode = 116; //'t'

        //find "data" in the byte array
        while (!(forwardsWavFileStreamByteArray[startIndexOfAudioData] == charDAsciiDecimalCode && forwardsWavFileStreamByteArray[startIndexOfAudioData + 1] == charAAsciiDecimalCode && forwardsWavFileStreamByteArray[startIndexOfAudioData + 2] == charTAsciiDecimalCode && forwardsWavFileStreamByteArray[startIndexOfAudioData + 3] == charAAsciiDecimalCode))
        {
            startIndexOfAudioData += 4;
            int chunkSize = forwardsWavFileStreamByteArray[startIndexOfAudioData] + forwardsWavFileStreamByteArray[startIndexOfAudioData + 1] * 256 + forwardsWavFileStreamByteArray[startIndexOfAudioData + 2] * 65536 + forwardsWavFileStreamByteArray[startIndexOfAudioData + 3] * 16777216;
            startIndexOfAudioData += 4 + chunkSize;
        }
        startIndexOfAudioData += 8;
        return startIndexOfAudioData;
    }
}

【问题讨论】:

    标签: c# wav


    【解决方案1】:

    更新:我为此发布了一个 NuGet 包: https://www.nuget.org/packages/WaveFileManipulator/

    感谢 BrokenGlass 为我指明了正确的方向。 我确实在反转我只打算反转样本的字节。 我计算出每个样本的字节数,然后反转样本:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    
    namespace TestWavPlayer
    {
        class Program
        {
            const int _bitsPerByte = 8;
            static int _bytesPerSample;
    
            static void Main(string[] args)
            {
                string localFolder = @"C:\Users\username\AppData\LocalState";
    
                string forwardsWavFilePath = Path.Combine(localFolder, "forwardsFile.wav");
                byte[] forwardsWavFileStreamByteArray = populateForwardsWavFileByteArray(forwardsWavFilePath);
    
                getWavMetadata(forwardsWavFileStreamByteArray);
    
                int startIndexOfDataChunk = getStartIndexOfDataChunk(forwardsWavFileStreamByteArray);
    
                byte[] reversedWavFileStreamByteArray = populateReversedWavFileByteArray(forwardsWavFileStreamByteArray, startIndexOfDataChunk, _bytesPerSample);
    
                string reversedWavFilePath = Path.Combine(localFolder, "reversedFile.wav");
                writeReversedWavFileByteArrayToFile(reversedWavFileStreamByteArray, reversedWavFilePath);
            }
    
            private static void getWavMetadata(byte[] forwardsWavFileStreamByteArray)
            {
                MetadataGatherer.GetRiffText(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetFileSize(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetWaveText(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetFmtText(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetLengthOfFormatData(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetTypeOfFormat(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetNumOfChannels(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetSampleRate(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetBytesPerSecond(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetBlockAlign(forwardsWavFileStreamByteArray);
                _bytesPerSample = MetadataGatherer.GetBitsPerSample(forwardsWavFileStreamByteArray) / _bitsPerByte;
                MetadataGatherer.GetListText(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetDataText(forwardsWavFileStreamByteArray);
                MetadataGatherer.GetDataSize(forwardsWavFileStreamByteArray);
            }
    
            private static void writeReversedWavFileByteArrayToFile(byte[] reversedWavFileStreamByteArray, string reversedWavFilePath)
            {
                FileStream reversedFileStream = new FileStream(reversedWavFilePath, FileMode.Create, FileAccess.Write, FileShare.Write);
                reversedFileStream.Write(reversedWavFileStreamByteArray, 0, reversedWavFileStreamByteArray.Length);
            }
    
            private static byte[] populateReversedWavFileByteArray(byte[] forwardsWavFileStreamByteArray, int startIndexOfDataChunk, int bytesPerSample)
            {
                byte[] forwardsArrayWithOnlyHeaders = createForwardsArrayWithOnlyHeaders(forwardsWavFileStreamByteArray, startIndexOfDataChunk);
    
                byte[] forwardsArrayWithOnlyAudioData = createForwardsArrayWithOnlyAudioData(forwardsWavFileStreamByteArray, startIndexOfDataChunk);
    
                byte[] reversedArrayWithOnlyAudioData = reverseTheForwardsArrayWithOnlyAudioData(bytesPerSample, forwardsArrayWithOnlyAudioData);
    
                byte[] reversedWavFileStreamByteArray = combineArrays(forwardsArrayWithOnlyHeaders, reversedArrayWithOnlyAudioData);
    
                return reversedWavFileStreamByteArray;
            }
    
            private static byte[] combineArrays(byte[] forwardsArrayWithOnlyHeaders, byte[] reversedArrayWithOnlyAudioData)
            {
                byte[] reversedWavFileStreamByteArray = new byte[forwardsArrayWithOnlyHeaders.Length + reversedArrayWithOnlyAudioData.Length];
                Array.Copy(forwardsArrayWithOnlyHeaders, reversedWavFileStreamByteArray, forwardsArrayWithOnlyHeaders.Length);
                Array.Copy(reversedArrayWithOnlyAudioData, 0, reversedWavFileStreamByteArray, forwardsArrayWithOnlyHeaders.Length, reversedArrayWithOnlyAudioData.Length);
                return reversedWavFileStreamByteArray;
            }
    
            private static byte[] reverseTheForwardsArrayWithOnlyAudioData(int bytesPerSample, byte[] forwardsArrayWithOnlyAudioData)
            {
                int length = forwardsArrayWithOnlyAudioData.Length;
                byte[] reversedArrayWithOnlyAudioData = new byte[length];
    
                int sampleIdentifier = 0;
    
                for (int i = 0; i < length; i++)
                {
                    if (i != 0 && i % bytesPerSample == 0)
                    {
                        sampleIdentifier += 2 * bytesPerSample;
                    }
                    int index = length - bytesPerSample - sampleIdentifier + i;
                    reversedArrayWithOnlyAudioData[i] = forwardsArrayWithOnlyAudioData[index];
                }
                return reversedArrayWithOnlyAudioData;
            }
    
            private static byte[] createForwardsArrayWithOnlyAudioData(byte[] forwardsWavFileStreamByteArray, int startIndexOfDataChunk)
            {
                byte[] forwardsArrayWithOnlyAudioData = new byte[forwardsWavFileStreamByteArray.Length - startIndexOfDataChunk];
                Array.Copy(forwardsWavFileStreamByteArray, startIndexOfDataChunk, forwardsArrayWithOnlyAudioData, 0, forwardsWavFileStreamByteArray.Length - startIndexOfDataChunk);
                return forwardsArrayWithOnlyAudioData;
            }
    
            private static byte[] createForwardsArrayWithOnlyHeaders(byte[] forwardsWavFileStreamByteArray, int startIndexOfDataChunk)
            {
                byte[] forwardsArrayWithOnlyHeaders = new byte[startIndexOfDataChunk];
                Array.Copy(forwardsWavFileStreamByteArray, 0, forwardsArrayWithOnlyHeaders, 0, startIndexOfDataChunk);
                return forwardsArrayWithOnlyHeaders;
            }
    
            private static byte[] populateForwardsWavFileByteArray(string forwardsWavFilePath)
            {
                FileStream forwardsWavFileStream = new FileStream(forwardsWavFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
                byte[] forwardsWavFileStreamByteArray = new byte[forwardsWavFileStream.Length];
                forwardsWavFileStream.Read(forwardsWavFileStreamByteArray, 0, (int)forwardsWavFileStream.Length);
                return forwardsWavFileStreamByteArray;
            }
    
            private static int getStartIndexOfDataChunk(byte[] forwardsWavFileStreamByteArray)
            {
                int startIndexOfAudioData = 12;
                int charDAsciiDecimalCode = 100; //'d' //data is located at index 70 in my .wav file
                int charAAsciiDecimalCode = 97;  //'a'
                int charTAsciiDecimalCode = 116; //'t'
    
                int chunkSize;
    
                //find "data" in the byte array
                while (!(forwardsWavFileStreamByteArray[startIndexOfAudioData] == charDAsciiDecimalCode && forwardsWavFileStreamByteArray[startIndexOfAudioData + 1] == charAAsciiDecimalCode && forwardsWavFileStreamByteArray[startIndexOfAudioData + 2] == charTAsciiDecimalCode && forwardsWavFileStreamByteArray[startIndexOfAudioData + 3] == charAAsciiDecimalCode))
                {
                    startIndexOfAudioData += 4;
                    chunkSize = forwardsWavFileStreamByteArray[startIndexOfAudioData] + forwardsWavFileStreamByteArray[startIndexOfAudioData + 1] * 256 + forwardsWavFileStreamByteArray[startIndexOfAudioData + 2] * 65536 + forwardsWavFileStreamByteArray[startIndexOfAudioData + 3] * 16777216;
                    startIndexOfAudioData += 4 + chunkSize;
                }
                startIndexOfAudioData += 8;
                return startIndexOfAudioData;
            }
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace TestWavPlayer
    {
        static class MetadataGatherer
        {
            internal static ushort GetTypeOfFormat(byte[] forwardsWavFileStreamByteArray)
            {
                int startIndex = 20;
                int endIndex = 21;
                byte[] typeOfFormatByteArray = GetRelevantBytesIntoNewArray(forwardsWavFileStreamByteArray, startIndex, endIndex);
                ushort typeOfFormat = BitConverter.ToUInt16(typeOfFormatByteArray, 0);
                Console.WriteLine("Type of format (1 is PCM) = {0}", typeOfFormat);
                return typeOfFormat;
            }
    
            internal static void GetFmtText(byte[] forwardsWavFileStreamByteArray)
            {
                int startIndex = 12;
                int endIndex = 15;
                GetAsciiText(forwardsWavFileStreamByteArray, startIndex, endIndex);
            }
    
            internal static string GetWaveText(byte[] forwardsWavFileStreamByteArray)
            {
                int startIndex = 8;
                int endIndex = 11;
                return GetAsciiText(forwardsWavFileStreamByteArray, startIndex, endIndex);
            }
    
            internal static string GetRiffText(byte[] forwardsWavFileStreamByteArray)
            {
                int startIndex = 0;
                int endIndex = 3;
                return GetAsciiText(forwardsWavFileStreamByteArray, startIndex, endIndex);
            }
    
            internal static uint GetLengthOfFormatData(byte[] forwardsWavFileStreamByteArray)
            {
                int startIndex = 16;
                int endIndex = 19;
                byte[] lengthOfFormatDataByteArray = GetRelevantBytesIntoNewArray(forwardsWavFileStreamByteArray, startIndex, endIndex);
                uint lengthOfFormatData = BitConverter.ToUInt32(lengthOfFormatDataByteArray, 0);
                Console.WriteLine("Length of format data = {0}", lengthOfFormatData);
                return lengthOfFormatData;
            }
    
            internal static byte[] GetRelevantBytesIntoNewArray(byte[] forwardsWavFileStreamByteArray, int startIndex, int endIndex)
            {
                int length = endIndex - startIndex + 1;
                byte[] relevantBytesArray = new byte[length];
                Array.Copy(forwardsWavFileStreamByteArray, startIndex, relevantBytesArray, 0, length);
                return relevantBytesArray;
            }
    
            internal static uint GetFileSize(byte[] forwardsWavFileStreamByteArray)
            {
                int fileSizeStartIndex = 4;
                int fileSizeEndIndex = 7;
                byte[] fileSizeByteArray = GetRelevantBytesIntoNewArray(forwardsWavFileStreamByteArray, fileSizeStartIndex, fileSizeEndIndex);
                uint fileSize = BitConverter.ToUInt32(fileSizeByteArray, 0) + 8; //need to add the size of the 
                Console.WriteLine("File size = {0}", fileSize);
                return fileSize;
            }
    
            internal static string GetAsciiText(byte[] forwardsWavFileStreamByteArray, int startIndex, int endIndex)
            {
                string asciiText = "";
                for (int i = startIndex; i <= endIndex; i++)
                {
                    asciiText += Convert.ToChar(forwardsWavFileStreamByteArray[i]);
                }
                Console.WriteLine(asciiText);
                return asciiText;
            }
    
            internal static ushort GetNumOfChannels(byte[] forwardsWavFileStreamByteArray)
            {
                int numOfChannelsStartIndex = 22;
                int numOfChannelsEndIndex = 23;
                byte[] numOfChannelsByteArray = GetRelevantBytesIntoNewArray(forwardsWavFileStreamByteArray, numOfChannelsStartIndex, numOfChannelsEndIndex);
                ushort numOfChannels = BitConverter.ToUInt16(numOfChannelsByteArray, 0); //need to add the size of the 
                Console.WriteLine("Number Of Channels = {0}", numOfChannels);
                return numOfChannels;
            }
    
            internal static uint GetSampleRate(byte[] forwardsWavFileStreamByteArray)
            {
                int sampleRateStartIndex = 24;
                int sampleRateEndIndex = 27;
                byte[] sampleRateByteArray = GetRelevantBytesIntoNewArray(forwardsWavFileStreamByteArray, sampleRateStartIndex, sampleRateEndIndex);
                uint sampleRate = BitConverter.ToUInt32(sampleRateByteArray, 0); //need to add the size of the 
                Console.WriteLine("Sample Rate = {0}", sampleRate);
                return sampleRate;
            }
    
            internal static uint GetBytesPerSecond(byte[] forwardsWavFileStreamByteArray)
            {
                int bytesPerSecondStartIndex = 28;
                int bytesPerSecondEndIndex = 31;
                byte[] bytesPerSecondByteArray = GetRelevantBytesIntoNewArray(forwardsWavFileStreamByteArray, bytesPerSecondStartIndex, bytesPerSecondEndIndex);
                uint bytesPerSecond = BitConverter.ToUInt32(bytesPerSecondByteArray, 0); //need to add the size of the 
                Console.WriteLine("Bytes Per Second = {0}", bytesPerSecond);
                return bytesPerSecond;
            }
    
            internal static ushort GetBlockAlign(byte[] forwardsWavFileStreamByteArray)
            {
                int blockAlignStartIndex = 32;
                int blockAlignEndIndex = 33;
                byte[] blockAlignByteArray = GetRelevantBytesIntoNewArray(forwardsWavFileStreamByteArray, blockAlignStartIndex, blockAlignEndIndex);
                ushort blockAlign = BitConverter.ToUInt16(blockAlignByteArray, 0); //need to add the size of the 
                Console.WriteLine("Block Align = {0}", blockAlign);
                return blockAlign;
            }
    
            internal static ushort GetBitsPerSample(byte[] forwardsWavFileStreamByteArray)
            {
                int bitsPerSampleStartIndex = 34;
                int bitsPerSampleEndIndex = 35;
                byte[] bitsPerSampleByteArray = GetRelevantBytesIntoNewArray(forwardsWavFileStreamByteArray, bitsPerSampleStartIndex, bitsPerSampleEndIndex);
                ushort bitsPerSample = BitConverter.ToUInt16(bitsPerSampleByteArray, 0); //need to add the size of the 
                Console.WriteLine("Bits Per Sample = {0}", bitsPerSample);
                return bitsPerSample;
            }
    
            internal static void GetDataText(byte[] forwardsWavFileStreamByteArray)
            {
                //should be these values according to http://www.topherlee.com/software/pcm-tut-wavformat.html
                //int startIndex = 36; //this is the index of "LIST" not "data" :S
                //int endIndex = 39;
    
                //data is located at index 70 in my .wav file
                int startIndex = 70;
                int endIndex = 73;
                GetAsciiText(forwardsWavFileStreamByteArray, startIndex, endIndex);
            }
    
            internal static void GetListText(byte[] forwardsWavFileStreamByteArray)
            {
                int startIndex = 36; //this is the index of "LIST"
                int endIndex = 39;
                GetAsciiText(forwardsWavFileStreamByteArray, startIndex, endIndex);
            }
    
            internal static uint GetDataSize(byte[] forwardsWavFileStreamByteArray)
            {
                int dataSizeStartIndex = 70;
                int dataSizeEndIndex = 73;
                byte[] dataSizeByteArray = GetRelevantBytesIntoNewArray(forwardsWavFileStreamByteArray, dataSizeStartIndex, dataSizeEndIndex);
                uint dataSize = BitConverter.ToUInt16(dataSizeByteArray, 0); //need to add the size of the 
                Console.WriteLine("Data Size = {0}", dataSize);
                return dataSize;
            }
        }
    }
    

    这里有一个tutorial 描述了整个过程。

    【讨论】:

      【解决方案2】:

      您目前只是反转 字节流 - 但您应该做的是反转 样本流。每个样本可能超过一个字节,因此会导致失真。您可以从标题中找到样本大小和通道数(例如立体声)。

      有关标头格式的描述,请参阅here - 您需要每个样本的位数和通道数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-14
        • 1970-01-01
        • 1970-01-01
        • 2016-06-19
        • 1970-01-01
        • 1970-01-01
        • 2020-09-22
        • 2016-10-02
        相关资源
        最近更新 更多