Читання файлів AIFF

B оригінальної специфікації IFF автори звертають особливу увагу на те, що блоки даних різних типів можуть слідувати в будь-якому порядку Тому не виключено, що в деяких файлах AIFF, наприклад, блок звукових даних буде розташований перед форматним блоком, определющім спосіб зберігання даних

Один із способів роботи з такими файлами побіжний перегляд всього файлу в пошуках останньої великої області та створення в памяті картки, що містить положення і тип кожного блоку Потім цю карту можна було б використовувати для витягання конкретних блоків

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

B моєму класі AiffRead реалізований інший підхід Це викликано почасти необхідністю забезпечити можливість роботи з потоками, почасти простотою реалізації Замість того щоб картировать місце розташування кожній області, я читаю файл від початку до кінця, обробляючи кожен блок у міру виявлення Хоча теоретично можуть існувати файли AIFF, які не вдасться відтворити за допомогою цієї програми, я жодного разу не стикався з подібною ситуацією

Контейнери

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

При читанні, наприклад, блоку ssnd, розташованого всередині контейнера FORM AIFF, стек містить два елементи Мінлива _currentChunk вказує поточну вершину стека -1 Означає, що стек порожній

Лістинг 184 Інтерфейс функцій зберігання блоків класу AiffRead

private:

/ / Стек блоків

struct {

unsigned long type / / Тип блоку

unsigned long size / / Розмір блоку

unsigned long remaining / / Залишилося прочитати байтів

bool isContainer / / Істина, якщо це контейнер

unsigned long containerType / / Тип контейнера

} _chunk[5]

int _currentChunk / / Вершина стека

void PopStack()

bool ReadAiffSpecificChunk(unsigned long type, unsigned long size)

void DumpTextChunk(unsigned long size, const char *)

bool ReadIffGenericChunk(unsigned long type, unsigned long size)

void NextChunk(void)

Ядро цього класу метод NextChunk Насамперед він пропускає залишок поточного блоку області та відповідним чином оновлює стек Потім читається заголовок наступного блоку і приймається рішення, що з ним робити

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

Лістинг 185 Реалізація класу AiffRead

void AiffRead::PopStack(void) {

bool AiffRead::ReadAiffSpecificChunk(unsigned long type, unsigned long size) {

return false

}

bool AiffRead::ReadIffGenericChunk(unsigned long type, unsigned long size) {

return false

}

/ / Скидання памяті текстового блоку див таку главу

void AiffRead::DumpTextChunk(unsigned long size, const char

*name) {

char *text = new char[size+2]

_streamread(text,size)

long length = _streamgcount()

_chunk[_currentChunk]remaining = length

text[length] = 0

cerr &lt&lt name &lt&lt &quot &quot &lt&lt text &lt&lt &quot\n"

delete [] text

}

void AiffRead::NextChunk(void) { PopStack()

if (_streameof()) {

/ / Прочитуємо черговий блок

_currentChunk = 1 / / Спустошуємо стек

return

}

unsigned long type = ReadIntMsb(_stream,4) unsigned long size = ReadIntMsb(_stream,4) if (_streameof()) {

_currentChunk = 1 / / Спустошуємо стек

return

}

_currentChunk++

_chunk[_currentChunk]type = type

_chunk[_currentChunk]size = size

_chunk[_currentChunk]remaining = size

_chunk[_currentChunk]isContainer = false

_chunk[_currentChunk]containerType = 0

if (ReadAiffSpecificChunk(type,size)) return if (ReadIffGenericChunk(type,size)) return char code[5] = &quotCODE"

code[0] = (type&gt&gt24)&amp255code[1] = (type&gt&gt16)&amp255

code[2] = (type&gt&gt8 )&amp255code[3] = (type   )&amp255

/ / Нерозпізнаний блок ..

/ / Ігнорується

cerr &lt&lt &quotIgnoring unrecognized `&quot &lt&lt code &lt&lt &quot&quot chunk\n"

}

Методи ReadAiffSpecificChunk і ReadIffGenericChunk відповідають за обробку даних усередині всіх розпізнаних блоків Детальніше ми поговоримо про це в наступних розділах

Є кілька типів блоків, які є загальними для форматів AIFF

і IFF/8SVX O них мова піде в наступному розділі

Контейнер FORM AIFF

Файл AIFF містить єдиний контейнер FORM, який, у свою чергу, включає в себе всі інші блоки файлу Я пропоную зчитувати зовнішній блок під час ініціалізації

Лістинг 186 Ініціалізація стека блоків у класі AiffRead

_currentChunk = 1 / / Спустошуємо стек NextChunk ()

/ / Переконуємося, що перший блок –

/ / Це контейнер FORM / AIFF

if ( (_currentChunk = 0)

|| (_chunk[0]type = ChunkName(F,O,R,M))

|| (_chunk[0]isContainer = true)

|| ( (_chunk[0]containerType = ChunkName(A,I,F,F))

&amp&amp(_chunk[0]containerType = ChunkName(A,I,F,C)))

)

{

/ / Зовнішній блок у файлі AIFF не є контейнером

// FORM/AIF

cerr &lt&lt &quotOutermost chunk in AIFF file isnt FORM/AIF!!"

exit(1)

}

}

Блок FORM легко обробляється всередині методу NextChunk Потрібно тільки позначити його як контейнер і прочитати в контейнерний тип

Лістинг 187 Обробка AIFF блок конкретного формату і повернення істини

if (type == ChunkName(F,O,R,M)) {

_chunk[_currentChunk]isContainer = true

/ / Спочатку необхідно перевірити розмір контейнера

_chunk[_currentChunk]containerType = ReadIntMsb(_stream,4)

_chunk[_currentChunk]remaining = 4

if (_currentChunk &gt 0) {

/ / Ha внутрішньому рівні виявлений блок FORM

cerr &lt&lt &quotFORM chunk seen at inner level!?!\n"

}

return true

}

Навіть якщо поточний блок не є блоком FORM, ми твердо знаємо, що зовн-

ний блок завжди буде ставитися до цього типу

Лістинг 187 Обробка AIFF-блоку конкретного формату і повернення істини (продовження)

if ((_currentChunk &gt= 0) &amp&amp (_chunk[0]type = ChunkName(F,O,R,M))){

/ / Зовнішній блок не відноситься до типу FORM

cerr &lt&lt &quotOutermost chunk is not FORM!?!\n"

_currentChunk = 1

return true

}

Блок FVER

Щоб у майбутньому уникнути проблем, коли знову буде потрібно оновити AIFF-C,

був запропонований новий тип блоків FVER Такий блок містить мітку часу,

що показує дату опублікування використаної версії стандарту AIFF-C Коли стандарт зазнає значних змін, ця тимчасова позначка змінюється Ha момент написання цієї книги сама остання версія стандарту AIFF-C була опубліковано 23 травня 1990 р у 14:40 Використовує стандартний для компютерів Macintosh формат представлення часу (кількість секунд, що пройшли з 1 січня 1904 року, 0:00) тимчасова позначка має значення 2726318400 (у шістнадцятковій запису A2805140)

Лістинг 187 Обробка AIFF-блоку конкретного формату і повернення істини (продовження)

if (type == ChunkName(F,V,E,R)) { unsigned long version = ReadIntMsb{_stream,4) if (version = 2726318400) {

/ / Нераспознан формат AIFC-файла

cerr &lt&lt &quotUnrecognized AIFC file format\n"

exit(1)

}

_chunk[_currentChunk]remaining = 4

return true

}

Блок COMM

Блок COMM містить інформацію про формат запису звуку

Лістинг 187 Обробка AIFF-блоку конкретного формату і повернення істини (продовження)

if (type == ChunkName(C,O,M,M)) {

if (_currentChunk = 1) {

/ / Блок COMM зустрівся на неприпустимому рівні

cerr &lt&lt &quotCOMM chunk seen at wrong level!?!\n"

}

_formatData = new unsigned char[size+2]

_streamread(reinterpret_cast&ltchar *&gt(_formatData),size)

_formatDataLength = _streamgcount()

_chunk[_currentChunk]remaining = 0

return true

}

Більшість аудіоформатів увазі зберігання значень, таких як частота дискретизації, у вигляді цілих чисел Для підвищення точності фірма Apple вирішила використовувати формат з плаваючою крапкою Даний підхід застосуємо до додатків, які вимагають підвищеної точності (наприклад, виробництво кіно або відеофільмів), але робота з таким форматом ускладнює читання і запис даних Точний формат, використовуваний Apple, 80-бітний, з плаваючою крапкою Перші 16 біт застосовуються для запису біта знака і 15-бітної двійковій експоненти Розумно припустити, що число нормалізовано значить можна проігнорувати молодші 32 біта і використовувати швидке перетворення в ціле

Лістинг 185 Реалізація класу AiffRead (продовження)

void AiffRead::MinMaxSamplingRate(long *min, long *max, long

*preferred) { InitializeDecompression()

unsigned ieeeExponent = BytesToIntMsb(_formatData+8,2)

unsigned long ieeeMantissaHi = BytesToIntMsb(_formatData+10,4)

ieeeExponent & = 0x7FFF / / Видаляємо знаковий біт

/ / (Частота дискретизації не може

/ / Бути менше 0)

long samplingRate = ieeeMantissaHi &gt&gt (16414 ieeeExponent)

*min = *max = *preferred = samplingRate

}

void AiffRead::MinMaxChannels(int *min, int *max, int *preferred)

{

InitializeDecompression()

unsigned long channels = BytesToIntMsb(_formatData+0,2)

*min = *max = *preferred = channels

}

Точне вміст блоку типу COMM варіюється залежно від використовуваного методу стиснення B табл 182 показаний формат, який застосовується для чистих даних ІКМ Інші технології стиснення використовують розширений формат з додатковою інформацією

B системному програмному забезпеченні Apple 4-байтниє коди широко застосовуються для ідентифікації типів даних та іншої важливої ​​інформації B випадку з AIFF-C ці коди визначають метод стиснення Мультимедійне програмне забезпечення Apple здатне знайти звуковий кодек на основі даного 4-байтного коду, що робить можливим динамічну установку нових кодеків Зауважимо, що найменування методу компресії використовується тільки для інформації користувача воно може варіюватися залежно від програми запису

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

Лістинг 185 Реалізація класу AiffRead (продовження)

size_t AiffRead::GetSamples(AudioSample *buffer, size_t numSamples) {

Таблиця 182 Вміст блоку COMM

Розмір Опис

2 Кількість каналів

4 Загальна кількість фреймів за відліками

2 Кількість бітів на відлік

10 Фрейми з отсчетами в секунду (80-бітове число з плаваючою точкою IEEE)

4 Код компресії (див табл 183)

n Найменування методу компресії

Таблиця 183 Вибрані коди форматів для АIFF-С

Код Опис

NONE                                    PCM

АСЕ2 Стиснення АСЕ 2:1

АСЕ8 Стиснення АСЕ 8:3

МАСЗ Стиснення MACE 3:1

МАС6 Стиснення MACE 6:1

ulaw ITU G711 мю-функція

ima4                                       IMAADPCM

if (_decoder) InitializeDecompression()

return _decoder-&gtGetSamples(buffer,numSamples)

}

При виборі декодера я просто витягаю імя кодека з файлу і порівнюю його з іменами декодерів, підтримка яких реалізована в програмі

Лістинг 185 Реалізація класу AiffRead (продовження)

void AiffRead::InitializeDecompression() {

if (_decoder) return

/ / Перевіряємо, що ми прочитали блок типу COMM

while (_formatData) { NextChunk ()

if (_currentChunk &lt 0) {

/ / He знайдений блок типу COMM

cerr &lt&lt &quotNo COMM chunk found!?!\n"

exit(1)

}

}

/ / B відповідно до типу компресії підбираємо

/ / Декомпрессор

unsigned long type = ChunkName(N,O,N,E)

/ / За замовчуванням немає if (_formatDataLength> = 22)

type = BytesToIntMsb(_formatData+18, 4)

if (_decoder) {

char code[5] = &quotCODE"

code[0] = (type&gt&gt24)&amp255   code[1] = (type&gt&gt16)&amp255

code[2] = (type&gt&gt8 )&amp255   code[3] = (type  )&amp255

cerr &lt&lt &quotI dont support AIFF-C compression type &quot &lt&lt code

&lt&lt &quot\n"

exit (1)

}

}

Дані ІКМ

Для підтримки зворотної сумісності файл, записаний зі звуковими даними ІКМ, повинен бути описаний як файл AIFF (а не AIFF-C) Більшість файлів у форматі AIFF / AIF-C зберігається саме так Відліки ІКМ зберігаються у форматі чисел зі знаком запис багатобайтові відліків ведеться зі старшого розряду

Лістинг 188 Створення декомпресора для АIFF-файла відповідно до типу компресії

if (type == ChunkName (N, O, N, E)) {/ / Формат ІКМ unsigned long bitsPerSample = BytesToIntMsb (_formatData +6, 2)

_decoder = new DecompressPcm8Signed(*this)

else if (bitsPerSample <= 16) / / 16-бітові дані

/ / Зі знаком

_decoder = new DecompressPcm16MsbSigned(*this)

}

Дані мю-функції

Файли формату AIFF-C дозволяють використовувати компресію за допомогою мю-

функції

Лістинг 188 Створення декомпресора для AIFF-файла відповідно до типу компресії (продовження)

if (type == ChunkName (u, l, а, w)) {/ / Формат

/ / Мю-функції

_decoder = new DecompressG711MuLaw(*this)

}

Дані IMA ADPCM

Фірма Apple використовує свій власний варіант стиснення IMA ADPCM

Докладніше про це розповідається в главі 13

Лістинг 188 Створення декомпресора для AIFF-файла відповідно до типу компресії (продовження)

if (type == ChunkName (i, m, a, 4 )) {/ / Формат IMA ADPCM int channels = BytesToIntMsb (_formatData +0,2)

_decoder = new DecompressImaAdpcmApple(*this,channels)

}

Блок SSND

B блоках типу SSND зберігаються стислі звукові дані Вони починаються з 8 байт інформації, яка зазвичай не використовується

Лістинг 187 Обробка AIFF-блоку специфічного формату і повернення істини (продовження)

if (type == ChunkName(S,S,N,D)) { SkipBytes(_stream,8)

_chunk[_currentChunk]remaining = 8

return true

}

Тут важливий метод ReadBytes Коли хто-небудь запитує звукові дані, потрібно, по-перше, переконатися, що вони зчитуються з блоку SSND, по-друге, витягти відповідну кількість байтів і повернутися

Лістинг 185 Реалізація класу AiffRead (продовження)

size_t AiffRead::ReadBytes(AudioByte *buffer, size_t numBytes) {

while (_chunk[_currentChunk]type = ChunkName(S,S,N,D)) {

NextChunk()

if (_currentChunk <0) {/ / Стек порожній?

/ / Ні звукових даних

cerr &lt&lt &quotI didnt find any sound data?!?\n"

return 0

}

}

if (numBytes &gt _chunk[_currentChunk]remaining)

numBytes = _chunk[_currentChunk]remaining

_streamread(reinterpret_cast&ltchar *&gt(buffer), numBytes)

numBytes = _streamgcount()

_chunk[_currentChunk]remaining = numBytes

return numBytes

}

Глава 19 Формат файлу IFF/8SVX

Основні ідеї, що лежать в основі обмінних файлових форматів, були впроваджені фірмою Electronic Arts для використання на машинах Commodore Amiga Фірма розробила гнучкий файловий формат  IFF  (Interchange File Format, формат файлів для обміну) для зберігання різних типів даних Файли IFF можуть містити одне зображення, форматований текст, анімацію, звук або будь-яку комбінацію типів даних Для зберігання своїх власних типів даних розробники можуть створювати нові типи блоків

Структура файлів IFF та ж, що і у RIFF або AIFF-файлів вона описувалася в главі 17 Проте багатобайтові значення в IFF-файлах зберігаються у форматі MSB Крім того, в IFF-файлах використані інші імена блоків Хоча сьогодні IFF застосовується лише зрідка, він є попередником RIFF і AIFF Вихідна документація IFF у великій мірі підходить для всіх трьох файлових форматів

Зазвичай файл IFF складається з єдиного блоку FORM, тип якого визначають дані, записані у файлі Рідше зовнішньої областю є блоки CAT або LIST з кількома вкладеними блоками FORM B цьому розділі ми розглянемо тільки файли, що містять єдину область FORM

Ідентифікація файлів IFF/8SVX

Файли, про які піде мова, мають єдиний зовнішній контейнер FORM типу 8SVX (абревіатура фрази «8-bit sampled voice» 8-бітний оцифрований голос) Існують інші способи структуризації файлів IFF, однак вони використовуються рідко

Лістинг 191 Ідентифікація файлів формату IFF/8SVX

bool IsIffFile (istream & file) {fileseekg (0) / / K початку файлу unsigned long form = ReadIntMsb (file, 4) if (form = CnunkName (F, O, R, M))

return false / / He IFF-файл

SkipBytes (file, 4) / / Пропускаємо поле довжини блоку

unsigned long type = ReadIntMsb(file,4)

if (type == ChunkName(8,S,V,X))

return true

return false / / IFF-файл, але його формат НЕ 8SVX

}

Огляд формату IFF/8SVX

Найбільш широко використовуваний на компютерах Amiga аудіоформат IFF файл складається з одного контейнера FORM типу 8SVX, який часто називають IFF/8SVX Форма 8SVX була розроблена для зберігання семплів (оцифрованих звуків) музичних інструментів

Форма 8SVX може містити безліч блоків різних типів Некото-

риє з них перераховані в табл 191

Таблиця 191 Типи блоків IFF/8SVX

Імя Опис

VHDR Інформація про звуковому форматі

NAME Найменування звуку

(С) Інформація про авторські права

AUTH Автор

ANNO Додатковий коментар

BODY Звукові дані

АТАК Атака

RLSE Загасання

Джерело: Кінтцель Т Керівництво програміста по роботі зі звуком = 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>

*

*