简体   繁体   中英

What am I doing wrong when parsing a wav file?

I'm trying to parse a wav file. I'm not sure if there can be multiple data chunks in a wav file, but I originally assumed there was only 1 since the wav file format description I was reading only mentioned there being 1.

But I noticed that the subchunk2size was very small (like 26) when the wav file being parsed was something like 36MB and the sample rate was 44100.

So I tried to parse it assuming there were multiple chunks, but after the 1st chunk, there was no subchunk2id to be found.

To go chunk by chunk, I was using the below code

int chunkSize = System.BitConverter.ToInt32(strm, 40);
int widx = 44; //wav data starts at the 44th byte
//strm is a byte array of the wav file
while(widx < strm.Length)
{
    widx += chunkSize;
    if(widx < 1000)
    {
        //log "data" or "100 97 116 97" for the subchunkid
        //This is only getting printed the 1st time though. All prints after that are garbage
        Debug.Log( strm[widx] + " " + strm[widx+1] + " " + strm[widx+2] + " " + strm[widx+3]);
    }
    if(widx + 8 < strm.Length)
    {
        widx += 4;
        chunkSize = System.BitConverter.ToInt32(strm, widx);
        widx += 4;
    }else
    {
        widx += 8;
    }
}

A .wav-File has 3 chunks: Each chunk has a size of 4 Byte

The first chunk is the "RIFF"-chunk. It includes 8 Byte the filesize(4 Byte) and the name of the format(4byte, usually "WAVE").

The next chunk is the "fmt "-chunk (the space in the chunk-name is important). It includes the audio-format(2 Byte), the number of channels (2 Byte), the sample rate (4 Byte), the byte rate (4 Byte), blockalign (2 Byte) and the bits per sample (2 Byte).

The third and last chunk is the data-chunk. Here are the real data and the amplitudes of the samples. It includes 4 Byte for the datasize, which is the number of bytes for the data.

You can find further explanations of the properties of a .wav-file here .

From this knowledge I have already created the following class:

public sealed class WaveFile
{
    //privates
    private int fileSize;
    private string format;
    private int fmtChunkSize;
    private int audioFormat;
    private int numChannels;
    private int sampleRate;
    private int byteRate;
    private int blockAlign;
    private int bitsPerSample;
    private int dataSize;
    private int[][] data;//One array per channel

    //publics
    public int FileSize => fileSize;
    public string Format => format;
    public int FmtChunkSize => fmtChunkSize;
    public int AudioFormat => audioFormat;
    public int NumChannels => numChannels;
    public int SampleRate => sampleRate;
    public int ByteRate => byteRate;
    public int BitsPerSample => bitsPerSample;
    public int DataSize => dataSize;
    public int[][] Data => data;

    public WaveFile(string path)
    {
        FileStream fs = File.OpenRead(path);
        LoadChunk(fs); //read RIFF Chunk
        LoadChunk(fs); //read fmt Chunk
        LoadChunk(fs); //read data Chunk
        fs.Close();
    }

    private void LoadChunk(FileStream fs)
    {
        ASCIIEncoding Encoder = new ASCIIEncoding();
        byte[] bChunkID = new byte[4];

        fs.Read(bChunkID, 0, 4);
        string sChunkID = Encoder.GetString(bChunkID);

        byte[] ChunkSize = new byte[4];

        fs.Read(ChunkSize, 0, 4);

        if (sChunkID.Equals("RIFF"))
        {
            fileSize = BitConverter.ToInt32(ChunkSize, 0);

            byte[] Format = new byte[4];
            fs.Read(Format, 0, 4);
            this.format = Encoder.GetString(Format);
        }

        if (sChunkID.Equals("fmt "))
        {
            fmtChunkSize = BitConverter.ToInt32(ChunkSize, 0);
            byte[] audioFormat = new byte[2];
            fs.Read(audioFormat, 0, 2);
            this.audioFormat = BitConverter.ToInt16(audioFormat, 0);
            byte[] numChannels = new byte[2];
            fs.Read(numChannels, 0, 2);
            this.numChannels = BitConverter.ToInt16(numChannels, 0);
            byte[] sampleRate = new byte[4];
            fs.Read(sampleRate, 0, 4);
            this.sampleRate = BitConverter.ToInt32(sampleRate, 0);
            byte[] byteRate = new byte[4];
            fs.Read(byteRate, 0, 4);
            this.byteRate = BitConverter.ToInt32(byteRate, 0);
            byte[] blockAlign = new byte[2];
            fs.Read(blockAlign, 0, 2);
            this.blockAlign = BitConverter.ToInt16(blockAlign, 0);
            byte[] bitsPerSample = new byte[2];
            fs.Read(bitsPerSample, 0, 2);
            this.bitsPerSample = BitConverter.ToInt16(bitsPerSample, 0);
        }

        if (sChunkID.Equals("data"))
        {
            dataSize = BitConverter.ToInt32(ChunkSize, 0);
            data = new int[this.numChannels][];

            byte[] temp = new byte[dataSize];

            for (int i = 0; i < this.numChannels; i++)
            {
                data[i] = new int[this.dataSize / (numChannels * bitsPerSample / 8)];
            }

            for (int i = 0; i < data[0].Length; i++)
            {
                for (int j = 0; j < numChannels; j++)
                {
                    if (fs.Read(temp, 0, blockAlign / numChannels) > 0)
                    {
                        if (blockAlign / numChannels == 2)
                        { data[j][i] = BitConverter.ToInt32(temp, 0); }
                        else
                        { data[j][i] = BitConverter.ToInt16(temp, 0); }

                    }
                }
            }
        }
    }
}

Needed using-directives:

using System;
using System.IO;
using System.Text;

This class reads all chunks byte per byte and sets the properties. You just have to initialize this class and it will return all properties of your selected wave-file.

In the reference you added I dont see any mention of the chunk size being repeated for each data chunk...

Try something like this:

int chunkSize = System.BitConverter.ToInt32(strm, 40);
int widx = 44; //wav data starts at the 44th byte
//strm is a byte array of the wav file
while(widx < strm.Length)
{
    if(widx < 1000)
    {
        //log "data" or "100 97 116 97" for the subchunkid
        //This is only getting printed the 1st time though. All prints after that are garbage
        Debug.Log( strm[widx] + " " + strm[widx+1] + " " + strm[widx+2] + " " + strm[widx+3]);
    }    
    widx += chunkSize;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM