СообЧа > База Знаний > Программирование

Вопрос

Формат файлов .wav PCM и ADPCM. желательно описание или полный url.

Ответ

Просто и ясно о формате WAVE файла.

-----------------------------

Приведенной информации вполне достаточно для работы с 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' + <fmt-чанк> + <data-чанк> , где
'WAVE' — просто сигнатура WAVE-формы
<fmt-чанк> — чанк с информацией о звуковом сигнале
<data-чанк> — чанк с собственно сигналом
<fmt-чанк> = 'fmt ' + <ckSize> + <WaveFormat> + <fmt-specific> , где
'fmt ' — сигнатура fmt-чанка
<ckSize> — его размер
<WaveFormat> — структура WaveFormat(mmsystem.h), описанная ниже
<fmt-specific> — структура с дополнительной информацией о формате, имеет переменную длину и зависит от wFormatCategory (см. ниже). В случае с PCM удобно пользоваться структурой WaveFormatEx (mmsystem.h), объединяющей в себе WaveFormat и два поля из fmt-s

pecific. Документация по Win32 SDK утверждает, что WaveFormatEx будет работать для ВСЕХ не-PCM форматов, что идет вразрез с утверждением MM Programmer`s Reference о переменной длине fmt-specific. Так что вопрос с не-PCM форматами мне пока не ясен.

<data-чанк> = 'data' + <ckSize> + <собственно сигнал> , где 'data' — сигнатура data-чанка
<ckSize> — его размер
<собственно сигнал> — последовательность байт, описывающая сигнал (см. Формат данных PCM)
WaveFormat
Структура WaveFormat имеет вид:
typedef struct
{
WORD wFormatTag; // Категория формата
WORD nChannels; // Число каналов
DWORD nSamplesPerSec; // Частота дискретизации
DWORD nAvgBytesPerSec; // Байт в секунду
WORD nBlockAlign; // Выравнивание данных в data-чанке
} WaveFormat;

Рассмотрим эту структуру подробнее.

wFormatTag Категория формата (неудачный перевод: калька format category). От этого значения зависят значения остальных полей этой структуры, структура <fmt-specific> и data-чанка. Существует несколько категорий формата; самая доступная — PCM (Pulse Code
Modulation) имеет wFormatTag = 1.
nChannels 1 — моно, 2-стерео, о большем числе каналов документация умалчивает.
nSamplesPerSec Частота дискретизации (число сэмплов в секунду).
nAvgBytesPerSec Среднее число байт в секунду, используется для эффективной буферизации. Для PCM вычисляется по формуле: (nChannels*nSamplesPerSec*nBitsPerSample)/8.
nBlockAlign Выравнивание данных в data-чанке. Для PCM вычисляется по формуле: (nChannels*nBitsPerSample)/8.
<fmt-specific> — Для категории 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------- --------Sample2-------

Channel0 Channel1 Channel0 Channel1
16 bit mono:

--------Sample1------- --------Sample2-------

Channel0 Channel0 Channel0 Channel0
(low byte) (high byte) (low byte) (high byte)
16 bit stereo:

-------------------Sample1---------------------

Channel0 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-чанка и соответственно читаем, сколько надо)

Из конференции Expert_FAQ



Copyright © 2000-2004 Сообщество Чайников
Контактная информация