Главная » Программирование звука » Практическая реализация IMA ADPCM

1

Теперь,  когда  мы  уже  описали  базовую  пошаговую  декомпрессию  отсчетов, давайте  перейдем  к  детальному  рассмотрению  форматов  двух  незначительно  отличающихся  реализаций  этого  алгоритма.  Первый  из  них  был  разработан  фирмой  Microsoft  для  сжатия  WAVE-файлов.  Второй   фирмой  Apple  для  работы с файлами в формате AIFF-C и QuickTime.

Листинг 13.6. Программа imaadpcm.h

#ifndef IMAADPCM_H_INCLUDED

#define IMAADPCM_H_INCLUDED

#include "audio.h"

#include "compress.h"

// Декодируем/кодируем одну  выборку  и  обновляем состояние AudioSample ImaAdpcmDecode(AudioByte deltaCode, ImaState &); AudioByte ImaAdpcmEncode(AudioSample, ImaState &);

#endif

Мы   уже   рассмотрели   общие   элементы   кода   декомпрессора   IMA   ADPCM. B  следующих  двух  разделах  содержится  детальное  объяснение  форматов  пакетов   и   другая   информация,   относящаяся   к   реализованным   фирмами   Microsoft и Apple версиям алгоритма IMA ADPCM.

Листинг 13.7. Программа imaadpcm.cpp

#include "imaadpcm.h"

#include <cstdlib>

Вариант Microsoft IMA ADPCM

Как я уже говорил выше, в стандарте IMA не определяется размер или формат пакетов.  B реализации  этого  алгоритма,  предложенной  фирмой  Microsoft,  используются  пакеты  двух  различных  форматов:  один   для  записи  монозвучания,  другой для файлов со стереозвуками.

Чтобы задать размер каждого пакета, Microsoft резервирует два поля в заголовкефайла WAVE. B поле Block Alignment (выравнивание блока, см. табл. 17.2) указывается количество байтов в каждом пакете. B поле Number of Channels (количество каналов)  указывается  количество  каналов  и,  следовательно,  определяется  точный формат пакета. Кроме того, в файлах WAVE IMA ADPCM добавляется 2 байта

информации  для  компрессора,  которые  используются  при  описании  количества отсчетов, содержащихся в пакете. Заметим, что значение поля Bits per Sample (бит на выборку) всегда равно 4.

Формат  пакета,  используемого  для  записи  монозвука,  приведен  в  табл.  13.1. Начинается он с 4-байтного заголовка, вслед за которым записываются сжатые данные. При декомпрессии пакета один отсчет извлекается из самого заголовка, а затем по  одному  отсчету  получается  при  декомпрессии  каждого  полубайта.  Например, если в поле BlockAlignment записано 256, то в пакете будет содержаться 505 моментальных значений (504 значения в 252 байтах плюс одно значение в заголовке).

Фирма  Microsoft  перезапускает  компрессор  в  начале  каждого  пакета.  Хотя в заголовке достаточно места для записи всех 16 бит текущего отсчета, в тех файлах с монозаписями, которые я исследовал (сжатых с помощью компрессора IMA ADPCM,  входящего  в  состав  Windows  95),  младший  значащий  байт  всегда  был равен 0.

Как следует из табл. 13.2, стереопакет очень похож на структуру, которая по-

лучилась бы при объединении двух монопакетов и записи их в виде чередующихся 32-битных слов. B частности, расширенное поле в заголовке WAVE-файла содержит  информацию  о  количестве  записанных  в  пакете  отсчетов  по  каждому  из каналов. Например, размер пакета в 2048 байт соответствует записи 2041 отсчета в заголовке файла WAVE (один в четырех байтах с информацией о статусе компрессора, 2040 в 1020 сжатых байтах).

Чтобы  произвести  непосредственную  реализацию  декомпрессора,  нам  пона-

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

Таблица 13.1. Формат пакета Microsoft IMA АDРСМ для записи монозвука

Байтов                      Описание

1                                 Старший байт текущего отсчета (всегда 0 для компрессора Microsoft)

1                                 Младший байт текущего отсчета

1                                Текущий размер шага

1                                 Всегда 0

n                                 Сжатые отсчеты

Таблица 13.2. Формат пакета Microsoft IMA АDРСМ для записи стереозвука

Байтов                                     Описание

4                                                Состояние декомпрессора для левого канала

4                                                Состояние декомпрессора для правого канала

4                                                Сжатые отсчеты для левого канала

4                                                Сжатые отсчеты для правого канала

4                                                Сжатые отсчеты для левого канала

4                                                Сжатые отсчеты для правого канала

4                                                Сжатые отсчеты для левого канала

4                                                Сжатые отсчеты для правого канала

буфера, в которые, по мере декодирования, будут записываться отсчеты для правого и левого каналов. Как только оба буфера будут пусты, метод GetSamples обратится к методу NextPacket для чтения и декодирования следующего пакета.

Листинг 13.8. Объявление класса IMA ADPCM версии Microsoft

class DecompressImaAdpcmMs: public AbstractDecompressor {

private:

int _channels;

AudioSample *_samples[2];   // Левый   и  правый  буферы   выборок.

AudioSample *_samplePtr[2]; // Указатели на   текущие  выборки.

size_t_samplesRemaining;    // Остаток  выборок

// для каждого из  каналов.

size_t_samplesPerPacket;    // Общее  количество  выборок

// в пакете.

public:

DecompressImaAdpcmMs(AudioAbstract &a, int packetLength, int channels);

~DecompressImaAdpcmMs();

size_t GetSamples(AudioSample *outBuff, size_t len);

private:

AudioByte *_packet;        // Временный буфер  для  пакетов.

size_t_bytesPerPacket;     // Длина   пакета.

size_t NextPacket();

};

Для    создания    декомпрессора    класса    DecompressImaAdpcmMs необходимо указать длину пакета (я использовал количество отсчетов в пакете, описанное среди дополнительной информации в заголовке WAVE-файла) и число каналов. Несмотря на то что информацию о количестве байтов в пакете также можно получить  из  заголовка  WAVE-файла,  я  предпочитаю  непосредственно  вычислять  этот параметр.  Кроме  того,  в  данной  функции  выделяется  память  под  различные  буферы. Деструктор необходим для удаления буферов.

Листинг 13.9. Реализация класса IMA ADPCM версии Microsoft

DecompressImaAdpcmMs::DecompressImaAdpcmMs

(AudioAbstract &a, int packetLength, int channels)

: AbstractDecompressor(a) {

cerr << "Encoding: IMA ADPCM (Microsoft variant)";

if (channels == 2) { cerr << " (stereo)"; }

cerr << "\n" ;

_channels = channels;

_samplesPerPacket = packetLength;

_bytesPerPacket = (_samplesPerPacket + 7 )/2 * channels;

_packet = new AudioByte[_bytesPerPacket];

_samples[1] = _samples[0] = 0;

while (channels-> 0)

_samples[channels] = new AudioSample[_samplesPerPacket];

_samplesRemaining = 0;

};

DecompressImaAdpcmMs::~DecompressImaAdpcmMs() {

if (_samples[0]) delete [] _samples[0];

if (_samples[1]) delete [] _samples[1];

if (_packet) delete [] _packet;

};

Наличие  пакетов  двух  форматов  делает  задачу  считывания  очередного  пакета несколько более сложной. Так как Microsoft решила перезапускать компрессор для каждого пакета, приходится создавать и инициализировать переменную, описывающую состояние компрессора для каждого канала. B ходе декомпрессии нужно тщательно  следить  за  тем,  чтобы  использовать  для  каждого  канала  соответствующую переменную.

Листинг 13.9. Реализация класса IMA ADPCM версии Microsoft (продолжение)

size_t DecompressImaAdpcmMs::NextPacket() {

// Выбираем пакет  и  проверяем  заголовок.

size_t bytesRead = ReadBytes(_packet,_bytesPerPacket); if (bytesRead < _bytesPerPacket) { return 0; } AudioByte *bytePtr = _packet;

// Перезапускаем декомпрессор.

ImaState state[2]; // По  одной переменной состояния

// декомпрессора для каналов.

// Считываем  4-байтный  заголовок

// для  каждого канала.

for(int ch=0;ch < _channels; ch++) {

state[ch].previousValue =

static_cast<signed char>(bytePtr[1])*0x100

+ static_cast<signed char>(bytePtr[0]);

if (bytePtr[2] > 88)

// Ошибка  формата  IMA ADPCM

// (неправильное значение  индекса).

cerr << "IMA ADPCM Format Error (bad index value)\n";

else

state[ch].index = bytePtr[2];

if (bytePtr[3])

// Ошибка  формата  IMA ADPCM

// (нарушение  синхронизации).

cerr << "IMA ADPCM Format Error (synchronization error)\n";

bytePtr+=4;     // Пропускаем этот  заголовок.

_samplePtr[ch] = _samples[ch];

// Декодируем одну выборку из  заголовка.

*_samplePtr[ch]++ = state[ch].previousValue;

}

// Декомпрессия  полубайтов.

size_t remaining = _samplesPerPacket-1;

while (remaining>0) {

remaining-=8;

int i;

// Декодируем  8 выборок левого  канала. for (i=0;i<4;i++) {

AudioByte b = *bytePtr++;

*_samplePtr[0]++ = ImaAdpcmDecode(b & 0xF,state[0]);

*_samplePtr[0]++ = ImaAdpcmDecode((b>>4) &

0xF,state[0]);

}

if (_channels < 2)

continue; // Если   монозапись, то  пропускаем  оставшуюся

// часть цикла.

// Декодируем  8 выборок правого  канала.

for (i=0;i<4;i++) { AudioByte b = *bytePtr++;

*_samplePtr[1]++ = ImaAdpcmDecode(b & 0xF,state[1]);

*_samplePtr[1]++ = ImaAdpcmDecode((b>>4) &

0xF,state[1]);

}

};

return _samplesPerPacket;

};

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

Листинг 13.9. Реализация класса IMA ADPCM версии Microsoft (продолжение)

size_t DecompressImaAdpcmMs::GetSamples(AudioSample *outBuff, size_t len) {

size_t wanted = len;

while (wanted > 0) {                 // Еще  нужны  данные?

if (_samplesRemaining == 0) {     // Требуется  чтение  данных

// с диска?

_samplesRemaining = NextPacket();

if (_samplesRemaining == 0) { return len wanted; }

_samplePtr[0] = _samples[0];

_samplePtr[1] = _samples[1];

}

switch(_channels) {         // Копируем данные в  outBuff.

case 1:                      // Монозапись: копируем  данные

// только левого  канала.

while((_samplesRemaining > 0) && (wanted > 0)) {

*outBuff++ = *_samplePtr[0]++;

_samplesRemaining–;

wanted–;

}

break;

case 2:                      // Стереозапись:

// чередование выборок.

while((_samplesRemaining > 0) && (wanted > 0)) {

*outBuff++ = *_samplePtr[0]++; // Левый   канал.

*outBuff++ = *_samplePtr[1]++; // Правый   канал.

samplesRemaining–;

wanted = 2;

}

break;

default: exit(1);

}

}

return len wanted;

}

Вариант Аррlе IMA ADPCM

Microsoft и Apple применяют одни и те же низкоуровневые средства компрес-

сии, однако форматы пакетов полностью различаются:

?   Apple использует один и тот же формат пакета для записи моно и стерео.

B файлах со стереозаписями пакеты данных левого канала чередуются с па-

кетами данных правого канала;

?   Microsoft выделяет одно моментальное значение для заголовка, a Apple нет.

Пакет  Microsoft  всегда  содержит  нечетное  количество  моментальных  зна-

чений, пакет Apple четное;

?   в заголовке пакета Microsoft записывается 8-битный индекс размера шага

и 8 или 16 бит текущего отсчета. Apple для индекса размера шага использу-

ет всего 7 бит и хранит 9 старших бит текущего отсчета;

декомпрессор Microsoft полностью перезапускается в начале очередного па-

кета. C компрессором Apple этого не происходит;

?   в заголовке пакета Microsoft для выявления ошибок синхронизации исполь-

зуется нулевой байт. B версии Apple подобных средств не предусмотрено (за исключением проверки того, что значение, записанное в 7-битном индексе, попадает в диапазон от 0 до 88);

?   длина пакета Apple всегда равна 34 байтам (64 отсчета). Разработчики Mic-

rosoft решили дать компрессору возможность подбирать размер пакета, ко-

личество отсчетов в пакете записывается в заголовке файла.

Имея в своем распоряжении этот длинный список, нетрудно адаптировать созданную  Microsoft версию компрессора IMA ADPCM,  которая  нами уже  рассматривалась, для файлов IMA ADPCM, подготовленных по правилам Apple.

Листинг 13.10. Объявление класса IMA ADPCM версии Apple

class DecompressImaAdpcmApple: public AbstractDecompressor {

private:

int _channels; ImaState _state[2]; AudioSample _samples[2][64]; AudioSample *_samplePtr[2]; size_t _samplesRemaining;

size_t NextPacket(AudioSample *sampleBuffer, ImaState &state);

public:

DecompressImaAdpcmApple(AudioAbstract &a, int channels);

size_t GetSamples(AudioSample *outBuff, size_t len);

};

Так как версия Apple всегда имеет дело с пакетами одного размера, можно исполь-

зовать массивы: нам незачем беспокоиться о выделении и освобождении памяти.

Листинг 13.11. Реализация класса IMA ADPCM версии Apple

DecompressImaAdpcmApple::DecompressImaAdpcmApple

(AudioAbstract &a, int channels) : AbstractDecompressor(a)

{

// Кодировка: IMA ADPCM: Версия  Apple.

cerr << "Encoding: IMA ADPCM (Apple variant)"; if (channels == 2) { cerr << " (stereo)"; } cerr << "\n";

_samplesRemaining = 0;

_state[0].previousValue = 0;

_state[0].index = 0;

_state[1] = _state[0];

_channels = channels;

};

Чтение   пакетов   представляет   собой   относительно   прямолинейный   процесс. Отметим, что в отличие от  Microsoft Apple  не стала перезапускать декомпрессор в начале каждого пакета. Это означает, во-первых, что информацию о состоянии декомпрессора  необходимо  записывать  в  объекте  (вместо  того  чтобы  держать  ее локально  в  методе  NextPacket),  во-вторых,  что  при  считывании  9  старших  бит последнего  отсчета  из  заголовка  необходимо  сделать  выбор:  продолжить  работу с  имеющейся  информацией  о  состоянии  или  использовать  значение,  поставляемое с пакетом. Например, когда я получу первый пакет файла, я, вероятно, долженбуду воспользоваться значением, указанным  в файле. B представленной ниже функции я сравниваю старшие 9 бит и проверяю, согласуются они или нет. Если согласуются, то я сохраняю накопленное значение, так как оно точнее.

Листинг 13.11. Реализация класса IMA ADPCM версии Apple

(продолжение)

size_t DecompressImaAdpcmApple::NextPacket(AudioSample

*sampleBuffer,

&state) {

AudioByte _packet[34];

// Считываем пакет  и  проверяем заголовок.

size_t bytesRead = ReadBytes(_packet,34);

if (bytesRead < 34) return 0;

// Проверяем состояние  декомпрессора.

state.index = _packet[1] & 0x7F;

if (state.index > 88) {

// Ошибка  синхронизации.

cerr << "Synchronization error.\n";

exit(1);

ImaState

}

// Восстанавливаем 9 старших бит  последней  выборки. AudioSample lastSample =

(static_cast<signed char>(_packet[0])*0x100 +

static_cast<signed char>(_packet[1])) & 0xFF80;

// Если   полученное значение  не   совпадает с образцом,

// берем значение  из  файла,

if ((state.previousValue & 0xFF80) != lastSample)

state.previousValue = lastSample;

// Декомпрессия  полубайтов, for(int i=0; i<32; i++) {

*sampleBuffer++ = ImaAdpcmDecode(_packet[i+2] & 0xF, state);

*sampleBuffer++ = ImaAdpcmDecode((_packet[i+2]>>4) & 0xF, state);

}

return 64;

};

Метод  GetSamples идентичен  тому,  который  мы  использовали  для  класса версии Microsoft. Единственное различие заключается в том, что нужно вызывать NextPacket один  раз  для  каждого  канала.  Также  необходимо  сообщить  методу NextPacket, какое   состояние   следует   использовать   (так   как   состояние   компрессора  сохраняется  независимо  для  каждого  канала)  и  куда  записывать  декодированные данные.

Листинг 13.11. Реализация класса IMA ADPCM версии Apple (продолжение)

size_t DecompressImaAdpcmApple::GetSamples(AudioSample *outBuff, size_t wanted){

size_t remaining = wanted;

while (remaining > 0) {

if (_samplesRemaining == 0) {

for(int i = 0; i<_channels; i++) {

_samplesRemaining = NextPacket (_samples [i] ,_state[i]);

if (_samplesRemaining == 0) { return wanted remaining; }

_samplePtr[i] = _samples[i];

}

}

switch(_channels) {

case 1:

while((_samplesRemaining > 0) && (remaining > 0)) {

*outBuff++ = *_samplePtr[0]++;

_samplesRemaining–;

remaining–;

}

break;

case 2:

while((_.samplesRemaining > 0) && (remaining > 0)) {

*outBuff++ = *_samplePtr[0]++; // Левый   канал.

*outBuff++ = *_samplePtr[1]++; // Правый   канал.

_samplesRemaining–;

remaining = 2;

}

break;

}

}

return wanted remaining;

};

Сравнение модулей Microsoft и Apple

C  точки  зрения  кодирования/декодирования,  реализация  IMA  ADPCM,  предложенная Apple, несколько проще, в основном благодаря тому, что не нужно заботиться о двух форматах пакетов. Кроме того, вариант фирмы Apple обеспечивает более  высокое  качество  записи.  B  реализации  Microsoft  отбрасывается  8  бит  состояния  декомпрессора  при  переходе  к  новому  монопакету.  Это  приводит  к  небольшому скачку величины отсчета в начале обработки каждого нового пакета.

Подход,   использованный   Microsoft,   позволяет   получать   более   компактный код. Если  предположить, что мы работаем  с пакетами длиной 2048  байт,  то при использовании  алгоритма  Microsoft  требуется  всего  8  байт  служебной  информации  на  каждые  2041  байт  сжатых  данных  (накладные  расходы  составляют  менее

0,4%).  При  использовании  метода  Apple  требуется  2  байта  служебной  информации на каждые 32 байта сжатых данных (более 6% накладных расходов). B этом подходе,  кроме  того,  уменьшается  время  задержки.  При одновременной  компрессии  и  декомпрессии  стереозвука  задержка  у  Microsoft  составит  всего  8  отсчетов, в  то  время  как  при  использовании  модуля  Apple  придется  ждать,  пока  не  будет получен весь пакет длиной 64 отсчета (до этого момента стереоданные недоступны). Однако данный аспект не является серьезным недостатком даже при низких частотахдискретизации: если мы записываем 11025 отсчетов в секунду, то 64 отсчета это меньше 6 миллисекунд.

Несколько слов об IMA ADPCM

Хотя первоначально  данный стандарт разрабатывался, чтобы стать платформонезависимым  стандартом  компрессии  звуковых  файлов,  IMA  ADPCM  не  полностью соответствует этому требованию. Низкоуровневая компрессия не вызывает затруднений: она не зависит от порядка следования байтов и, по определению, в ней используются только  элементарные  битовые  манипуляции.  Это  позволяет  добиться высокой производительности даже на тех процессорах, на которых целочисленное деление выполняется медленно.

Как бы там ни было, различия между версиями Microsoft и Apple сделали невозможным  совместное  использование  подобных  кодировщиков  и  декодировщиков. Невозможно извлечь сжатые данные формата IMA ADPCM из файла AIFF-C

и  подать  их  на  модуль  кодек  Microsoft,  и  точно  так  же  нельзя  данные  из  файла формата  IMA  ADPCM  WAVE  подать  на  кодек  Apple.  K  счастью,  кодеки  IMA ADPCM  занимают  немного  места,  так  что  программы,  о  которых  мы  говорили в этой главе, позволят вам с легкостью работать с обоими широко распространенными вариантами.

Источник: Кинтцель Т.  Руководство программиста по работе со звуком = A Programmer’s Guide to Sound: Пер. с англ. М.: ДМК Пресс, 2000. 432 с, ил. (Серия «Для программистов»).

По теме:

  • Комментарии

1 комментарий

  1. прошу помощи, есть закодированый в виде RAW
    из енго, с помощью Audacity получил расжатый в 8 раз, звук слышно достаточно хорошо, но шумы остались – помогите плз. разобраться с алгоритмом расжатия