簡體   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