Nintendo GameCube / Wii ADP

From Just Solve the File Format Problem
Jump to: navigation, search
File Format
Name Nintendo GameCube / Wii ADP
Ontology
Extension(s) .adp

Description

Gamecube DVD drive has an on-board decoder for specially compressed ADPCM files stored on the DVD. These "streaming audio" files can be triggered to play via I2S direct to the DSP, freeing the main CPU for other tasks. Typically this is used for background music or other sounds that don't need exact trigger / synchronization.

These files must be aligned on 32k boundaries on the DVD because that's the granularity of the request packet for playback.

Sample rate is not included in these files but rather controlled by the game, though nearly all (if not 100% entirely) used 48khz.

Output format is 16-bit, stereo, PCM.

Format

GCADPCM files are composed of repeated "blocks" of 32 bytes. Each block encodes for 28 samples. Output samples are based on the current sample value, adjusted by the previous two samples. The first 2 bytes control decode behavior for the samples in the block (in other words, the history function to use in decoding of this block's samples).

Byte Type Meaning
0 uint8 FLAGS L
1 uint8 FLAGS R
2 uint8 FLAGS L (repeat)
3 uint8 FLAGS R (repeat)
4-31 uint8 Sample values, one byte per sample. Top 4 bits are for R, bottom 4 are for L stereo.

You may wish to ASSERT(flagsL == flagsLrepeated) and (flagsR == flagsRrepeated), which should be true in all cases...

Decode process follows:

namespace StreamADPCM
{

static s32 histl1;
static s32 histl2;
static s32 histr1;
static s32 histr2;

static s16 ADPDecodeSample(s32 bits, s32 q, s32& hist1, s32& hist2)
{
	s32 hist = 0;
	switch (q >> 4)
	{
	case 0:
		hist = 0;
		break;
	case 1:
		hist = (hist1 * 0x3c);
		break;
	case 2:
		hist = (hist1 * 0x73) - (hist2 * 0x34);
		break;
	case 3:
		hist = (hist1 * 0x62) - (hist2 * 0x37);
		break;
	}
	hist = MathUtil::Clamp((hist + 0x20) >> 6, -0x200000, 0x1fffff);

	s32 cur = (((s16)(bits << 12) >> (q & 0xf)) << 6) + hist;

	hist2 = hist1;
	hist1 = cur;

	cur >>= 6;
	cur = MathUtil::Clamp(cur, -0x8000, 0x7fff);

	return (s16)cur;
}

void InitFilter()
{
	histl1 = 0;
	histl2 = 0;
	histr1 = 0;
	histr2 = 0;
}

void DecodeBlock(s16* pcm, const u8* adpcm)
{
	for (int i = 0; i < SAMPLES_PER_BLOCK; i++)
	{
		pcm[i * 2]     = ADPDecodeSample(adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] & 0xf, adpcm[0], histl1, histl2);
		pcm[i * 2 + 1] = ADPDecodeSample(adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] >> 4,  adpcm[1], histr1, histr2);
	}
}

Information

  • [1] - Decoder from Dolphin GC/Wii emulator
Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox