XA

## Maxis XA

**Maxis XA** (named after CD-ROM Extended Architecture) is a variant of the EA ADPCM lossy audio compression format. Maxis XA is used by all Maxis games from Sim City 3000 onwards.

Francois Revol is credited for reverse engineering the original EA ADPCM audio format. Dmitry Kirnocenskij is credited for reverse engineering Maxis XA.

All content on this page is public domain. A complete working Maxis XA file reading implementation can be found in the FileHandler interface of Niotso under the ISC license.

## XA file header

```
struct XAHeader
{
 char szID[4];
 DWORD dwOutSize;
 /\* WAVEFORMATEX \*/
 WORD wFormatTag;
 WORD nChannels;
 DWORD nSamplesPerSec;
 DWORD nAvgBytesPerSec;
 WORD nBlockAlign;
 WORD wBitsPerSample;
};

```

* **szID** - A null-terminated string identifier, which may be equal to "XAI" (sound, speech) or "XAJ" (music) or "XA" (untagged) ("XAS" appeared in Command & Conquer 3)
* **dwOutSize** - The decompressed size of the audio stream
* **wFormatTag** - The decoded audio format; set to WAVE\_FORMAT\_PCM (0x0001)
* **nChannels** - Number of channels in the decoded audio data
* **nSamplesPerSec** - Sampling rate used in the decoded audio data
* **nAvgBytesPerSec** - Bytes per second consumed by the decoded audio data; equal to nChannels\*nSamplesPerSec\*wBitsPerSample/8 or nSamplesPerSec\*nBlockAlign
* **nBlockAlign** - The number of bytes consumed by an audio frame (one sample for each channel) in the decoded audio data; equal to nChannels\*wBitsPerSample/8
* **wBitsPerSample** - The bits per sample for one audio channel in the decoded audio data; 8-, 16-, or 24-bit, etc.

Note that the part of the header from wFormatTag below is a Microsoft WAVEFORMATEX structure, albeit without the cbSize parameter.

szID is just a hint. XAI is stored stereo signed 16-bit 22,050Hz. XAJ is stored mono signed 16-bit 22,050Hz. If szID is untagged ("XA"), it can be determined from the WAVEFORMATEX structure.

## Decoding

The goal is to pack 16 bits per sample per channel into 4 without sacrificing as much quality as we would through basic scalar quantization.

Lossy Differential/Delta PCM ("DPCM") works on the principle of predicting the difference between the last sample and the current sample and storing the residual in a constricted range. The decompressor must therefore retain the last audio frame when it reads a new one. Before decompression begins, all previous-sample variables must be initialized to zero.

Prediction is applied in blocks of 15 bytes (1 control byte providing the prediction model for 28 samples) for each channel. Here is decompression code in C89 to decode a sequence of *complete* XA blocks.

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

\_\_inline int16\_t Clip16(int sample)
{
 if(sample>=32767) return 32767;
 else if(sample<=-32768) return -32768;
 else return (int16\_t) sample;
}

static const int8\_t XATable[] =
{
 0, 240, 460, 392,
 0, 0, -208, -220,
 0, 1, 3, 4,
 7, 8, 10, 11,
 0, -1, -3, -4
};

typedef struct {
 int16\_t PrevSample, CurSample;
 int divisor; /\* residual right-shift value \*/
 int c1, c2; /\* predictor coefficients \*/
} channel\_t;

void XADecode(const uint8\_t \*\_\_restrict InBuffer, unsigned BlockCount, int16\_t \*\_\_restrict OutBuffer, unsigned Channels)
{
 channel\_t \* Channel = malloc(Channels \* sizeof(channel\_t));
 memset(Channel, 0x00, Channels \* sizeof(channel\_t));

 while(BlockCount--){
 unsigned i;

 for(i=0; i<Channels; i++){
 unsigned byte = \*(InBuffer++);
 Channel[i].divisor = LONIBBLE(byte)+8;
 Channel[i].c1 = XATable[HINIBBLE(byte)];
 Channel[i].c2 = XATable[HINIBBLE(byte)+4];
 }

 for(i=0; i<14; i++){
 unsigned j;
 for(j=0; j<Channels; j++){
 unsigned byte = \*(InBuffer++);
 int n;
 for(n=0; n<2; n++){
 int NewValue = (n == 0) ? HINIBBLE(byte) : LONIBBLE(byte);
 NewValue = (NewValue << 28) >> Channel[j].divisor;
 NewValue = (NewValue + Channel[j].CurSample\*Channel[j].c1 + Channel[j].PrevSample\*Channel[j].c2 + 128) >> 8;
 Channel[j].PrevSample = Channel[j].CurSample;
 Channel[j].CurSample = Clip16(NewValue);
 }
 \*(OutBuffer++) = Channel[j].PrevSample;
 }
 for(j=0; j<Channels; j++) \*(OutBuffer++) = Channel[j].CurSample;
 }
 }

 free(Channel);
}

```

A complete working Maxis XA file reading implementation can be found in the FileHandler interface of Niotso under the ISC license.

## Other versions

There are several variants of EA ADPCM with decode support in FFmpeg. These are:

* EA ADPCM
* EA ADPCM / Maxis XA
* EA ADPCM R1
* EA ADPCM R2
* EA ADPCM R3
* EA ADPCM / XAS
* EA IMA ADPCM / EACS
* EA IMA ADPCM / SEAD

Source code for these can be found in libavcodec/adpcm.c.