Дата: 20 октября 2005 г.
Автор: jTommy (jTommy@by.ru)
WWW: jTommy.by.ru
Спецификация формата файлов FDA
(Fourier/Fast Digital Audio)

Для описания структур данных я буду использовать синтаксис языка С. Используемые типы данных:
CHAR 8-битовое целое со знаком, 1-байтовый символ
BYTE 8-битовое целое без знака
INT 16-битовое целое со знаком
UINT 16-битовое целое без знака
LONG 32-битовое целое со знаком
ULONG 32-битовое целое без знака
PSTRING Строка фиксированной длины (Pascal-строка)

Общие сведения
Порядок байтов в 16- и 32-битных числах "младший в младшем" т.е. нормальный для систем x86.
Данные в файле организованы в виде порций. Порция – это независимая структура данных, содержащая заголовок и (не обязательно) блок данных. Порции не имеют фиксированных позиций в файле и читаются последовательно. В блоке данных порция может содержать структуру данных, поток данных или другие порции.

Структура файла
FDAHeader Заголовок файла
FBIF Порция File Burn Info
FDA_ Порция-контейнер FDA
INFO
Порция INFO
DATA
Порция DATA

Заголовок файла
Заголовок файла содержит идентификатор и версию файла.

typedef struct TFDAHeader {
  CHAR Signature[12]; // Идентификатор файла (всегда "Relic Chunky")
  CHAR SignServ[4];   // (всегда 0x0d0a1a00)
  ULONG MajorVer;     // Возможно, старший номер версии (всегда 0x01)
  ULONG MinorVer;     // Возможно, младший номер версии (всегда 0x01)
} FDAHeader;

Поле SignServ останавливает вывод файла на экран в командной строке (команда "type”).

Порции данных
FDA файл всегда содержит следующие порции: FBIF, FDA_, INFO, DATA. Порядок порций не меняется. Но, возможно, в будущем будут добавлены новые порции. Каждая порция начинается с заголовка:

typedef struct TChunkHeader {
  CHAR ChunkType[4];            // Тип порции
  CHAR ChunkID[4];              // Идентификатор порции
  ULONG ChunkVer;               // Версия порции
  ULONG ChunkSize;              // Размер данных в порции, в байтах
  ULONG lenChunkName;           // Длина строки ChunkName
  CHAR ChunkName[lenChunkName]; // Имя порции
} ChunkHeader;

Поле ChunkType определяет тип порции: "DATA" - порция содержит данные (структуру и/или поток); "FOLD" - порция-контейнер, содержит субпорции. В этом случае в поле ChunkSize записывается сумма размеров всех субпорций.

Порция FBIF (File Burn Info)

Порция содержит информацию о создании FDA файла. Во всех файлах “Relic Chunky” эта порция всегда записана сразу после заголовка.

typedef struct TFBIFChunk {
  ULONG lenPluginName;            // Длина строки PluginName (всегда 0x0a)
  CHAR PluginName[lenPluginName]; // Имя плагина (всегда "RAW to FDA")
  ULONG PluginVer;                // Версия плагина (всегда 0x01)
  ULONG lenUserName;              // Длина строки UserName
  CHAR UserName[lenUserName];     // Имя пользователя в ОС
  ULONG lenBurnTime;              // Длина строки BurnTime
  CHAR BurnTime[lenBurnTime];     // Дата и время создания файла
} FBIFChunk;

Поле PluginName определяет имя плагина, который использовался при создании файла (или умеет работать с данными, записанными в файле).
В поле BurnTime дата и время записываются в следующем формате: "[Month] [dd], [yyyy], [h]:[mm]:[ss] [AM/PM]". Например: "August 08, 2004, 5:36:59 PM".
Поля заголовка порции всегда равны следующим значениям:
  ChunkType    = "DATA"
  ChunkID      = "FBIF"
  ChunkVer     = 0x01
  lenChunkName = 0x0d
  ChunkName    = "FileBurnInfo"\0


Порция FDA_
Это порция-контейнер. Содержит две субпорции: INFO и DATA. По идентификатору этой порции определяется тип данных, записанных в порции DATA.

typedef struct TFDA_Chunk {
} FDA_Chunk;

Поля заголовка порции всегда равны следующим значениям:
  ChunkType    = "FOLD"
  ChunkID      = "FDA "
  ChunkVer     = 0x01
  lenChunkName = 0x00
  ChunkName    = ""

Порция INFO
Порция содержит информацию о данных, записанных в порции DATA.

typedef struct TINFOChunk {
  ULONG Channels;     // Количество каналов (1 - моно, 2 - стерео и т.д.)
  ULONG SampleSize;   // Размер семпла в битах
  ULONG BlockBitrate; // Размер фрейма в битах
  ULONG SampleRate;   // Частота дискретизации в герцах
  ULONG BeginLoop;    // Начало цикла (всегда 0x00)
  ULONG EndLoop;      // Конец цикла (всегда 0xffffffff)
  ULONG StartOffset;  // Начальное смещение цикла (всегда 0x00)
} INFOChunk;

Поле BlockBitrate определяет размер фрейма в битах. Стандартные значения 256, 512, 1024 и 2048 эти значения соответствуют следующим битрейтам (для одного канала): 22.05kbps, 44.1kbps – низкое качество, 88.2kbps – среднее качество и 176.4kbps – высокое качество.
Поля BeginLoop, EndLoop и StartOffset содержат значения для проигрывания звука в цикле. При указанных выше значениях цикл не используется.
Поля заголовка порции всегда равны следующим значениям:
  ChunkType    = "DATA"
  ChunkID      = "INFO"
  ChunkVer     = 0x01
  ChunkSize    = 0x1c
  lenChunkName = 0x00
  ChunkName    = ""

Порция DATA
Порция содержит аудио данные (поток). Аудио данные сжаты алгоритмом с потерями, собственной разработки Relic. Cжатые данные разделены на фреймы. Размер фрейма постоянен и задается полем BlockBitrate порции INFO.

typedef struct TDATAChunk {
  ULONG DataSize;           // Размер сжатых звуковых данных
  BYTE SoundData[DataSize]; // Звуковые данные
} DATAChunk;

Поля заголовка порции всегда равны следующим значениям:
  ChunkType    = "DATA"
  ChunkID      = "DATA"
  ChunkVer     = 0x01
  lenChunkName = 0x00
  ChunkName    = ""

Спецификация формата файлов AIFF-C
(Compressed AIFF)
Официальную спецификацию можно найти, например, здесь. Я опишу только структуру файла, особенности и изменения, которые внесла в формат Relic.

Структура файла
FORM Порция-контейнер FORM
FVER
Порция Format Version
COMM
Порция Common
SSND
Порция Sound Data
MARK
Порция Marker

Порции данных
Порция SSND (Sound Data)
Порция содержит аудио данные.

typedef struct TSSNDChunk {
  ULONG Offset;      // Смещение данных (всегда 0x00)
  ULONG BlockSize;   // Размер блока данных (всегда 0x00)
  UINT BlockBitrate; // Размер фрейма в битах
} SSNDChunk;

Поле BlockBitrate существует только в файлах, созданных программами Relic. Значение этого поля аналогично полю BlockBitrate порции INFO в FDA файле.

Порция MARK (Marker)
Порция содержит данные, которые определяют, будет ли проигрываться звук в цикле. Структура этой порции соответствует официальной спецификации. Структура маркера отличается от описанной в спецификации только типом поля MarkerID. Каждый маркер определяется следующей структурой:

typedef struct TAIFCMarker {
  UINT MarkerID;      // Уникальный ID маркера (должен быть >0)
  ULONG Position;     // Позиция в данных, на которую указывает маркер
  PSTRING MarkerName; // Имя маркера
} AIFCMarker;

Программы Relic записывают в файл три маркера:
Begin Loop Marker:
  MarkerID   = 0x01
  Position   = 0x00
  MarkerName = "beg loop"\0
End Loop Marker:
  MarkerID   = 0x02
  Position   = 0xffffffff
  MarkerName = "end loop"\0
Start Offset Marker:
  MarkerID   = 0x03
  Position   = 0x00
  MarkerName = "start offset"\0
При указанных значениях поля Position цикл не работает. Некоторые программы Relic при таких значениях вообще не записывают в файл порцию MARK, другие все же записывают.