简体   繁体   English

如何在C ++中正确读取WAV标头?

[英]How to properly read a WAV header in C++?

First of all, let me say that I have been reading another similar questions but I can´t find the solution for my problem in them. 首先,让我说我已经读过另一个类似的问题,但是在这些问题中找不到解决方案。

I'm using the "OpenAL" library to play WAV files by creating an AudioBuffer and then an AudioSource, but I think this does not matter. 我正在使用“ OpenAL”库通过创建AudioBuffer和AudioSource来播放WAV文件,但是我认为这没有关系。 I created a class called AudioBuffer which has a static method to get all the info and then return a pointer to a object created inside it. 我创建了一个名为AudioBuffer的类,该类具有一个静态方法来获取所有信息,然后返回指向在其中创建的对象的指针。 What I'm trying to do is to read a WAV file. 我正在尝试做的是读取WAV文件。 To do so, I first read the header to get the values of each field and then build a buffer with the "data size" I read before and store in it the whole data field. 为此,我首先读取标题以获取每个字段的值,然后使用之前读取的“数据大小”构建一个缓冲区并将其存储在整个数据字段中。 The problem is that when I try to load WAV file, it just won't play. 问题是,当我尝试加载WAV文件时,它将无法播放。 Here is the function I use to both load a WAV file and read its fields: 这是我用来加载WAV文件并读取其字段的函数:

typedef struct {
char chunk_id[4];
uint32_t chunk_size;
char format[4];
} wave_header;

typedef struct {
char id[4];
uint32_t size;
} riff_chunk_header;

typedef struct {
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
} wave_fmt_chunk;

AudioBuffer* AudioBuffer::load(const char* filename) {

wave_header w_header;
riff_chunk_header r_c_header;
wave_fmt_chunk w_f_chunk;
short extra_params_size = 0;
bool data = false;
char bloque[1];
int data_size = 0;

AudioBuffer *audiobuffer = new AudioBuffer(1);

std::ifstream in(filename, std::ios::binary);

if (in.is_open()) {

    printf("Fichero abierto correctamente.\n");

    in.read(w_header.chunk_id, 4);

    if (strncmp(w_header.chunk_id, "RIFF", 4) != 0) {
        printf("El fichero no es de tipo WAV.\n");
        return nullptr;
    }
    else {
        printf("Fichero WAV valido.\n");
    }

    in.read(reinterpret_cast<char *>(&w_header.chunk_size), 4);
    in.read(w_header.format, 4);

    in.read(r_c_header.id, 4);
    in.read(reinterpret_cast<char *>(&r_c_header.size), 4); //FmtChunkSize

    in.read(reinterpret_cast<char *>(&w_f_chunk.audio_format), 2);
    in.read(reinterpret_cast<char *>(&w_f_chunk.num_channels), 2);
    in.read(reinterpret_cast<char *>(&w_f_chunk.sample_rate), 4);
    in.read(reinterpret_cast<char *>(&w_f_chunk.byte_rate), 4);
    in.read(reinterpret_cast<char *>(&w_f_chunk.block_align), 2);
    in.read(reinterpret_cast<char *>(&w_f_chunk.bits_per_sample), 2);

    if (r_c_header.size > 16) {
        in.read(reinterpret_cast<char *>(&extra_params_size), 2);
        in.ignore(extra_params_size); //Ignoramos los bytes de parámetros adicionales.
    }

    while (!data) {
        in.read(bloque, 1);
        if (bloque[0] == 'd') {
            in.read(bloque, 1);
            if (bloque[0] == 'a') {
                in.read(bloque, 1);
                if (bloque[0] == 't') {
                    in.read(bloque, 1);
                    if (bloque[0] == 'a')
                        data = true; //Se ha encontrado "data".
                }
            }
        }

    }

    //Una vez encontrado "data"
    in.read(reinterpret_cast<char *>(&data_size), 4); //Leemos el tamaño del bloque data.

    char *m_data = new char[data_size]; //Buffer con el tamaño de los datos.
    in.read(m_data, data_size); //Rellenamos el buffer con los datos.

    //Generamos el buffer de OpenAL.
    alGenBuffers(1, audiobuffer->buffer);

    if (w_f_chunk.bits_per_sample == 8) {
        if (w_f_chunk.num_channels == 1) {
            alBufferData(audiobuffer->buffer[0], AL_FORMAT_MONO8, m_data, data_size, w_f_chunk.sample_rate);
        }
        else {
            alBufferData(audiobuffer->buffer[0], AL_FORMAT_STEREO8, m_data, data_size, w_f_chunk.sample_rate);
        }
    }
    else if (w_f_chunk.bits_per_sample == 16) {
        if (w_f_chunk.num_channels == 1) {
            alBufferData(audiobuffer->buffer[0], AL_FORMAT_MONO16, m_data, data_size, w_f_chunk.sample_rate);
        }
        else {
            alBufferData(audiobuffer->buffer[0], AL_FORMAT_STEREO16, m_data, data_size, w_f_chunk.sample_rate);
        }
    }

    return audiobuffer;
}
else {
    printf("El fichero no se pudo abrir. Ruta incorrecta.\n");
    return nullptr;
}
}

Sorry if some variable names and comments are in Spanish, but I think it's easy to understand. 很抱歉,如果某些变量名和注释是西班牙语,但我认为这很容易理解。

  • First I open the file the function got by parameter and print if it was successfully opened. 首先,我打开文件通过参数获得的功能,并打印是否成功打开。
  • Then I look for the "RIFF" string which tells me if it is a valid WAV file. 然后,我寻找“ RIFF”字符串,该字符串告诉我它是否是有效的WAV文件。
  • After that, I read the values for each field. 之后,我读取每个字段的值。

The WAV header structure I'm following is this: 我要遵循的WAV标头结构是这样的: 在此处输入图片说明

I'm assuming the last 2 elements only appear depending on the "AudioFormat" field value. 我假设仅根据“ AudioFormat”字段的值显示最后2个元素。 If it is equal to 1, those elements won't appear. 如果等于1,则不会显示这些元素。 Otherwise, they may or may not appear. 否则,它们可能会或可能不会出现。 For knowing this, I'm comparing the "FmtChunkSize"field value: 为了知道这一点,我正在比较“ FmtChunkSize”字段值:

  • If it is equal to 16, then the last 2 fields are definitely not present. 如果等于16,则肯定不存在最后2个字段。
  • If it is greater than 16, then I have to read the value of "ExtraParamsSize" field and skip those bytes when reading. 如果大于16,则必须读取“ ExtraParamsSize”字段的值,并在读取时跳过这些字节。

Then, I'm looking for the "data" string. 然后,我正在寻找“数据”字符串。 When I have finally found it, I read its size (the next 4 bytes) and create a buffer of that size. 当我最终找到它时,我读取了它的大小(接下来的4个字节)并创建了该大小的缓冲区。 From alGenBuffers(1, audiobuffer->buffer); 来自alGenBuffers(1, audiobuffer->buffer); on I'm just creating OpenAL buffers (this is not where my problem is). 我只是在创建OpenAL缓冲区(这不是我的问题所在)。

Debugging I found that the AudioFormat value is 1 (so it shouldn't have the last two fields) BUT FmtChunkSize is greater than 16 (so it should have the last two fields... Kind of conflicting...) Therefore, I might think that my problem is that I'm not taking into account the endianness, but if so I don0t know how to read the values properly then. 调试中,我发现AudioFormat的值为1(因此它不应具有最后两个字段),但FmtChunkSize大于16(因此它应具有最后两个字段...有点冲突...)因此,我可能认为我的问题是我没有考虑字节序,但是如果是这样,我不知道如何正确读取值。

The WAV file I'm loading is OK because other people I know have successfully played it with their code. 我正在加载的WAV文件是可以的,因为我认识的其他人已经使用他们的代码成功播放了该文件。

Sorry If i haven't explained myself very well, and sorry for the question size too, but I thought it might be helpful for you to know which WAV header structure I was following. 抱歉,如果我对自己的解释不够好,并且对问题的大小也感到抱歉,但我认为这对您了解我要遵循的WAV标头结构可能会有所帮助。

Any help would be appreciated, thank you very much in advance. 任何帮助将不胜感激,非常感谢您。

A wav file works a little differently than what you think. WAV文件的工作方式与您认为的有所不同。

You have chunks of data, each must be read together. 您有大量数据,每个数据必须一起读取。 They all follow the same pattern: 4 characters, a size on 4 bytes and possibly additional data. 它们都遵循相同的模式:4个字符,4个字节的大小以及可能的其他数据。

The first block should be fixed, that's the file type, with then the size of the file (-8 bytes, so that's the remaining size of the file) and the file format. 第一个块应该是固定的,即文件类型,然后是文件的大小(-8个字节,因此是文件的剩余大小)和文件格式。

Then, there are the usual (possibly) chunks. 然后,有通常的(可能是)块。 You have a type (4 characters), then the size of the chunk (-8 bytes) and the relevant data. 您有一个类型(4个字符),然后是块的大小(-8字节)和相关数据。

In your case, the second chunk seems to be "fmt". 在您的情况下,第二个块似乎是“ fmt”。 You should only care about this chunk size to know if you have more than 16 bytes of information or not. 您只需要关心此块的大小即可知道您是否拥有超过16个字节的信息。 That's what decides things. 那就是决定事情的。

Then you have the "data" chunk. 然后,您获得了“数据”块。 Same pattern, 4 characters, then the size of the sound data and the data itself. 相同的模式,4个字符,然后是声音数据的大小和数据本身。

But you could get other chunks, like "bext", so you need to read all other chunks, not just "data". 但是您可以获得其他块,例如“ bext”,因此您需要读取所有其他块,而不仅仅是“ data”。

As I've said, they follow the same pattern. 正如我所说的,它们遵循相同的模式。 4 characters, 4 bytes for size and then some bytes (of size size ) that are attached to that chunk. 4个字符,大小为4个字节,然后附加到该块的一些字节(大小为size )。 If you follow the rules, then you will be able to read your file. 如果您遵守规则,那么您将能够读取文件。

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

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