【问题标题】:Reading in Wav header - Not setting data size读取 Wav 标头 - 不设置数据大小
【发布时间】:2012-12-18 23:59:59
【问题描述】:

我正在尝试读取 .wav 文件的 Header 信息。

如果我有一个低采样率 (22050) 的 .wav 文件,它将完美地读取所有信息,但是,如果我有更高的采样率 (8000),则它无法读取某些信息:

“dataSize”设置在使用 22050 .wav 文件时,但是,当使用 8000 .wav 文件时,它不会被设置,只是显示一些随机数。 “1672494080”,实际大小在 4k-4.5k 左右时。

对我哪里出错有什么建议吗?

编辑:

#include <iostream>
#include <fstream>
#include <vector>
#include <inttypes.h>
#include <stdint.h>
#include <math.h>

using namespace std;
struct riff_hdr
{
   char id[4];
   uint32_t size;
   char type[4];
};

struct chunk_hdr
{
   char id[4];
   uint32_t size;
};

struct wavefmt
{
   uint16_t format_tag;
   uint16_t channels;
   uint32_t sample_rate;
   uint32_t avg_bytes_sec;
   uint16_t block_align;
   uint16_t bits_per_sample;
   uint16_t extra_size;
};


riff_hdr riff;
chunk_hdr chunk;
wavefmt fmt = {0};
uint32_t padded_size;
vector<uint8_t> chunk_data;

bool readHeader(ifstream &file) {

file.read(reinterpret_cast<char*>(&riff), sizeof(riff));
if (memcmp(riff.id, "RIFF", 4) == 0)
{
    cout << "size=" << riff.size << endl;
    cout << "id=" << string(riff.type, 4) << endl;

    if (memcmp(riff.type, "WAVE", 4) == 0)
    {
        // chunks can be in any order!
        // there is no guarantee that "fmt" is the first chunk.
        // there is no guarantee that "fmt" is immediately followed by "data".
        // There can be other chunks present!

        do {

            file.read(reinterpret_cast<char*>(&chunk), sizeof(chunk));
            padded_size = ((chunk.size + 2 - 1) & ~1);

             cout << "id=" << string(chunk.id, 4) << endl;
             cout << "size=" << chunk.size << endl;
             cout << "padded size=" << padded_size << endl;

             if (memcmp(chunk.id, "fmt\0", 4) == 0) 
             {
                 if (chunk.size < sizeof(wavefmt))
                 {
                     // error!
                     file.ignore(padded_size);
                 }else{

                    // THIS block doesn't seem to be executing
                    chunk_data.resize(padded_size);
                    file.read(reinterpret_cast<char*>(&chunk_data[0]), padded_size);

                    fmt = *(reinterpret_cast<wavefmt*>(&chunk_data[0]));

                    cout << "format_tag=" << fmt.format_tag << endl;
                    cout << "channels=" << fmt.channels << endl;
                    cout << "sample_rate=" << fmt.sample_rate << endl;
                    cout << "avg_bytes_sec=" << fmt.avg_bytes_sec << endl;
                    cout << "block_align=" << fmt.block_align << endl;
                    cout << "bits_per_sample=" << fmt.bits_per_sample << endl;
                    cout << "extra_size=" << fmt.extra_size << endl;
                }

                if(fmt.format_tag != 1)
                {
                    uint8_t *extra_data = &chunk_data[sizeof(wavefmt)];
                }
             }else if(memcmp(chunk.id, "data", 4) == 0) {
                file.ignore(padded_size);
            }else{
                file.ignore(padded_size);
            }
        }while ((!file) && (!file.eof()));
    }
}

    return true;
}

int main()
{
ifstream file("example2.wav");


readHeader(file);
return 0;
}

输出:

大小=41398

id=WAVE

id=fmt

尺寸=18

填充大小=18

chunk_data 大小=0

我哪里错了?

【问题讨论】:

    标签: c++ audio wav


    【解决方案1】:

    您的代码有两个问题:

    1. bitsPerSample 值后面有一个 2 字节整数,您没有读取。它指定该块中任何额外数据的大小。如果format2 的值仅表示PCM 格式,则可以忽略整数的值(无论如何它通常为0,但也可能是垃圾),但您仍然需要考虑它的存在。对于非 PCM 格式,整数不能忽略,您必须读取该值,然后读取它表示的字节数。您需要确保在进入 while 循环之前读取整个块,否则您将无法在文件中的正确起始位置读取更多块。

    2. 您没有考虑到块被填充到最近的 WORD 边界,但块大小不包括任何填充。调用seekg()时,需要将值向上舍入到下一个WORD边界。

    更新:根据您发布的新代码,它应该看起来更像这样:

    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <inttypes.h>
    #include <stdint.h>
    #include <math.h>
    
    using namespace std;
    
    // if your compiler does not have pshpack1.h and poppack.h, then
    // use #pragma pack instead. It is important that these structures
    // be byte-alignd!
    
    #include <pshpack1.h>
    
    struct s_riff_hdr
    {
       char id[4];
       uint32_t size;
       char type[4];
    };
    
    struct s_chunk_hdr
    {
       char id[4];
       uint32_t size;
    };
    
    struct s_wavefmt
    {
       uint16_t format_tag;
       uint16_t channels;
       uint32_t sample_rate;
       uint32_t avg_bytes_sec;
       uint16_t block_align;
    };
    
    struct s_wavefmtex
    {
       s_wavefmt fmt;
       uint16_t bits_per_sample;
       uint16_t extra_size;
    };
    
    struct s_pcmwavefmt
    {
       s_wavefmt fmt;
       uint16_t bits_per_sample;
    };
    
    #include <poppack.h>
    
    bool readWave(ifstream &file)
    {
        s_riff_hdr riff_hdr;
        s_chunk_hdr chunk_hdr;
        uint32_t padded_size;
        vector<uint8_t> fmt_data;
        s_wavefmt *fmt = NULL;
    
        file.read(reinterpret_cast<char*>(&riff_hdr), sizeof(riff_hdr));
        if (!file) return false;
    
        if (memcmp(riff_hdr.id, "RIFF", 4) != 0) return false;
    
        cout << "size=" << riff_hdr.size << endl;
        cout << "type=" << string(riff_hdr.type, 4) << endl;
    
        if (memcmp(riff_hdr.type, "WAVE", 4) != 0) return false;
    
        // chunks can be in any order!
        // there is no guarantee that "fmt" is the first chunk.
        // there is no guarantee that "fmt" is immediately followed by "data".
        // There can be other chunks present!
    
        do
        {
            file.read(reinterpret_cast<char*>(&chunk_hdr), sizeof(chunk_hdr));
            if (!file) return false;
    
            padded_size = ((chunk_hdr.size + 1) & ~1);
    
            cout << "id=" << string(chunk_hdr.id, 4) << endl;
            cout << "size=" << chunk_hdr.size << endl;
            cout << "padded size=" << padded_size << endl;
    
            if (memcmp(chunk_hdr.id, "fmt ", 4) == 0) 
            {
                if (chunk_hdr.size < sizeof(s_wavefmt)) return false;
    
                fmt_data.resize(padded_size);
    
                file.read(reinterpret_cast<char*>(&fmt_data[0]), padded_size);
                if (!file) return false;
    
                fmt = reinterpret_cast<s_wavefmt*>(&fmt_data[0]);
    
                cout << "format_tag=" << fmt->format_tag << endl;
                cout << "channels=" << fmt->channels << endl;
                cout << "sample_rate=" << fmt->sample_rate << endl;
                cout << "avg_bytes_sec=" << fmt->avg_bytes_sec << endl;
                cout << "block_align=" << fmt->block_align << endl;
    
                if (fmt->format_tag == 1) // PCM
                {
                    if (chunk_hdr.size < sizeof(s_pcmwavefmt)) return false;
    
                    s_pcmwavefmt *pcm_fmt = reinterpret_cast<s_pcmwavefmt*>(fmt);
    
                    cout << "bits_per_sample=" << pcm_fmt->bits_per_sample << endl;
                }
                else
                {
                    if (chunk_hdr.size < sizeof(s_wavefmtex)) return false;
    
                    s_wavefmtex *fmt_ex = reinterpret_cast<s_wavefmtex*>(fmt);
    
                    cout << "bits_per_sample=" << fmt_ex->bits_per_sample << endl;
                    cout << "extra_size=" << fmt_ex->extra_size << endl;
    
                    if (fmt_ex->extra_size != 0)
                    {
                        if (chunk_hdr.size < (sizeof(s_wavefmtex) + fmt_ex->extra_size)) return false;
    
                        uint8_t *extra_data = reinterpret_cast<uint8_t*>(fmt_ex + 1);
                        // use extra_data, up to extra_size bytes, as needed...
                    }
                }
            }
            else if (memcmp(chunk_hdr.id, "data", 4) == 0)
            {
                // process chunk data, according to fmt, as needed...
    
                file.ignore(padded_size);
                if (!file) return false;
            }
            else
            {
                // process other chunks as needed...
    
                file.ignore(padded_size);
                if (!file) return false;
            }
        }
        while (!file.eof());
    
        return true;
    }
    
    int main()
    {
        ifstream file("example2.wav");
        readWave(file);
        return 0;
    }
    

    【讨论】:

    • 嘿,谢谢,我会试试的!但为什么它适用于某些文件而不适用于其他文件?
    • 你走运了。您确实应该使用音频库来处理此类数据。在 Windows 上,您可以使用 mmioOpen()mmioDescend()mmioRead() 等。如果您想要跨平台的东西,请查看 libav
    • 感谢您的回复。我不允许使用库,但我正在遵循 C 中的一些代码.. 有趣!这是一项艰巨的任务吗?我发现它只适用于较小的(我认为是 PCM)wav 文件,而不适用于更高的 hz ......有什么想法吗?谢谢你。呃尝试了你的建议
    • 嘿,我已经更新了我的代码 :) 希望你能帮助我,谢谢。
    • 您误解了fread() 的参数如何工作,因此您的C++ 代码没有正确读取大部分字段。例如,fread(&amp;size, sizeof(DWORD), 1, fp) 读取 4 个字节(DWORD 大小的 1 个元素),但您对应的 file.read() 调用只读取 1 个字节而不是 4 个。同样,fread(id, sizeof(BYTE), 4, fp) 读取 4 个字节(BYTE 的 4 个元素大小),您对应的file.read() 正在读取 4 个字节。所以你只看fread() 的第三个参数并忽略它乘以第二个参数来得到实际的字节数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多