FutureVision audio formats

From MultimediaWiki
Jump to navigation Jump to search

Credit

The information on this page was originally based on one of the many format documents written up by Valery V. Anisimovsky, available on http://wotsit.org/ and many other sites across the internet.

Byte Order

All numbers are stored in little endian format.

CMP Audio Files

The music/sfx/speech in Harvester are .CMP files (stand-alone or stored in .DAT resources). CMP file has the following header:

struct CMPHeader
{
 char	szID[4];
 DWORD dwDataSize;
 DWORD dwSampleRate;
 WORD	wBits;
};

szID -- ID string, which is "FCMP".

dwDataSize -- the size of the data in the file. May be used for song length (in seconds) calculation (taking into account the compression ratio). CMP file size is the sum of the CMPHeader size and (dwDataSize).

dwSampleRate -- sample rate for the file.

wBits -- resolution of the file (8 (8-bit), 16 (16-bit), etc.).

The channels number is NOT specified in the header, so the default value (mono) should be used (all audio files in Harvester are mono).

After the CMPHeader comes seemingly useless data. Its size is somewhat uncertain, so you may define this as an option -- the good values for such an option are 0x0 (no odd data), 0x4 and 0x37 bytes. The last value seems to be the most adequate. After that odd junk of data comes IMA ADPCM compressed sound data. You may find IMA ADPCM decompression scheme description further in this document.

End of file

Note that at the end of some CMP files there's a junk of seemingly garbage data (usually 17 bytes) -- if you do not skip that when decompressing IMA ADPCM stream you'll hear a considerable "popping" at the end of the decompressed waveform.

IMA ADPCM Decompression Algorithm

During the decompression two LONG variables must be maintained for mono stream: lIndex, lCurSample. At the beginning of the file you must initialize them to zeroes. Note that LONG here is signed.

Here's the code which decompresses one byte of IMA ADPCM compressed mono stream. Other bytes are processed in the same way.

BYTE Input; // current byte of compressed data
BYTE Code;
LONG Delta;

Code=LONIBBLE(Input); // get LOWER 4-bit nibble

Delta=StepTable[lIndex]>>3;
if (Code & 4)
  Delta+=StepTable[lIndex];
if (Code & 2)
  Delta+=StepTable[lIndex]>>1;
if (Code & 1)
  Delta+=StepTable[lIndex]>>2;
if (Code & 8) // sign bit
  lCurSample-=Delta;
else
  lCurSample+=Delta;

// clip sample
if (lCurSample>32767)
  lCurSample=32767;
else if (lCurSample<-32768)
  lCurSample=-32768;

lIndex+=IndexAdjust[Code]; // adjust index

// clip index
if (lIndex<0)
  lIndex=0;
else if (lIndex>88)
  lIndex=88;

Output((SHORT)lCurSample); // send the sample to output

Code=HINIBBLE(Input); // get HIGHER 4-bit nibble
// ...just the same as above for higher nibble

HINIBBLE and LONIBBLE are higher and lower 4-bit nibbles:

#define HINIBBLE(byte) ((byte) >> 4)
#define LONIBBLE(byte) ((byte) & 0x0F)

Note that depending on your compiler you may need to use additional nibble separation in these defines, e.g. (((byte) >> 4) & 0x0F).

StepTable and IndexAdjust are the tables given in the next section of this document.

Output() is just a placeholder for any action you would like to perform for decompressed sample value.

Note that LOWER nibble is processed first for mono sound.

Of course, this decompression routine may be greatly optimized.

IMA ADPCM Tables

LONG IndexAdjust[]=
{
   -1,
   -1,
   -1,
   -1,
    2,
    4,
    6,
    8,
   -1,
   -1,
   -1,
   -1,
    2,
    4,
    6,
    8
};

LONG StepTable[]=
{
   7,	   8,	  9,	 10,	11,    12,     13,    14,    16,
   17,    19,	  21,	 23,	25,    28,     31,    34,    37,
   41,    45,	  50,	 55,	60,    66,     73,    80,    88,
   97,    107,   118,	 130,	143,   157,    173,   190,   209,
   230,   253,   279,	 307,	337,   371,    408,   449,   494,
   544,   598,   658,	 724,	796,   876,    963,   1060,  1166,
   1282,  1411,  1552,  1707,	1878,  2066,   2272,  2499,  2749,
   3024,  3327,  3660,  4026,	4428,  4871,   5358,  5894,  6484,
   7132,  7845,  8630,  9493,	10442, 11487,  12635, 13899, 15289,
   16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767
};

FST Movie Soundtrack

.FST files are movies used in Harvester. FST file has the following header:

struct FSTHeader
{
 char	szID[4];
 DWORD dwImageWidth;
 DWORD dwImageHeight;
 DWORD dwUnknown1;
 DWORD dwNumFrames;
 DWORD dwFrameRate;
 DWORD dwSampleRate;
 WORD	wBits;
 WORD	wUnknown2;
};

szID -- ID string, which is "2TSF".

dwNumFrames -- number of frames in FST.

dwSampleRate -- sample rate for the file.

wBits -- resolution of the file (8 (8-bit), 16 (16-bit), etc.).

After the FSTHeader comes the table of frame entries. It contains (dwNumFrames) entries of the following format:

struct FSTFrameEntry
{
 DWORD dwImageSize;
 WORD	wSoundSize;
};

After the table of frame entries comes the frame data. Each frame chunk contains image data which has the size (dwImageSize) bytes, and the sound data which has the size (wSoundSize) bytes -- these values are taken from the frame entry correspondent to the given frame. Sound data immediately follows the image data. Sound data in Harvester movies is non-compressed signed 16-bit PCM data.

Note that the sound part in the first frame chunk contains data for several frames (not only for the first frame). Thus you should skip the sound parts of several last frame chunks. Namely, the good estimation for the number of frame chunks to skip is the number of frames covered by sound data in the first frame chunk minus one. That is, you may take the frame entry for the first frame, devide its (wSoundSize) by the correspondent value for the second frame and subtract one from the result -- that'll give the number of frame chunks to skip (the last chunks, of course).

CMP Audio Files in DAT Archives

When stored in .DAT resources, CMP audio files are stored "as is", without compression or encryption. That means if you want to play/extract CMP file from the DAT resource you just need to search for (szID) id-string ("FCMP") and read CMP header starting at the beginning position of found id-string. This will give you starting point of the file and the size of the file will be the sum of CMPHeader size and (dwDataSize) header field.

PC Games Using FutureVision audio

Harvester