Westwood ADPCM Audio
- Company Westwood Studios
Credit
The information below 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.
Note
AUD3.txt refers to Vladan Bato's file which is not yet up on the wiki.
AUD Files
Malcolm's AUD files have the same format as C&C's AUDs (which is described in AUD3.TXT) with only one exception: there's no OutSize field in their header. So it looks like the following:
struct AUDHeaderOld { WORD wSampleRate; DWORD dwSize; BYTE bFlags; BYTE bType; };
bType is equal to 0x01 for WS ADPCM compressed AUDs. All WS ADPCM compressed sounds I've ever encountered are 8-bit.
The meanings of the other fields in AUD header are the same as for C&C AUDs. These AUDs are divided in chunks with the chunk header being the same as for C&C, but those chunks have variable size (may be NOT 512 bytes) unlike C&C AUDs!
Note that WS ADPCM compressed AUDs in C&C (death screams) have just the same format as other AUDs in this game, i.e. with OutSize field.
WS ADPCM Decompression Algorithm
Each AUD chunk may be decompressed independently of others. This lets you implement the seeking for WS ADPCM AUDs (unlike IMA ADPCM ones). But during the decompression of the given chunk a variable (CurSample) should be maintained for this whole chunk:
SHORT CurSample; BYTE InputBuffer[InputBufferSize]; // input buffer containing the whole chunk WORD wSize, wOutSize; // Size and OutSize values from this chunk's header BYTE code; CHAR count; // this is a signed char! WORD i; // index into InputBuffer WORD input; // shifted input if (wSize==wOutSize) // such chunks are NOT compressed { for (i=0;i<wOutSize;i++) Output(InputBuffer[i]); // send to output stream return; // chunk is done! } // otherwise we need to decompress chunk CurSample=0x80; // unsigned 8-bit i=0; // note that wOutSize value is crucial for decompression! while (wOutSize>0) // until wOutSize is exhausted! { input=InputBuffer[i++]; input<<=2; code=HIBYTE(input); count=LOBYTE(input)>>2; switch (code) // parse code { case 2: // no compression... if (count & 0x20) { count<<=3; // here it's significant that (count) is signed: CurSample+=count>>3; // the sign bit will be copied by these shifts! Output((BYTE)CurSample); wOutSize--; // one byte added to output } else // copy (count+1) bytes from input to output { for (count++;count>0;count--,wOutSize--,i++) Output(InputBuffer[i]); CurSample=InputBuffer[i-1]; // set (CurSample) to the last byte sent to output } break; case 1: // ADPCM 8-bit -> 4-bit for (count++;count>0;count--) // decode (count+1) bytes { code=InputBuffer[i++]; CurSample+=WSTable4bit[(code & 0x0F)]; // lower nibble CurSample=Clip8BitSample(CurSample); Output((BYTE)CurSample); CurSample+=WSTable4bit[(code >> 4)]; // higher nibble CurSample=Clip8BitSample(CurSample); Output((BYTE)CurSample); wOutSize-=2; // two bytes added to output } break; case 0: // ADPCM 8-bit -> 2-bit for (count++;count>0;count--) // decode (count+1) bytes { code=InputBuffer[i++]; CurSample+=WSTable2bit[(code & 0x03)]; // lower 2 bits CurSample=Clip8BitSample(CurSample); Output((BYTE)CurSample); CurSample+=WSTable2bit[((code>>2) & 0x03)]; // lower middle 2 bits CurSample=Clip8BitSample(CurSample); Output((BYTE)CurSample); CurSample+=WSTable2bit[((code>>4) & 0x03)]; // higher middle 2 bits CurSample=Clip8BitSample(CurSample); Output((BYTE)CurSample); CurSample+=WSTable2bit[((code>>6) & 0x03)]; // higher 2 bits CurSample=Clip8BitSample(CurSample); Output((BYTE)CurSample); wOutSize-=4; // 4 bytes sent to output } break; default: // just copy (CurSample) (count+1) times to output for (count++;count>0;count--,wOutSize--) Output((BYTE)CurSample); } }
HIBYTE and LOBYTE are just higher and lower bytes of WORD:
#define HIBYTE(word) ((word) >> 8) #define LOBYTE(word) ((word) & 0xFF)
Note that depending on your compiler you may need to use additional byte separation in these defines, e.g. (((byte) >> 8) & 0xFF). The same holds for 4-bit and 2-bit nibble separation in the code above.
WSTable4bit and WSTable2bit are the delta tables given in the next section.
Output() is just a placeholder for any action you would like to perform for decompressed sample value.
Clip8BitSample is quite evident:
SHORT Clip8BitSample(SHORT sample) { if (sample>255) return 255; else if (sample<0) return 0; else return sample; }
This algorithm is ONLY for mono 8-bit unsigned sound, as I've never seen any other sound format used with WS ADPCM compression.
Of course, the decompression routine described above may be greatly optimized.
WS ADPCM Tables
CHAR WSTable2bit[]= { -2, -1, 0, 1 }; CHAR WSTable4bit[]= { -9, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8 };
AUDs in Legend Of Kyrandia III: Malcolm's Revenge
The WS ADPCM compression described above is used for all audio in this game:
Music is stand-alone .AUD files. Speech is AUDs in .TLK resource files. Sounds are AUDs in .PAK resource files.
These .TLKs and .PAKs do not use any compression or encryption for AUDs, so AUDs are stored "as is" in them. If you want to extract/play an AUD from PAK or TLK you just need to search the PAK or TLK for the AUD id, that is, DWORD value equal to 0x0000DEAF (or, in other words, string "\xAF\xDE\0\0"). Refer to Vladan Bato's AUD3.TXT for more details on AUD file structure.
VQA Movie Soundtracks
Soundtrack of VQA movie in Malcolm, C&C, Red Alert and C&C: Tiberian Sun is stored in SND0, SND1 or SND2 blocks. Refer to VQA_FRMT.TXT by Aaron Glover for details on the structure of VQA files. Here I only describe the contents of VQA sound blocks and VQHD (header) block.
VQHD block contains header for VQA. To the best of my knowledge, it has the following format:
struct VQAHeader { WORD wVersion; WORD unknown1; WORD wNumFrames; WORD wWidth; WORD wHeight; WORD unknown2; WORD unknown3; WORD unknown4; WORD unknown5; DWORD unknown6; WORD unknown7; WORD wSampleRate; BYTE bChannels; BYTE bResolution; char unknown8[14]; };
wVersion -- version of VQA: 1 -- oldest Malcolm's VQAs, 2 -- C&C, Red Alert, 3 -- C&C: Tiberian Sun.
wNumFrames -- number of frames in VQA. Note that number of sound blocks is (wNumFrames+1) for VQAs of version 2 (C&C, Red Alert), and (wNumFrames) for versions 1 and 3. But Dune2000 VQAs have also (wNumFrames) sound blocks while they're version 2 VQAs.
wSampleRate -- sample rate for soundtrack. Note that version 1 (Malcolm's) VQAs may have this value set to 0x0000! Use 22050 Hz in such cases.
bChannels -- number of channels (1 -- mono, 2 -- stereo). Note that version 1 VQAs may have this set to 0x00, so use 1 (mono) for such files.
bResolution -- resolution of soundtrack (0x10 -- 16-bit, 0x8 -- 8-bit). Note that version 1 VQAs may have this set to 0x00, so use 0x8 for such files.
All VQAs in Malcolm have their sound in either SND0 or SND1 blocks. SND0 blocks contain non-compressed PCM data. SND1 blocks contain small header and WS ADPCM compressed sound data. The header is the following:
struct SND1Header { WORD wOutSize; WORD wSize; };
Following the header comes WS ADPCM compressed sound data. Each SND1 sound block may be decompressed, just like a chunk of AUD file, independently of the others and the routine described above may be used for its decompression without any changes, provided you use wOutSize from the SND1Header.
As to VQAs in C&C and Red Alert their sound is in the SND2 blocks and compressed with IMA ADPCM algorithm, described in Vladan Bato's AUD3.TXT. The contents of SND2 block is just compressed data, without any headers and those blocks should be decompressed in their turn just like chunks of IMA ADPCM compressed AUD file as it's described in AUD3.TXT. This holds only for mono soundtracks.
But there're also stereo soundtracks in C&C and C&C: Tiberian Sun. They have different left/right channel nibbles layout.
For C&C (version 2) VQAs the layout is the following: LL RR LL RR ... That is, first byte contains two nibbles for two left channel values, next byte contains nibbles for right channel, etc. Note that lower nibble should be processed first and then higher one (see AUD3.TXT).
For C&C: Tiberian Sun (version 3) VQAs the layout is different: in SND2 block first go all nibbles for left channel, then all nibbles for right channel: LL LL LL ... LL RR RR RR ... RR Note that nibbles should be processed in the same turn: lower nibble first. So, when decoding SND2 block, just decompress first half of the block data for left channel, then second half -- for right channel.
PC Games Using this Format
Legend of Kyrandia III: Malcom's Revenge
Command & Conquer Tiberian Sun
Possibly using the format: