Date: November 14, 2005
Author: jTommy (jTommy@by.ru)
Home page: jTommy.by.ru
FDA file format specification
(Fourier/Fast Digital Audio)

A C-like language will be used to describe data structures in this document. The data types used are listed below:
CHAR 8-bit signed integer, 1-byte symbol
BYTE 8-bit unsigned integer
INT 16-bit signed integer
UINT 16-bit unsigned integer
LONG 32-bit signed integer
ULONG 32-bit unsigned integer
PSTRING Pascal-string

General info
Bytes order in 16- and 32-bit numbers is little-endian which is normal for x86 systems.
Data in file is organized in chunks. Chunk - independent data structure, containing header and data block (not necessarily). Chunks don't have fixed positions in a file and are read consistently. In a data block chunk can contain data structure, data stream or other chunks.

File structure
FDAHeader File header
FBIF File Burn Info chunk
FDA_ Container-chunk FDA
INFO
INFO chunk
DATA
DATA chunk

File header
File header contains identifier and file version.

typedef struct TFDAHeader {
  CHAR Signature[12]; // File identifier (always "Relic Chunky")
  CHAR SignServ[4];   // (always 0x0d0a1a00)
  ULONG MajorVer;     // Probably major version number (always 0x01)
  ULONG MinorVer;     // Probably minor version number (always 0x01)
} FDAHeader;

SignServ stops file output to display in command promt ("type" command).

Data chunks
FDA file always contains following chunks: FBIF, FDA_, INFO, DATA. Chunk order never changes. But in future new chunks may be added. Every chunk starts with header:

typedef struct TChunkHeader {
  CHAR ChunkType[4];            // Chunk type
  CHAR ChunkID[4];              // Chunk identifier
  ULONG ChunkVer;               // Chunk version
  ULONG ChunkSize;              // Data size in chunk (in bytes)
  ULONG lenChunkName;           // ChunkName string length
  CHAR ChunkName[lenChunkName]; // Chunk name
} ChunkHeader;

ChunkType sets chunk type: "DATA" - chunk contains data (structure and/or data stream); "FOLD" - container-chunk, contains subchunks. In this case in the ChunkSize the size of all chunks is writen.

FBIF chunk (File Burn Info)

This chunk contains info about FDA file creation. In all "Relic Chunky" files this chunk always follows the header.

typedef struct TFBIFChunk {
  ULONG lenPluginName;            // PluginName string length (always 0x0a)
  CHAR PluginName[lenPluginName]; // Plugin name (always "RAW to FDA")
  ULONG PluginVer;                // Plugin version (always 0x01)
  ULONG lenUserName;              // UserName string length
  CHAR UserName[lenUserName];     // OS username
  ULONG lenBurnTime;              // BurnTime string length
  CHAR BurnTime[lenBurnTime];     // Files creation date and time
} FBIFChunk;

PluginName defines plugin name, which was used for file creation (or can work with data in that file).
BurnTime contains date and time in the following format: "[Month] [dd], [yyyy], [h]:[mm]:[ss] [AM/PM]". For example: "August 08, 2004, 5:36:59 PM".
Chunk header always contains following values:
  ChunkType    = "DATA"
  ChunkID      = "FBIF"
  ChunkVer     = 0x01
  lenChunkName = 0x0d
  ChunkName    = "FileBurnInfo"\0


FDA_ chunk

This is container-chunk. It contains two subchunks: INFO and DATA.

typedef struct TFDA_Chunk {
} FDA_Chunk;

Chunk header always contains following values:
  ChunkType    = "FOLD"
  ChunkID      = "FDA "
  ChunkVer     = 0x01
  lenChunkName = 0x00
  ChunkName    = ""


INFO chunk

Chunk contains info on data contained in DATA chunk.

typedef struct TINFOChunk {
  ULONG Channels;     // Number of channels (1 - mono, 2 - stereo etc.)
  ULONG SampleSize;   // Sample size in bits
  ULONG BlockBitrate; // Frame size in bits
  ULONG SampleRate;   // Sampling rate in herz
  ULONG BeginLoop;    // Loop beginning (always 0x00)
  ULONG EndLoop;      // Loop ending (always 0xffffffff)
  ULONG StartOffset;  // Loop starting offset (always 0x00)
} INFOChunk;

BlockBitrate defines frame size in bits. Standard values are 256, 512, 1024 and 2048. This values correspond to the following bitrates (for one channel): 22.05kbps, 44.1kbps - low quality, 88.2kbps - medium quality and 176.4kbps - high quality.
BeginLoop, EndLoop and StartOffset contain values for sound playback in that loop. Loop is not used when values specified above are set.
Chunk header always contains following values:
  ChunkType    = "DATA"
  ChunkID      = "INFO"
  ChunkVer     = 0x01
  ChunkSize    = 0x1c
  lenChunkName = 0x00
  ChunkName    = ""


DATA chunk

Chunk contains audio data (stream). Audio is compressed with lossy algorithm by Relic's own development. Compressed data is divided into frames. Frame size is always constant and is set by BlockBitrate in INFO chunk.

typedef struct TDATAChunk {
  ULONG DataSize;           // Compressed data size
  BYTE SoundData[DataSize]; // Audio data
} DATAChunk;

Chunk header always contains following values:
  ChunkType    = "DATA"
  ChunkID      = "DATA"
  ChunkVer     = 0x01
  lenChunkName = 0x00
  ChunkName    = ""

AIFF-C file format specification
(Compressed AIFF)
Official specification docs can be found here. I will only describe changes Relic made to it.

File structure
FORM Container-chunk FORM
FVER
Format Version chunk
COMM
Common chunk
SSND
Sound Data chunk
MARK
Marker chunk

Data chunks
SSND chunk (Sound Data)
Chunk contains audio data.

typedef struct TSSNDChunk {
  ULONG Offset;      // Data offset (always 0x00)
  ULONG BlockSize;   // Data block size (always 0x00)
  UINT BlockBitrate; // Frame size in bits
} SSNDChunk;

BlockBitrate only exist in files made by Relics programs. Its purpose is the same as BlockBitrate in INFO chunk in FDA file.

MARK chunk (Marker)
This chunk contains data that define weather or not the sound will be played in loop. Chunk structure is the same as in official AIFF specs. Only MarkerID type is different. Every marker is defined by the following structure:

typedef struct TAIFCMarker {
  UINT MarkerID;      // Unique marker ID (must be >0)
  ULONG Position;     // Data position, marker points at
  PSTRING MarkerName; // Marker name
} AIFCMarker;

Relics programs make three markers in a file:
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
Loop doesn't work when such values are set in Position. Some programs by Relic don't even write MARK chunk in a file when such values are set. Although some do.