Audio File Formats
Previous  Top 

Psychonauts Audio File Formats:
By Bgbennyboy


Pc Version: .isb files:
The .isb files contain the audio data.

Structure of file:
4 bytes: Header 'RIFF'
4 bytes: File Size (-8)
8 bytes: 'isbftitl'
4 bytes: Size of Text Block
X bytes: Text Block

Repeated throughout file for each entry:
*****
4 bytes: Block Name
4 bytes: Block Size (not including 8 bytes for block name and size)
X Bytes: Block Data
*****

Some of these blocks are special:

LIST BLOCK:
4 bytes: 'LIST'
4 bytes: Block Size (including all sub-blocks)
8 bytes: Dummy title? Usually 'samptitl'
4 bytes: Size of filename
X Bytes: Filename (each letter is seperated by a 0 byte)
The filename sometimes has the incorrect file extension so you'll want to remote this and replace it with the correct one. However sometimes the file extension has '-loop' tagged on the end eg 'AsylumExt.aif-Loop' - if you remove the file extension then this file would become 'AsylumExt' - but there is already a file with this name so you'd need to add 'loop' back to the filename somehow to make it a unique name eg 'AsylumExt-Loop'.

SINF BLOCK:
4 bytes: 'SINF'
4 bytes: Block Size
8 bytes: ?
4 bytes: Samplerate
8 bytes: ?

CHNK BLOCK:
4 bytes: 'CHNK'
4 bytes: Block Size
4 bytes: Number of Channels

CMPI BLOCK:
4 bytes: 'CMPI'
4 bytes: Block Size
20 bytes: ?
4 bytes: if = 1053609165 then its a normal pcm wav otherwise
its xbox adpcm.

DATA BLOCK:
4 bytes: 'DATA'
4 bytes: Block Size
Blocksize sometimes has 1 byte padding so:
if blocksize mod 2 <> 0 then blocksize:=blocksize + 1
X bytes: Rest of block is the file data.
Dump the file data then continue on to next LIST block. Repeat until you reach end of file.

Any other block:
Just skip the block. Read the block name and block size then seek 'blocksize' bytes.

Dumping Files:
If first 4 bytes of file data='OggS' then its an ogg file.
Otherwise its always an xbox or normal pcm wave.
If its an ogg file then just copy out 'blocksize' bytes.
If its a wav file though you need to write the .wav header first:

You need to write the following:
4 bytes: 'RIFF' (1179011410)
4 bytes: Blocksize + 40
4 bytes: 'WAVE' (1163280727)
4 bytes: 'fmt ' (544501094)
4 bytes: Size of fmt block (20)
2 bytes: PCM Code:
    if its normal pcm then = 1
    if its xbox adpcm then = 105
2 bytes: Number of channels
4 bytes: Samplerate
4 bytes: Byterate:
    if its normal pcm then = (SampleRate * No Channels * 16) div 8
    if its xbox adpcm then = No Channels * 24806;
2 bytes: Block align
    if its normal pcm then = (No Channels * 16) div 8
    if its xbox adpcm then = No Channels * 36
2 bytes: Bits per sample
    if its normal pcm then = 16
    if its xbox adpcm then = 4
4 bytes: Extra data
    if its normal pcm then = 16
    if its xbox adpcm then = 4194306
4 bytes: 'DATA' (1635017060)
4 bytes: Size of data block (blocksize bytes)   

Extra Checks:
Sometimes the Number of Channels=0 so change this to 1
(I think its always 1)



Xbox Version: .xwb files:
.xwb files are Xbox Wavebank files and contain the audio data.

Structure of file:
4 bytes: 'WBND'
4 bytes: Version
32 bytes: Bank header
Strictly speaking you can seek straight past this. It (always?) refers to 4 sections:
Section 1
4 bytes: Offset
4 bytes: Length
Section 2
4 bytes: Offset
4 bytes: Length
Section 3
4 bytes: Offset
4 bytes: Length
Section 4
4 bytes: Offset
4 bytes: Length

Sections:
The sections are:
Section 1: Wave bank info
Section 2: File records
Section 3: Always empty?
Section 4: File data

Section 1:
Wave bank info
4 bytes: Flags
4 bytes: Number of files
16 bytes: WaveBank name (padded with 0's)
4 bytes: Size of each file record (in section 2)
4 bytes: Size of each entry name block
4 bytes: Offset of file data (offset of section 3)
4 bytes: Always empty?

Section 2:
File records
Repeat for (Number of files)

2 bytes: Number of channels
2 bytes: Format tag (0=normal pcm) (1=xbox adpcm)
4 bytes: Magic Value
4 bytes: File Offset (in section 3)
4 bytes: File size
4 bytes: Loop region offset (not sure what this does)
4 bytes: Loop region length (not sure what this does)

Section 3:
Always empty?

Section 4:
File data


Dumping files:
Read the file records then seek to 'Offset of file data'(section 3) + File Offset.
The files are wav's so you need to write a .wav header first:

4 bytes: 'RIFF' (1179011410
4 bytes: Blocksize + 40
4 bytes: 'WAVE' (1163280727)
4 bytes: 'fmt ' (544501094)
4 bytes: Size of fmt block (20)
2 bytes: PCM Code:
    if its xbox adpcm then = 105
    if its normal pcm then = 1
2 bytes: Number of channels (NoChannels)
4 bytes: Samplerate:
    if its xbox adpcm then = (MagicValue - (1 + (NoChannels * 4))) div 32
    if its normal pcm then: I havent been able to work out a formula for getting the
    samplerate from the MagicValue for a normal pcm wav. The best I can do for now is
    hardcode values:
    If MagicValue=2148894856 then SampleRate=44100
    There are other MagicValues for other SampleRates but I havent determined these yet.
4 bytes: Byterate:
    if its normal pcm then = (SampleRate * NoChannels * 16) div 8

    if its xbox adpcm then: the following values were obtained from the xbox codec itself
    if SampleRate=48000 then ByteRate=NoChannels * 27000
    if SampleRate=44100 then ByteRate=NoChannels * 24806
    if SampleRate=32000 then ByteRate=NoChannels * 18000
    if SampleRate=22050 then ByteRate=NoChannels * 12403
    if SampleRate=16000 then ByteRate=NoChannels * 9000
    if SampleRate=11025 then (its different for some reason)
    if NoChannels=1 then ByteRate=6201 else ByteRate=12403
    if SampleRate=8000 then ByteRate=NoChannels * 4500
2 bytes: Block align
    if its normal pcm then = (No Channels * 16) div 8
    if its xbox adpcm then = No Channels * 36
2 bytes: Bits per sample
    if its normal pcm then = 16
    if its xbox adpcm then = 4
4 bytes: Extra data
    if its normal pcm then = 16
    if its xbox adpcm then = 4194306
4 bytes: 'DATA' (1635017060)
4 bytes: Size of data block (FileSize bytes)


Extra Checks:
Sometimes the Number of Channels=0 so change this to 1
Sometimes the Number of Channels is > 2 so correct it:
See if there is a remainder, if there is, its 2 channels, if not its 1:
if its xbox adpcm then CheckValue:=(MagicValue - 5) mod 32;
if its normal pcm then CheckValue:=(MagicValue - 4) mod 32;
if CheckValue=0 then
NoChannels:=1
else
NoChannels:=2;

These methods of checking the number of channels work in all cases. Since the number of channels can be incorrectly reported its probably best to run these checks on every file.

File Names:
.xsb files

You will notice that the Wavebank doesnt contain any file names. This is because these are stored in a seperate file - the SoundBank file (.xsb).

The name of the SoundBank corresponds to the WaveBank usually, eg ASMusic.xwb and ASMusic.xsb. If in doubt though, the SoundBank referrs to its WaveBank internally, so you can check.
Some WaveBanks do not have a corresponding SoundBank.

Offsets are relative to the beginning of the file unless otherwise stated.

4 bytes: 'SDBK'
4 bytes: ?
4 bytes: Offfset of wavebank name (Name is 16 bytes long)
18 bytes: ?
2 bytes: Number of files (File names)
28 bytes: Soundbank name?
File records:
Repeat for 'Number of files'
4 bytes: Offset of filename
4 bytes: ?
4 bytes: ?
4 bytes: ?
4 bytes: ?

Seek to 'Offset of filename' and read the filename (it is null terminated)
I assume that the order of the filenames in the SoundBank corresponds to the order of the files in the WaveBank, but I dont know for sure. Be aware that sometimes there are more entries in a WaveBank than there are file names in a SoundBank, so some files have no name.