Difference between revisions of "XA"

From SimsWiki
Jump to: navigation, search
(Auto-inserted from WakkaWikki)
 
m (Info moved to 2026960B. Changed to redirect)
Line 1: Line 1:
{{OldWikiEntry}} Maxis eXtendable Audio format from The Sims and SimCity 3000/4. Can use Bil Simser's [[http:''www.simstools.com/xantippe.php Xantippe]] to convert to WAV format.
+
#REDIRECT [[2026960B]]
  
 
+
[[Category:Modding]]
 
+
[[Category:InternalFormats]]
--------------------------------------
+
[[Category:FormatsByName]]
    Maxis XA Audio File Format Description     5-01-2002
+
--------------------------------------
+
 
+
By Valery V. Anisimovsky (samael@avn.mccme.ru)
+
 
+
In this document I'll try to describe audio file format used in some Maxis
+
games, in particlular, SimCity3000 (perhaps, in some other Maxis games) for
+
music, speech and sfx files.
+
 
+
The files this document deals with have extension: .XA. Note that the
+
extension of audio files of this format may be different from that.
+
 
+
Throughout this document I use C-like notation.
+
 
+
All numbers in all structures described in this document are stored in files
+
using little-endian (Intel) byte order.
+
<pre>
+
======
+
1. XA File Header
+
======
+
 
+
The XA file has the following header:
+
 
+
struct XAHeader
+
{
+
  char szID[4];
+
  DWORD dwOutSize;
+
  WORD wTag;
+
  WORD wChannels;
+
  DWORD dwSampleRate;
+
  DWORD dwAvgByteRate;
+
  WORD wAlign;
+
  WORD wBits;
+
};
+
 
+
szID -- string ID, which is equal to "XAI\0" (sound/speech) or "XAJ\0" (music).
+
 
+
dwOutSize -- the output size of the audio stream stored in the file (in bytes).
+
 
+
wTag -- seems to be PCM waveformat tag (0x0001). This corresponds to the
+
(decompressed) output audio stream, of course.
+
 
+
wChannels -- number of channels for the file.
+
 
+
dwSampleRate -- sample rate for the file.
+
 
+
dwAvgByteRate -- average byte rate for the file (equal to (dwSampleRate)*(wAlign)).
+
Note that this also corresponds to the decompressed output audio stream.
+
 
+
wAlign -- the sample align value for the file (equal to (wBits/8)*(wChannels)).
+
Again, this corresponds to the decompressed output audio stream.
+
 
+
wBits -- resolution of the file (8 (8-bit), 16 (16-bit), etc.).
+
 
+
Note that the part of the header from (wTag) until (wBits) is really
+
WAVEFORMATEX structure (the contents of PCM .WAV fmt chunk).
+
 
+
=====
+
2. XA File Data
+
=====
+
 
+
Right after the XA header comes the compressed audio stream. The compression
+
algorithm used is EA ADPCM (see below). Music files in SimCity3000 are
+
stereo 22050 Hz 16-bit, and speech/sfx are mono 22050 Hz 16-bit.
+
 
+
============
+
3. EA ADPCM Decompression Algorithm
+
============
+
 
+
During the decompression four LONG variables must be maintained for stereo
+
stream: lCurSampleLeft, lCurSampleRight, lPrevSampleLeft, lPrevSampleRight
+
and two -- for mono stream: lCurSample, lPrevSample. At the beginning of the
+
audio stream you must initialize these variables to zeros.
+
Note that LONG here is signed.
+
 
+
The stream is divided into small blocks of 0x1E (stereo) or 0xF (mono) bytes.
+
You should process all blocks in their turn. Here's the code which
+
decompresses one stereo stream block.
+
 
+
BYTE  InputBuffer[InputBufferSize]; '' buffer containing data for one block
+
BYTE  bInput;
+
DWORD i;
+
LONG  c1left,c2left,c1right,c2right,left,right;
+
BYTE  dleft,dright;
+
 
+
bInput=InputBuffer[0];
+
c1left=EATable[HINIBBLE(bInput)];  '' predictor coeffs for left channel
+
c2left=EATable[HINIBBLE(bInput)+4];
+
dleft=LONIBBLE(bInput)+8;  '' shift value for left channel
+
 
+
bInput=InputBuffer[1];
+
c1right=EATable[HINIBBLE(bInput)];  '' predictor coeffs for right channel
+
c2right=EATable[HINIBBLE(bInput)+4];
+
dright=LONIBBLE(bInput)+8;  '' shift value for right channel
+
 
+
for (i=2;i<0x1E;i+=2)
+
{
+
  left=HINIBBLE(InputBuffer[i]);  '' HIGHER nibble for left channel
+
  left=(left<<0x1c)>>dleft;
+
  left=(left+lCurSampleLeft*c1left+lPrevSampleLeft*c2left+0x80)>>8;
+
  left=Clip16BitSample(left);
+
  lPrevSampleLeft=lCurSampleLeft;
+
  lCurSampleLeft=left;
+
 
+
  right=HINIBBLE(InputBuffer[i+1]); '' HIGHER nibble for right channel
+
  right=(right<<0x1c)>>dright;
+
  right=(right+lCurSampleRight*c1right+lPrevSampleRight*c2right+0x80)>>8;
+
  right=Clip16BitSample(right);
+
  lPrevSampleRight=lCurSampleRight;
+
  lCurSampleRight=right;
+
 
+
  '' Now we've got lCurSampleLeft and lCurSampleRight which form one stereo
+
  '' sample and all is set for the next step...
+
  Output((SHORT)lCurSampleLeft,(SHORT)lCurSampleRight); '' send the sample to output
+
 
+
  '' now do just the same for LOWER nibbles...
+
  '' note that nubbles for each channel are packed pairwise into one byte
+
 
+
  left=LONIBBLE(InputBuffer[i]);  '' LOWER nibble for left channel
+
  left=(left<<0x1c)>>dleft;
+
  left=(left+lCurSampleLeft*c1left+lPrevSampleLeft*c2left+0x80)>>8;
+
  left=Clip16BitSample(left);
+
  lPrevSampleLeft=lCurSampleLeft;
+
  lCurSampleLeft=left;
+
 
+
  right=LONIBBLE(InputBuffer[i+1]); '' LOWER nibble for right channel
+
  right=(right<<0x1c)>>dright;
+
  right=(right+lCurSampleRight*c1right+lPrevSampleRight*c2right+0x80)>>8;
+
  right=Clip16BitSample(right);
+
  lPrevSampleRight=lCurSampleRight;
+
  lCurSampleRight=right;
+
 
+
  '' Now we've got lCurSampleLeft and lCurSampleRight which form one stereo
+
  '' sample and all is set for the next step...
+
  Output((SHORT)lCurSampleLeft,(SHORT)lCurSampleRight); '' send the sample to output
+
}
+
 
+
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).
+
 
+
EATable is the table 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.
+
 
+
Clip16BitSample is quite evident:
+
 
+
LONG Clip16BitSample(LONG sample)
+
{
+
  if (sample>32767)
+
return 32767;
+
  else if (sample<-32768)
+
return (-32768);
+
  else
+
return sample;
+
}
+
 
+
As to mono sound, it's just analoguous -- you should process the blocks each
+
being 0xF bytes long:
+
 
+
bInput=InputBuffer[0];
+
c1=EATable[HINIBBLE(bInput)]; '' predictor coeffs
+
c2=EATable[HINIBBLE(bInput)+4];
+
d=LONIBBLE(bInput)+8;  '' shift value
+
 
+
for (i=1;i<0xF;i++)
+
{
+
  left=HINIBBLE(InputBuffer[i]);  '' HIGHER nibble for left channel
+
  left=(left<<0x1c)>>dleft;
+
  left=(left+lCurSampleLeft*c1left+lPrevSampleLeft*c2left+0x80)>>8;
+
  left=Clip16BitSample(left);
+
  lPrevSampleLeft=lCurSampleLeft;
+
  lCurSampleLeft=left;
+
 
+
  '' Now we've got lCurSampleLeft which is one mono sample and all is set
+
  '' for the next input nibble...
+
  Output((SHORT)lCurSampleLeft); '' send the sample to output
+
 
+
  left=LONIBBLE(InputBuffer[i]);  '' LOWER nibble for left channel
+
  left=(left<<0x1c)>>dleft;
+
  left=(left+lCurSampleLeft*c1left+lPrevSampleLeft*c2left+0x80)>>8;
+
  left=Clip16BitSample(left);
+
  lPrevSampleLeft=lCurSampleLeft;
+
  lCurSampleLeft=left;
+
 
+
  '' Now we've got lCurSampleLeft which is one mono sample and all is set
+
  '' for the next input byte...
+
  Output((SHORT)lCurSampleLeft); '' send the sample to output
+
}
+
 
+
So, you should process HIGHER nibble of the input byte first and then
+
LOWER nibble for mono sound.
+
 
+
Of course, this decompression routine may be greatly optimized.
+
 
+
======
+
4. EA ADPCM Table
+
======
+
 
+
LONG EATable[]=
+
{
+
  0x00000000,
+
  0x000000F0,
+
  0x000001CC,
+
  0x00000188,
+
  0x00000000,
+
  0x00000000,
+
  0xFFFFFF30,
+
  0xFFFFFF24,
+
  0x00000000,
+
  0x00000001,
+
  0x00000003,
+
  0x00000004,
+
  0x00000007,
+
  0x00000008,
+
  0x0000000A,
+
  0x0000000B,
+
  0x00000000,
+
  0xFFFFFFFF,
+
  0xFFFFFFFD,
+
  0xFFFFFFFC
+
};
+
 
+
</pre>
+
-----------
+
5. Credits
+
-----------
+
 
+
Dmitry Kirnocenskij (ejt@mail.ru)
+
Worked out EA ADPCM decompression algorithm.
+
 
+
Nicholas Sales (nicsales@mweb.co.za)
+
Provided me with SimCity3000 decoding stuff thereby inspired
+
me to decode the formats and write the plug-in for GAP.
+
 
+
-------------------------------------------
+
 
+
Valery V. Anisimovsky (samael@avn.mccme.ru)
+
http:''bim.km.ru/gap/
+
http:''www.anxsoft.newmail.ru
+
http:''anx.da.ru
+
On these sites you can find my GAP program which can search for XA audio
+
files in game resources, extract them, convert them to WAV and play them back.
+
There's also complete source code of GAP and all its plug-ins there,
+
including XA plug-in, which could be used for further details on how you
+
can deal with this format.
+
[[Category:Modding]]
+

Revision as of 01:12, 14 July 2006

  1. REDIRECT 2026960B
Personal tools
Namespaces

Variants
Actions
Navigation
game select
Toolbox