簡體   English   中英

如何在C ++中正確讀取WAV標頭?

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

首先,讓我說我已經讀過另一個類似的問題,但是在這些問題中找不到解決方案。

我正在使用“ OpenAL”庫通過創建AudioBuffer和AudioSource來播放WAV文件,但是我認為這沒有關系。 我創建了一個名為AudioBuffer的類,該類具有一個靜態方法來獲取所有信息,然后返回指向在其中創建的對象的指針。 我正在嘗試做的是讀取WAV文件。 為此,我首先讀取標題以獲取每個字段的值,然后使用之前讀取的“數據大小”構建一個緩沖區並將其存儲在整個數據字段中。 問題是,當我嘗試加載WAV文件時,它將無法播放。 這是我用來加載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;
}
}

很抱歉,如果某些變量名和注釋是西班牙語,但我認為這很容易理解。

  • 首先,我打開文件通過參數獲得的功能,並打印是否成功打開。
  • 然后,我尋找“ RIFF”字符串,該字符串告訴我它是否是有效的WAV文件。
  • 之后,我讀取每個字段的值。

我要遵循的WAV標頭結構是這樣的: 在此處輸入圖片說明

我假設僅根據“ AudioFormat”字段的值顯示最后2個元素。 如果等於1,則不會顯示這些元素。 否則,它們可能會或可能不會出現。 為了知道這一點,我正在比較“ FmtChunkSize”字段值:

  • 如果等於16,則肯定不存在最后2個字段。
  • 如果大於16,則必須讀取“ ExtraParamsSize”字段的值,並在讀取時跳過這些字節。

然后,我正在尋找“數據”字符串。 當我最終找到它時,我讀取了它的大小(接下來的4個字節)並創建了該大小的緩沖區。 來自alGenBuffers(1, audiobuffer->buffer); 我只是在創建OpenAL緩沖區(這不是我的問題所在)。

調試中,我發現AudioFormat的值為1(因此它不應具有最后兩個字段),但FmtChunkSize大於16(因此它應具有最后兩個字段...有點沖突...)因此,我可能認為我的問題是我沒有考慮字節序,但是如果是這樣,我不知道如何正確讀取值。

我正在加載的WAV文件是可以的,因為我認識的其他人已經使用他們的代碼成功播放了該文件。

抱歉,如果我對自己的解釋不夠好,並且對問題的大小也感到抱歉,但我認為這對您了解我要遵循的WAV標頭結構可能會有所幫助。

任何幫助將不勝感激,非常感謝您。

WAV文件的工作方式與您認為的有所不同。

您有大量數據,每個數據必須一起讀取。 它們都遵循相同的模式:4個字符,4個字節的大小以及可能的其他數據。

第一個塊應該是固定的,即文件類型,然后是文件的大小(-8個字節,因此是文件的剩余大小)和文件格式。

然后,有通常的(可能是)塊。 您有一個類型(4個字符),然后是塊的大小(-8字節)和相關數據。

在您的情況下,第二個塊似乎是“ fmt”。 您只需要關心此塊的大小即可知道您是否擁有超過16個字節的信息。 那就是決定事情的。

然后,您獲得了“數據”塊。 相同的模式,4個字符,然后是聲音數據的大小和數據本身。

但是您可以獲得其他塊,例如“ bext”,因此您需要讀取所有其他塊,而不僅僅是“ data”。

正如我所說的,它們遵循相同的模式。 4個字符,大小為4個字節,然后附加到該塊的一些字節(大小為size )。 如果您遵守規則,那么您將能夠讀取文件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM