繁体   English   中英

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

[英]Reading WAV header renders wrong data

我正在尝试读取WAV文件的内容,并开始阅读标题以查找信息。 根据许多资料,文件的前44个字节包含信息。

我已经使用HEX编辑器检查了该文件,它包含正确的数据,从“ RIFF”开始,然后是数字,“ WAVE”以及所有。

然后,我尝试使用以下代码访问标头值: https : //gist.github.com/ranisalt/1b0dbc84ea874a04db3f

问题是,这将导致完全无用的信息,就像我试图读取文件中间的内容一样。 我得到的废话值与来自HEX编辑器的值不符:

伊姆古尔

请注意 ,即使代码不是完全正确的,我的问题是fstream读取的结果与文件的字节数不一样。

我还在一个内存很少的嵌入式平台上工作,所以我需要最小的开销的代码,这就是为什么我不寻求完整的库。

我尝试在读取之前尝试查找文件的位置0,但这甚至更糟,读取其他没有意义的奇怪值。

读取文件以呈现这种无意义的值可能会发生什么?

从文件读取字节并将这些字节直接用作数字字节是实现定义的行为 C ++标准不保证整数的内部字节顺序。

首先,您需要一个从文件中读取小尾数并将其作为本机整数存储在内存中的函数。 如果您已经在使用小端字节序的计算机上,这似乎是多余的,但是没有充分的理由不以这种方式阅读内容,并且有很多充分的理由不要盲目地假设您正在小端字节序的体系结构上运行。

接下来,您将要正确地遵循规范,并读取大小和偏移量以解析信息,如Remy所说。 盲目假设所有wav文件的布局都一样,这比您可能想像的要糟糕得多。

至于实际解决问题的方法,请一次读取一个字节并打印该文件。 确保您实际上正在读取期望的数据。 有时,可能会发生奇怪的事情,尤其是在Linux上。

该代码不是很紧密,不是读取WAV文件的正确方法。 WAV文件具有结构,但是代码完全忽略了该结构,做出了不正确的假设而不验证了它们:

  • 假设WAVE块中的第一个子块为fmt\\0并非总是如此!
  • 它假定fmt\\0块的数据大小恰好是16个字节-并非总是如此!
  • 它假定在fmt\\0data块之间不存在其他块-并非总是如此!

您确实应该使用一个预先存在的库来读取音频文件,例如libav ,但是如果您要手动进行操作,则至少要注意所读取的内容。 每个块都有一个标头,用于指示块类型和数据大小。 您必须正确考虑这些因素。 读取循环中的块,根据需要读取每个标头,数据有效负载和可选的填充,检查您感兴趣的特定块,并忽略其他您不感兴趣的块。

尝试更多类似这样的方法:

#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