Вопрос по audio, wav – Как я могу определить, имеет ли WAV-файл 44- или 46-байтовый заголовок?

15

мы обнаружили, что опасно предполагать, что все аудиофайлы PCM wav имеют 44 байта данных заголовка до начала семплов. Хотя это часто встречается, многие приложения (например, ffmpeg) будут генерировать wavs с 46-байтовым заголовком, и игнорирование этого факта во время обработки приведет к повреждению и нечитаемости файла. Но как определить, насколько длинным является заголовок?

Очевидно, что есть способ сделать это, но я искал и нашел небольшое обсуждение по этому поводу. Множество аудиопроектов предполагают 44 (или, наоборот, 46) в зависимости от контекста авторов.

У меня есть много файлов WAV, где данные начинаются где-то еще целиком: может быть, сотни байтов с начала файла, кто знает? Заголовки блоков WAV на самом деле легко анализируются, у вас нет оправдания тому, что они не анализируются. Dietrich Epp
мы всегда находили, чтоФормат звукового файла WAVE PCM страница на веб-сайте Центра компьютерных исследований в области музыки и акустики (Standford), чтобы быть полезным ресурсом для такого рода вещей. Sheridan
Это'Правда, нет никакого оправдания для синтаксического анализа заголовка, но есть много дезинформации по этому поводу. Ищи "wav parser " в Google, и многие из топ-хитов содержат код, который имеет длину 44 байта без обсуждения. ТАК содержал только намеки на что-то большее. Я'Я пытаюсь привлечь внимание к этой проблеме для следующего разочарованного человека, который идет смотреть. Matt J.

Ваш Ответ

3   ответа
25

чтобы увидеть, каковы фактические размеры. Файлы Broadcast Wave Format будут содержать еще больший подчанк расширения. Файлы WAV и AIFF от Pro Tools имеют еще больше расширений, которые не документированы, а также данные после аудио. Если вы хотите быть уверенными в том, где начинается и заканчивается пример данных, вам нужно по-настоящему искать блок данных ('данные' для файлов WAV иSSND» для AIFF).

Как обзор, все подэмплы WAV соответствуют следующему формату:

Subchunk Descriptor (4 bytes)
    Subchunk Size (4 byte integer, little endian)
    Subchunk Data (size is Subchunk Size)

Это очень легко обрабатывать. Все, что вам нужно сделать, это прочитать дескриптор, если онне тот, который вы ищете, прочитайте размер данных и переходите к следующему. Простая подпрограмма Java, которая делает это, будет выглядеть так:

//
// Quick note for people who don't know Java well:
// 'in.read(...)' returns -1 when the stream reaches
// the end of the file, so 'if (in.read(...) < 0)'
// is checking for the end of file.
//
public static void printWaveDescriptors(File file)
        throws IOException {
    try (FileInputStream in = new FileInputStream(file)) {
        byte[] bytes = new byte[4];

        // Read first 4 bytes.
        // (Should be RIFF descriptor.)
        if (in.read(bytes) < 0) {
            return;
        }

        printDescriptor(bytes);

        // First subchunk will always be at byte 12.
        // (There is no other dependable constant.)
        in.skip(8);

        for (;;) {
            // Read each chunk descriptor.
            if (in.read(bytes) < 0) {
                break;
            }

            printDescriptor(bytes);

            // Read chunk length.
            if (in.read(bytes) < 0) {
                break;
            }

            // Skip the length of this chunk.
            // Next bytes should be another descriptor or EOF.
            int length = (
                  Byte.toUnsignedInt(bytes[0])
                | Byte.toUnsignedInt(bytes[1]) << 8
                | Byte.toUnsignedInt(bytes[2]) << 16
                | Byte.toUnsignedInt(bytes[3]) << 24
            );
            in.skip(Integer.toUnsignedLong(length));
        }

        System.out.println("End of file.");
    }
}

private static void printDescriptor(byte[] bytes)
        throws IOException {
    String desc = new String(bytes, "US-ASCII");
    System.out.println("Found '" + desc + "' descriptor.");
}

Например, вот случайный WAV-файл, который у меня был:

Found 'RIFF' descriptor.
Found 'bext' descriptor.
Found 'fmt ' descriptor.
Found 'minf' descriptor.
Found 'elm1' descriptor.
Found 'data' descriptor.
Found 'regn' descriptor.
Found 'ovwf' descriptor.
Found 'umid' descriptor.
End of file.

Примечательно, что здесь обафмт а также 'данные' законно появляются между другими кусками, потому чтоMicrosoft»спецификация RIFF говорит, что фрагменты могут появляться в любом порядке. Даже некоторые крупные аудиосистемы, о которых я знаю, ошибаются и нене стоит это учитывать.

Поэтому, если вы хотите найти определенный фрагмент, просматривайте файл, проверяя каждый дескриптор, пока не найдете тот, который вам нужен.ищу.

Большое спасибо! Roman M
Radiodef, спасибо за ваш комментарий! Я никогда раньше не использовал сдвиг битов и могунайти примеры практического использования в сети. Не могли бы вы объяснить, что делает это выражение, почему здесь используется сдвиг битов? <код> (байты [0] & 0xFF) | (байты [1] & 0xFF) << 8 | (байты [2] & 0xFF) << 16 | (байты [3] & 0xFF) << 24 </ Код>  Спасибо заранее! Roman M
@RomanM Преобразует 4 байта в 32-разрядное целое число. Например, 32-разрядное целое число00000000000000001000000010000001 (десятичное 32897) может быть разделен на четыре байта,,000000000000000010000000 а также10000001, Код со сдвигом битов берет байты и создает из них 32-разрядное целое число, сдвигая каждый байт на место, а затем комбинируя их с побитовым ИЛИ.& 0xFF часть специфична для Java, иобъяснил здесь. Radiodef
10

чтобы посмотреть наSubchunk1Size», которое является 4-байтовым целым числом, начинающимся с байта 16 заголовка. В обычном 44-байтовом wav это целое число будет 16 [10, 0, 0, 0]. Если оно'Для 46-байтового заголовка это целое число будет 18 [12, 0, 0, 0] или, возможно, даже выше, если есть дополнительные расширяемые метаданные (редко?).

Дополнительные данные (если они есть) начинаются с байта 36.

Таким образом, простая программа на C # для определения длины заголовка будет выглядеть так:

static void Main(string[] args)
{
    byte[] bytes = new byte[4];
    FileStream fileStream = new FileStream(args[0], FileMode.Open, FileAccess.Read);
    fileStream.Seek(16, 0);
    fileStream.Read(bytes, 0, 4);
    fileStream.Close();
    int Subchunk1Size = BitConverter.ToInt32(bytes, 0);

    if (Subchunk1Size < 16)
        Console.WriteLine("This is not a valid wav file");
    else
        switch (Subchunk1Size)
        {
            case 16:
                Console.WriteLine("44-byte header");
                break;
            case 18:
                Console.WriteLine("46-byte header");
                break;
            default:
                Console.WriteLine("Header contains extra data and is larger than 46 bytes");
                break;
        }
}
Это смутило меня .... спасибо kaitian521
2

отличный ответ, яЯ хотел бы добавить 3 вещи, которые неЭто очевидно.

Единственным правилом для файлов WAV является то, что блок FMT предшествует блоку данных. Кроме того, вы найдете куски, которые вы нене знаю об этом в начале, до блока данных и после него. Вы должны прочитать заголовок для каждого чанка, чтобы перейти к следующему чанку.

Блок FMT обычно встречается в 16-байтовых и 18-байтовых вариациях, но на самом деле спецификация также позволяет использовать более 18 байтов. Если кусок FMT ' поле размера заголовка говорит, что больше 16, байты 17 и 18 также указывают, сколько дополнительных байтов есть, поэтому, если они оба равны нулю, вы получите 18-байтовый блок FMT, идентичный 16-байтовому. Безопасно читать только первые 16 байтов блока FMT и анализировать их, игнорируя больше. Почему это важно? - немного больше, но Windows XP 's Media Player мог воспроизводить 16-битные WAV-файлы, но 24-битные WAV-файлы, только если фрагмент FMT был расширенной (18+ байтной) версией. Раньше было много жалоб, которые "Windows невоспроизводить мои 24-битные файлы WAV ", но если бы у него был 18-байтовый блок FMT, это было бы ... Microsoft исправила это когда-то в первые дни Windows 7, поэтому 24-битные с 16-байтовыми файлами FMT теперь работают нормально.

(Недавно добавлено) Размеры чанков с нечетными размерами встречаются довольно часто. В основном видно, когда создается 24-битный монофайл. Из спецификации неясно, но размер порции указывает фактическую длину данных (нечетное значение), и байт pad (ноль) добавляется после порции и перед началом следующего порции. Таким образом, чанки всегда начинаются с четных границ, но сам размер чанка сохраняется как фактическое нечетное значение.

Похожие вопросы