简体   繁体   English

读取WAV标头会呈现错误的数据

[英]Reading WAV header renders wrong data

I am trying to read the contents of a WAV file, and to start I want to read the header to find information. 我正在尝试读取WAV文件的内容,并开始阅读标题以查找信息。 According to a lot of sources, the first 44 bytes of the file contain the information. 根据许多资料,文件的前44个字节包含信息。

I have inspected the file with an HEX editor and it contains the correct data, starting with "RIFF", then numbers, "WAVE" and all. 我已经使用HEX编辑器检查了该文件,它包含正确的数据,从“ RIFF”开始,然后是数字,“ WAVE”以及所有。

I then tried the following code to access the header values: https://gist.github.com/ranisalt/1b0dbc84ea874a04db3f 然后,我尝试使用以下代码访问标头值: https : //gist.github.com/ranisalt/1b0dbc84ea874a04db3f

Problem is this renders completely useless information, as if I was trying to read something in the middle of the file. 问题是,这将导致完全无用的信息,就像我试图读取文件中间的内容一样。 I get nonsense values, not consistent with those from the HEX editor: 我得到的废话值与来自HEX编辑器的值不符:

伊姆古尔

Please mind that even though the code is not completely correct, my problem is that the fstream read is not rendering the same bytes the file has. 请注意 ,即使代码不是完全正确的,我的问题是fstream读取的结果与文件的字节数不一样。

I also am working on an embedded platform with very little memory, so I need code with minimal overhead, that's why I am not seeking a full fledged library. 我还在一个内存很少的嵌入式平台上工作,所以我需要最小的开销的代码,这就是为什么我不寻求完整的库。

I tried seeking to the position 0 of the file before reading, but this is even worse, reading other strange values with no meaning. 我尝试在读取之前尝试查找文件的位置0,但这甚至更糟,读取其他没有意义的奇怪值。

What could possibly be happening with the file reading to render such nonsense values? 读取文件以呈现这种无意义的值可能会发生什么?

Reading bytes from a file and using those bytes directly as bytes for numbers is implementation-defined behavior . 从文件读取字节并将这些字节直接用作数字字节是实现定义的行为 The C++ standard makes no guarantee of the internal byte order of integers. C ++标准不保证整数的内部字节顺序。

To start, you'll want a function that will read little endian numbers from a file and store them as native integers in memory. 首先,您需要一个从文件中读取小尾数并将其作为本机整数存储在内存中的函数。 It might seem superfluous if you're already on a little endian machine, but there is no good reason to not read things this way, and plenty of good reasons to not blindly assume that you're running on a little endian architecture. 如果您已经在使用小端字节序的计算机上,这似乎是多余的,但是没有充分的理由不以这种方式阅读内容,并且有很多充分的理由不要盲目地假设您正在小端字节序的体系结构上运行。

Next, you will want to properly follow the spec and read the sizes and offsets to parse the information, as Remy said. 接下来,您将要正确地遵循规范,并读取大小和偏移量以解析信息,如Remy所说。 Blindly assuming that all wav files are laid out the same is a much worse assumption than you might think. 盲目假设所有wav文件的布局都一样,这比您可能想像的要糟糕得多。

As far as actually troubleshooting your issue, read the file one byte at a time and print it. 至于实际解决问题的方法,请一次读取一个字节并打印该文件。 Make sure you're actually reading the data you're expecting. 确保您实际上正在读取期望的数据。 Sometimes, odd things can happen, especially on Linux. 有时,可能会发生奇怪的事情,尤其是在Linux上。

That code is NOT EVEN CLOSE to be the correct way to read a WAV file. 该代码不是很紧密,不是读取WAV文件的正确方法。 A WAV file has structure to it, but the code is completely ignoring that structure, making inaccurate assumptions without validating them: WAV文件具有结构,但是代码完全忽略了该结构,做出了不正确的假设而不验证了它们:

  • it assumes the first subchunk inside the WAVE chunk is fmt\\0 - not always true! 假设WAVE块中的第一个子块为fmt\\0并非总是如此!
  • it assumes the size of the fmt\\0 chunk's data is exactly 16 bytes - not always true! 它假定fmt\\0块的数据大小恰好是16个字节-并非总是如此!
  • it assumes that no other chunks exist between the fmt\\0 and data chunks - not always true! 它假定在fmt\\0data块之间不存在其他块-并非总是如此!

You really should use a pre-existing library to read audio files, such as libav , but if you are going to do it manually, at least pay attention to what you are reading. 您确实应该使用一个预先存在的库来读取音频文件,例如libav ,但是如果您要手动进行操作,则至少要注意所读取的内容。 Every chunk has a header that indicates the chunk type and data size. 每个块都有一个标头,用于指示块类型和数据大小。 You MUST take those into account correctly. 您必须正确考虑这些因素。 Read the chunks in a loop, reading each header, data payload, and optional padding as needed, checking for specific chunks that you are interested in, and ignoring other chunks that you are not interested in. 读取循环中的块,根据需要读取每个标头,数据有效负载和可选的填充,检查您感兴趣的特定块,并忽略其他您不感兴趣的块。

Try something more like this: 尝试更多类似这样的方法:

#ifndef FFT_FFT_H
#define FFT_FFT_H

#include <fstream>
#include <string>

class FFT {
public:
    FFT(const std::string& filename);

private:
    std::ifstream file;
};

#endif //FFT_FFT_H

#include "fft.h"

#include <cstdint>
#include <cstring>
#include <iostream>
#include <iomanip>
#include <vector>
#include <stdexcept> 

#pragma pack(push, 1)
struct chunkHdr
{    
    char chunkID[4];
    uint32_t dataSize;
};

struct waveFmt
{
    uint16_t wFormatTag;
    uint16_t nChannels;
    uint32_t nSamplesPerSec;
    uint32_t nAvgBytesPerSec;
    uint16_t nBlockAlign;
};

struct waveFmtEx
{
    waveFmt wf;
    uint16_t wBitsPerSample;
    uint16_t cbSize;
    // cbSize number of bytes follow this struct...
};

struct pcmWaveFmt
{
    waveFmt wf;
    uint16_t wBitsPerSample;
};

union uWaveFmt
{
    waveFmt wf;
    waveFmtEx wfx;
    pcmWaveFmt pcm;
};
#endif

void readBytes(std::ifstream &f, void *buf, std::streamsize bufsize)
{
    if (!f.read(reinterpret_cast<char*>(buf), bufsize))
        throw std::runtime_error("not enough bytes in file");
}

void skipBytes(std::ifstream &f, std::streamsize bufsize)
{
    if (!f.seekg(bufsize, std::ios_base::cur))
        throw std::runtime_error("not enough bytes in file");
}

void readChunkHeader(std::ifstream &f, chunkHdr &hdr)
{
    readBytes(f, &hdr, sizeof(hdr));
    // if you are on a big-endian system, you need to swap the bytes of hdr.dataSize here...
}

FFT::FFT(const std::string& filename)
{
    file.open(filename.c_str(), std::ifstream::binary);
    if (!file.is_open())
        throw std::runtime_error("cannot open the file");

    chunkHdr hdr;
    char riffType[4];
    std::vector<uint8_t> waveFmtBuffer;
    uWaveFmt *fmt = NULL;

    readChunkHeader(file, hdr); // should be RIFF
    if( (hdr.chunkID[0] != 'R') ||
        (hdr.chunkID[1] != 'I') ||
        (hdr.chunkID[2] != 'F') ||
        (hdr.chunkID[3] != 'F') )
        throw std::runtime_error("Expected chunk 'RIFF' not detected");

    readBytes(file, riffType, 4); // should be WAVE
    if( (riffType[0] != 'W') ||
        (riffType[1] != 'A') ||
        (riffType[2] != 'V') ||
        (riffType[3] != 'E') )
        throw std::runtime_error("Expected type 'WAVE' not detected");

    while (!file.eof())
    {
        readChunkHeader(file, hdr);

        if(
            (hdr.chunkID[0] == 'f') &&
            (hdr.chunkID[1] == 'm') &&
            (hdr.chunkID[2] == 't') &&
            (hdr.chunkID[3] == '\0') )
        {
            if (fmt)
                throw std::runtime_error("Only one 'fmt' chunk is allowed");

            if (hdr.dataSize == 0)
                throw std::runtime_error("Invalid 'fmt' data size detected");

            waveFmtBuffer.resize(hdr.dataSize);
            readBytes(file, &data[0], hdr.dataSize);
            fmt = reinterpret_cast<uWaveFmt*>(&waveFmtBuffer[0]);

            if (hdr.dataSize >= sizeof(waveFmtEx))
            {
                // if you are on a big-endian system, you need to swap the bytes of the uWaveFmt->wfx fields first...

                if (fmt->wfx.wFormatTag == 1) // PCM
                    fmt->wfx.cbSize = 0; // not used in PCM

                else if (hdr.dataSize < (sizeof(waveFmtEx) + fmt->cbSize))
                    throw std::runtime_error("Invalid 'fmt' data size detected");
            }
            else if (hdr.dataSize == sizeof(waveFmt))
            {
                // if you are on a big-endian system, you need to swap the bytes of the fmt->wf fields first...

                if (fmt->wf.wFormatTag == 1) // PCM
                    throw std::runtime_error("Invalid 'fmt' data size detected"); // needed at least sizeof(pcmWaveFmt) bytes
            }
            else
                throw std::runtime_error("Invalid 'fmt' data size detected");

            // use fmt as needed...
         }

         else if(
            (hdr.chunkID[0] == 'd') &&
            (hdr.chunkID[1] == 'a') &&
            (hdr.chunkID[2] == 't') &&
            (hdr.chunkID[3] == 'a') )
        {
            if (!fmt)
                throw std::runtime_error("'fmt' chunk not detected before 'data' chunk");

            // read exactly hdr.dataSize bytes, processing audio samples
            // as needed based on the format defined in the 'fmt' chunk...

            skipBytes(file, hdr.dataSize);
        }

        // any other chunks you are interested in...

        else
        {
            // skip everything else...
            skipBytes(file, hdr.dataSize);
        }

        // skip trailing pad byte if the data size is not even...
        if ((hdr.dataSize % 2) != 0)
            skipBytes(file, 1);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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