Как правильно читать WAV-заголовок в C ++? - PullRequest
0 голосов
/ 03 февраля 2019

Прежде всего, позвольте мне сказать, что я читал другие подобные вопросы, но я не могу найти решение моей проблемы в них.

Я использую библиотеку «OpenAL» для воспроизведения файлов WAV, создав AudioBuffer, а затем AudioSource, но я думаю, что это не имеет значения.Я создал класс 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, которой я следую, такова: enter image description here

Я предполагаю, что последние 2 элемента появляются только в зависимости от значения поля «AudioFormat».Если он равен 1, эти элементы не появятся.В противном случае они могут появиться или не появиться.Для этого я сравниваю значение поля «FmtChunkSize»:

  • Если оно равно 16, то последние 2 поля точно отсутствуют.
  • Если этобольше 16, тогда я должен прочитать значение поля «ExtraParamsSize» и пропустить эти байты при чтении.

Затем я ищу строку «data».Когда я наконец нашел его, я читаю его размер (следующие 4 байта) и создаю буфер такого размера.Начиная с alGenBuffers(1, audiobuffer->buffer);, я просто создаю буферы OpenAL (не в этом моя проблема).

Отладка Я обнаружил, что значение AudioFormat равно 1 (поэтому в нем не должно быть двух последних полей), НОFmtChunkSize больше 16 (поэтому он должен иметь последние два поля ... Вид конфликта ...) Поэтому я могу подумать, что моя проблема в том, что я не принимаю во внимание порядок байтов, но если это так, я не знаюкак правильно читать значения.

WAV-файл, который я загружаю, в порядке, потому что другие знакомые мне люди успешно воспроизвели его со своим кодом.

Извините, если я не объяснил себеочень хорошо, и извините за размер вопроса, но я подумал, что вам может быть полезно узнать, за какой структурой WAV-заголовка я следовал.

Любая помощь будет оценена, большое спасибо заранее.

1 Ответ

0 голосов
/ 03 февраля 2019

WAV-файл работает немного иначе, чем вы думаете.

У вас есть куски данных, каждый из которых должен быть прочитан вместе.Все они следуют одному и тому же шаблону: 4 символа, размер 4 байта и, возможно, дополнительные данные.

Первый блок должен быть фиксированным, это тип файла, а затем размер файла (-8 байт,так что это оставшийся размер файла) и формат файла.

Затем, есть обычные (возможно) куски.У вас есть тип (4 символа), затем размер блока (-8 байт) и соответствующие данные.

В вашем случае второй блок выглядит как «fmt».Вам следует заботиться только об этом размере чанка, чтобы знать, есть ли у вас более 16 байтов информации или нет.Вот что решает вещи.

Тогда у вас есть блок данных.Тот же шаблон, 4 символа, затем размер звуковых данных и сами данные.

Но вы можете получить другие чанки, такие как «bext», поэтому вам нужно прочитать все другие чанки, а не только «данные».

Как я уже сказал, они следуют той же схеме.4 символа, 4 байта для размера и затем несколько байтов (размером size), которые прикреплены к этому чанку.Если вы будете следовать правилам, то сможете прочитать свой файл.

...