Формат WAVE файла
🕛 25.04.2007, 17:55
Приведенной информации вполне достаточно для работы с pcm waveфайлами (8/16 бит, моно/стерео)
wave файлы являются подмножеством файлов riff формата (resource
interchange file format), разработанного для хранения ресурсов
мультимедиа. Об этом формате надо знать совсем немного. Основной
элемент riff файла - т.н. чанк (chunk), имеющий структуру
typedef struct
{
dword ckid; // Идентификатор чанка, служит для опознания чанка
dword cksize; // Размер чанка (без ckid & cdsize) в байтах
byte ckdata[cksize];// Данные
} ck;
Основные типы чанков имеют идентификаторы "riff" и "list" и могут состоять
из вложенных чанков (субчанков).
Мы рассмотрим наиболее простой случай wave файла, состоящего из одного
лишь riff-чанка, содержащего wave-форму (wave-form). Честно говоря, я ни разу
не видел wave файла, содержащего более одного wave-чанка, поэтому мы
рассмотрим именно файл с одним-единственным wave-чанком.
wave-форма
-
wave-форма наиболее простой категории - pcm (см. ниже) имеет следующий вид:
= 'wave' + + , где
'wave' - просто сигнатура wave-формы
- чанк с информацией о звуковом сигнале
- чанк с собственно сигналом
= 'fmt ' + + format> + , где
'fmt ' - сигнатура fmt-чанка
- его размер
format> - структура waveformat(mmsystem.h), описанная ниже
- структура с дополнительной информацией о формате,
имеет переменную длину и зависит от
wformatcategory (см. ниже). В случае с pcm удобно
пользоваться структурой waveformatex (mmsystem.h),
объединяющей в себе waveformat и два поля из fmt-specific.
Документация по win32 sdk утверждает, что waveformatex
будет работать для ВСЕХ не-pcm форматов, что идет вразрез
с утверждением mm programmer`s reference о переменной
длине fmt-specific. Так что вопрос с не-pcm форматами
мне пока не ясен.
= 'data' + + <собственно сигнал> , где
'data' - сигнатура data-чанка
- его размер
<собственно сигнал> - последовательность байт, описывающая сигнал
(см. Формат данных pcm)
waveformat
-
Структура waveformat имеет вид:
typedef struct
{
word wformattag; // Категория формата
word nchannels; // Число каналов
dword nsamplespersec; // Частота дискретизации
dword navgbytespersec; // Байт в секунду
word nblockalign; // Выравнивание данных в data-чанке
} waveformat;
Рассмотрим эту структуру подробнее.
wformattag Категория формата (неудачный перевод: калька format category).
От этого значения зависят значения остальных полей этой
структуры, структура и data-чанка. Существует
несколько категорий формата; самая доступная - pcm (pulse
code modulation) имеет wformattag = 1.
nchannels 1 - моно, 2-стерео, о большем числе каналов документация
умалчивает.
nsamplespersec Частота дискретизации (число сэмплов в секунду).
navgbytespersec Среднее число байт в секунду, используется для эффективной
буферизации. Для pcm вычисляется по формуле:
(nchannels*nsamplespersec*nbitspersample)/8.
nblockalign Выравнивание данных в data-чанке. Для pcm вычисляется
по формуле:
(nchannels*nbitspersample)/8.
- Для категории pcm эта структура имеет одно значащее поле
uint nbitspersample, которое поведает нам о разрядности
дискретизации (см. wformattag & Формат данных pcm). Если,
например,nbitspersample = 12 , то сэмпл хранится в старших
12 битах слова, а младшие 4 - нули. Следом идет поле
word cbsize, используемое не-pcm форматом ( так, формат
adpcm, например, хранит здесь некий коэффициент,
необходимый для кодирования/декодирования сигнала). Для
pcm-формата это поле может отсутствовать.
Формат данных pcm
Здесь описана схема размещения данных в data-чанке wave файла.
В моно wave файле сэмплы расположены последовательно один за другим:
sample[0],sample[1],sample[2]...
В стерео wave файле сэмплы идут попарно:
left[0],right[0],left[1],right[1],left[2]...
Для более, чем двухканального сигнала (квадрозвук?!?) последовательность
чередования каналов не определена (а, может уже и определена, а я не знаю).
channel0 - байт для левого канала
channel1 - байт для правого канала
8 bit mono:
-sample1- -sample2- -sample3- -sample4-
channel0 channel0 channel0 channel0
8 bit stereo:
-sample1- -sample2channel0 channel1 channel0 channel1
16 bit mono:
-sample1- -sample2channel0 channel0 channel0 channel0
(low byte) (high byte) (low byte) (high byte)
16 bit stereo:
-sample1channel0 channel0 channel1 channel1
(low byte) (high byte) (low byte) (high byte)
Средние и крайние значения элемента дискретизации вычисляются так:
Разрядность Формат данных
-1-8 bit unsigned char
8-16 bit int
например,
Формат max min midpoint
8 bit pcm 255 0 128
16 bit pcm 32767 -32768 0
Для примера разберем начало простенького pcm wave файла по байтам
Все смещения (слева) и размеры полей (справа в квадратных скобках )
приведены в hex виде.
- Начало riff-чанка
00 'riff' [4]
04 dword - размер riff-чанка [4]
- Начало wave-формы
08 'wave' [4]
- Начало fmt-чанка
0c 'fmt ' [4]
10 dword - размер fmt-чанка (10h или 12h) [4]
- Структура waveformat (или waveformatex)
14 word wformattag = 1 (это же pcm) [2]
16 word nchannels = 1 [2]
18 dword nsamplespersec = 11025 [4]
1c dword navgbytespersec = 11025 [4]
20 word nblockalign = 1 [2]
22 word nbitspersample = 8 [2]
24 word cbsize (=0 или отсутствует для pcm. [2]
Далее в круглых скобках приведены
смещения для случая без cbsize)
- Конец fmt-чанка
- Начало data-чанка
26 (24) 'data' [4]
2a (28) dword размер data-чанка [4]
2e (2c) sample0,sample1,sample2,... [???]
- Конец wave-формы
- Конец riff-чанка
Работать с таким файлом можно по следующей грубой схеме:
1. Проверяем сигнатуру 'riff' по смещению 0
2. Проверяем сигнатуру 'wave' по смещению 8
3. Проверяем wformattag=1 по смещению 14
4. Читаем nchannels,nbitspersample по смещениям 16 и 22
5. Если надо, читаем nsamplespersec по смещению 18
6. Начиная со смещения 24, начинаем искать data-чанк. Этого
можно было бы и не делать, а сразу читать сигнал по смещению 2e(2c),
но я встречал wave файлы, у которых после fmt-чанка вставлен некий
fact-чанк длины 4 (+4 на сигнатуру +4 на cksize), о назначении коего
мне, к сожалению, ничего не известно. Таким образом, после прочтения
fmt-чанка надо пройти по всем таким чанкам, пока не упремся в
data-чанк.
7. Читаем сигнал по смещению 2e(2С) или по смещению сигнатуры 'data' плюс 8
и до конца файла (или, если не лень, смотрим размер data-чанка и
соответственно читаем, сколько надо)
Надеюсь, сей скромный документ поможет читателю разобраться в
азах программирования звука, потратив на это гораздо меньше времени, чем
в моем случае. При его составлении весьма активно использовались
¦ microsoft windows multimedia programmer`s reference
¦ win32 sdk programmer`s guide
¦ некоторый личный опыт.
serg sorokin