Практична реалізація IMA ADPCM

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

Лістинг 136 Програма imaadpcmh

#ifndef IMAADPCM_H_INCLUDED

#define IMAADPCM_H_INCLUDED

#include &quotaudioh&quot

#include &quotcompressh&quot

/ / Декодируем / кодуємо одну вибірку і оновлюємо стан AudioSample ImaAdpcmDecode (AudioByte deltaCode, ImaState &) AudioByte ImaAdpcmEncode (AudioSample, ImaState &)

#endif

Ми вже розглянули загальні елементи коду декомпресора IMA ADPCM B наступних двох розділах міститься детальне пояснення форматів пакетів і інша інформація, що відноситься до реалізованих фірмами Microsoft і Apple версіями алгоритму IMA ADPCM

Лістинг 137 Програма imaadpcmcpp

#include &quotimaadpcmh&quot

#include &ltcstdlib&gt

Варіант Microsoft IMA ADPCM

Як я вже говорив вище, в стандарті IMA не визначається розмір або формат пакетів B реалізації цього алгоритму, запропонованої фірмою Microsoft, використовуються пакети двох різних форматів: один для запису монозвучанія, інший для файлів зі стереозвуком

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

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

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

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

Як випливає з табл 132, стереопакет дуже схожий на структуру, яка по-

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

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

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

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

Байтов Опис

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

1 Молодший байт поточного відліку

1 Поточний розмір кроку

1 Завжди 0

n Стислі відліки

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

Байтов Опис

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

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

4 Стислі відліки для лівого каналу

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

4 Стислі відліки для лівого каналу

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

4 Стислі відліки для лівого каналу

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

буфера, в які, по мірі декодування, будуть записуватися відліки для правого і лівого каналів Як тільки обидва буфера будуть порожні, метод GetSamples звернеться до методу NextPacket для читання і декодування наступного пакету

Лістинг 138 Оголошення класу 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 &ampa, 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-файлу, я віддаю перевагу безпосередньо обчислювати цей параметр Крім того, в даної функції виділяється память під різні буфери Деструктор необхідний для видалення буферів

Лістинг 139 Реалізація класу IMA ADPCM версії Microsoft

DecompressImaAdpcmMs::DecompressImaAdpcmMs

(AudioAbstract &ampa, int packetLength, int channels)

: AbstractDecompressor(a) {

cerr &lt&lt &quotEncoding: IMA ADPCM (Microsoft variant)"

if (channels == 2) { cerr &lt&lt &quot (stereo)" }

cerr &lt&lt &quot\n&quot

_channels = channels

_samplesPerPacket = packetLength

_bytesPerPacket = (_samplesPerPacket + 7 )/2 * channels

_packet = new AudioByte[_bytesPerPacket]

_samples[1] = _samples[0] = 0

while (channels-&gt 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 ході декомпресії потрібно ретельно стежити за тим, щоб використовувати для кожного каналу відповідну змінну

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

size_t DecompressImaAdpcmMs::NextPacket() {

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

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

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

ImaState state [2] / / За однією змінною стану

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

/ / Прочитуємо 4-байтний заголовок

/ / Для кожного каналу

for(int ch=0ch &lt _channels ch++) {

state[ch]previousValue =

static_cast&ltsigned char&gt(bytePtr[1])*0x100

+ static_cast&ltsigned char&gt(bytePtr[0])

if (bytePtr[2] &gt 88)

/ / Помилка формату IMA ADPCM

/ / (Неправильне значення індексу)

cerr &lt&lt &quotIMA ADPCM Format Error (bad index value)\n"

else

state[ch]index = bytePtr[2]

if (bytePtr[3])

/ / Помилка формату IMA ADPCM

/ / (Порушення синхронізації)

cerr &lt&lt &quotIMA ADPCM Format Error (synchronization error)\n"

bytePtr + = 4 / / Пропускаємо цей заголовок

_samplePtr[ch] = _samples[ch]

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

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

}

/ / Декомпресія напівбайтів

size_t remaining = _samplesPerPacket-1

while (remaining&gt0) {

remaining-=8

int i

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

AudioByte b = *bytePtr++

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

*_samplePtr[0]++ = ImaAdpcmDecode((b&gt&gt4) &amp

0xF,state[0])

}

if (_channels &lt 2)

continue / / Якщо монозапісь, то пропускаємо залишилася

/ / Частина циклу

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

for (i=0i&lt4i++) { AudioByte b = *bytePtr++

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

*_samplePtr[1]++ = ImaAdpcmDecode((b&gt&gt4) &amp

0xF,state[1])

}

}

return _samplesPerPacket

}

Оскільки формат пакета для монозаписи відрізняється від формату пакета, використовуваного для запису стереозображення метод GetSamples повинен за одне звернення запитувати тільки один пакет, а потім копіювати декодовані дані в буфер, зазначений у запиті

Лістинг 139 Реалізація класу 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 &gt 0) &amp&amp (wanted &gt 0)) {

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

_samplesRemaining–

wanted–

}

break

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

/ / Чергування вибірок

while((_samplesRemaining &gt 0) &amp&amp (wanted &gt 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

Лістинг 1310 Оголошення класу 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 &ampstate)

public:

DecompressImaAdpcmApple(AudioAbstract &ampa, int channels)

size_t GetSamples(AudioSample *outBuff, size_t len)

}

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

зовать масиви: нам нема чого турбуватися про виділення і звільнення памяті

Лістинг 1311 Реалізація класу IMA ADPCM версії Apple

DecompressImaAdpcmApple::DecompressImaAdpcmApple

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

{

/ / Кодування: IMA ADPCM: Версія Apple

cerr &lt&lt &quotEncoding: IMA ADPCM (Apple variant)" if (channels == 2) { cerr &lt&lt &quot (stereo)" } cerr &lt&lt &quot\n"

_samplesRemaining = 0

_state[0]previousValue = 0

_state[0]index = 0

_state[1] = _state[0]

_channels = channels

}

Читання пакетів являє собою відносно прямолінійний процес Відзначимо, що на відміну від Microsoft Apple не стала перезапускати декомпрессор на початку кожного пакета Це означає, по-перше, що інформацію про стан декомпресора необхідно записувати в обєкті (замість того щоб тримати її локально в методі NextPacket), по-друге, що при зчитуванні 9 старших біт останнього відліку з заголовка необхідно зробити вибір: продовжити роботу з наявною інформацією про стан або використовувати значення, що постачається з пакетом Наприклад, коли я отримаю перший пакет файлу, я, ймовірно, долженбуду скористатися значенням, вказаним у файлі B представленої нижче функції я порівнюю старші 9 біт і перевіряю, узгоджуються вони чи ні Якщо узгоджуються, то я зберігаю накопичене значення, так як воно точніше

Лістинг 1311 Реалізація класу IMA ADPCM версії Apple

(Продовження)

size_t DecompressImaAdpcmApple::NextPacket(AudioSample

*sampleBuffer,

&ampstate) {

AudioByte _packet[34]

/ / Прочитуємо пакет і перевіряємо заголовок

size_t bytesRead = ReadBytes(_packet,34)

if (bytesRead &lt 34) return 0

/ / Перевіряємо стан декомпресора

stateindex = _packet[1] &amp 0x7F

if (stateindex &gt 88) {

/ / Помилка синхронізації

cerr &lt&lt &quotSynchronization error\n"

exit(1)

ImaState

}

/ / Відновлюємо 9 старших біт останньої вибірки AudioSample lastSample =

(static_cast&ltsigned char&gt(_packet[0])*0x100 +

static_cast&ltsigned char&gt(_packet[1])) &amp 0xFF80

/ / Якщо отримане значення не збігається із зразком,

/ / Беремо значення з файлу,

if ((statepreviousValue &amp 0xFF80) = lastSample)

statepreviousValue = lastSample

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

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

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

}

return 64

}

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

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

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

size_t remaining = wanted

while (remaining &gt 0) {

if (_samplesRemaining == 0) {

for(int i = 0 i&lt_channels i++) {

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

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

_samplePtr[i] = _samples[i]

}

}

switch(_channels) {

case 1:

while((_samplesRemaining &gt 0) &amp&amp (remaining &gt 0)) {

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

_samplesRemaining–

remaining–

}

break

case 2:

while((_samplesRemaining &gt 0) &amp&amp (remaining &gt 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 Programmers Guide to Sound: Пер з англ М: ДМК Пресс, 2000 432 с, іл (Серія «Для програмістів»)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*