Почему у секции "fmt " размер не равен 16 байтам? Печать
Добавил(а) microsin   

Обычно у WAV-файла со стандартным заголовком размер секции "fmt " составляет 16 байт, тогда у этой секции структура традиционная:

typedef __packed struct _TWavFmtGeneric
{
   uint16_t compression;      // 2 байта, формат звуковых данных. Определяет
                              // наличие кодека или его отсутствие.
   uint16_t channels;         // 2 байта, количество каналов
   uint32_t samplerate;       // 4 байта, количество выборок в секунду
   uint32_t bytespersecond;   // 4 байта, количество байт в секунду.
   uint16_t blockalign;       // 2 байта, выравнивание блоков данных в файле
   uint16_t bitspersample;    // 2 байта, разрядность одной выборки.
}TWavFmtGeneric;

Однако из этого правила есть исключения, о чем подробнее рассказывается в этой статье.

Основное правило - при обработке WAV-файла нужно проверять размер всех секций. Находящиеся в общем пользовании файлы формата WAV могут неожиданно содержать расширенные секции с традиционными идентификаторами. Например, файлы WAV и AIFF пакета Pro Tools имеют больше секций расширения, которые нигде не задокументированы, а также содержат дополнительную информацию после фактических данных звука. Если Вы хотите успешно добраться до данных звука и определить их начало и конец, то на самом деле нужно искать секцию с идентификатором "data" и прочитать её размер (для файлов AIFF соответствующим идентификатором для информации о данных звука будет "SSND").

Правило 1: у дескриптора секции WAV-файла формат всегда одинаковый. Хорошая новость: несмотря на некоторую путаницу с идентификаторами и их следованием в теле WAV-файла, блок идентификации секций (дескриптор секции) имеет всегда один и тот же формат:

typedef struct _TRIFFsection
{
   char id[4];                // 4 байта, текстовый идентификатор секции
   uint32_t len;              // 4 байта, длина секции
}TRIFFsection;

По этой причине процесс обработки файла значительно упрощается. Все, что требуется, это просто прочитать блоки идентификации секции (назовем его дескриптор секции), и если он нас не интересует, просто переносим позицию чтения в файле на следующий дескриптор. Следующий простой код на Java демонстрирует этот процесс (код просто выведет название всех секций WAV-файла).

// Здесь функция 'in.read(...)' вернет -1, когда был достигнут
// конец файла, так что проверка 'if (in.read(...) < 0)'
// покажет окончание файла.
public static void printWaveDescriptors(File file)
throws IOException
{
   try (FileInputStream in = new FileInputStream(file))
   {
      byte[] bytes = new byte[4];
      // Прочитаем первые 4 байта, там должен быть
      // дескриптор RIFF:
      if (in.read(bytes) < 0)
      {
         return;
      }
      printDescriptor(bytes);
      // У первой секции всегда размер 12, из которых 4
      // уже были прочитаны, так что пропустим еще 8 байт:
      in.skip(8);
      for (;;)
      {
         // Цикл с чтением и проверкой каждого дескриптора секции.
         if (in.read(bytes) < 0)
         {
            break;
         }
         printDescriptor(bytes);
         // Прочитаем длину секции.
         if (in.read(bytes) < 0)
         {
            break;
         }
         // Пропуск в файле длины этой секции. Последующие байты
         // должны быть либо другим дескриптором, либо завершением
         // файла (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("Конец файла.");
   }
}

Например, могут встретиться следующие секции в произвольном WAV-файле (идентификаторы имеют размер 4 байта):

RIFF
bext
fmt 
minf
elm1
data
regn
ovwf
umid

Следует отметить, что секция и "fmt ", и секция "data" могут совершенно законно находиться в любом месте WAV-файла. Месторасположение этих секций в файле, как и других секций, спецификацией Microsoft RIFF жестко не определено - теоретически они могут следовать друг за другом в любом порядке.

[Размер секции "fmt "]

Итак, с секциями wav-файла разобрались, и стоит иметь в виду еще 3 важных правила, касающихся появления в файле информации о формате (секция "fmt ") и самих звуковых данных (секция "data").

Правило 2: секция "fmt " всегда идет перед секцией "data", это выполняется всегда. Однако перед секцией "data" и после неё могут быть и любые другие секции - в том числе секции не определенные ни в каком стандарте (это даже дает Вам возможность добавлять в WAV-файл свои собственные секции). Совместите правило 1 и правило 2, и с их помощью легко обработаете любой WAV-файл.

Правило 3: у секции "fmt " размер не всегда 16 байт. Существуют вариации 16 байт и 18 байт, однако стандарт не ограничивает размер этой секции, он может быть и больше 18 байт. Если в поле размера дескриптора "fmt " указано больше 16 байт, то байты 17 и 18 также указывают, сколько здесь имеется дополнительных байт. Если оба байта 17 и 18 равны 0, то это просто секция "fmt " с размером 18, идентичная секции "fmt " с размером 16, не более того, и на эти два байта не стоит обращать внимания (их надо просто пропустить). Т. е. совершенно безопасно прочитать предыдущие данные формата точно так же, как если бы это была секция "fmt " стандартного размера 16.

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

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

[Ссылки]

1. How can I detect whether a WAV file has a 44 or 46-byte header? site:stackoverflow.com.
2. Wave File Format - формат звукового файла WAV.
3. Audio File Format Specifications site:ece.mcgill.ca.