Duck DK3 IMA ADPCM

From MultimediaWiki
Jump to navigation Jump to search

Some Sega Saturn game CDs contain AVI files which store audio using the Duck DK3 IMA ADPCM algorithm. DK3 ADPCM data can be decoded using the same tables as are used to decode IMA ADPCM data while using a slightly modified variant of the IMA ADPCM algorithm. The name DK3 apparently comes from the fact that 3 ADPCM nibbles decode to 4 16-bit PCM samples, in contrast to Duck's DK4 IMA ADPCM algorithm, in which 4 ADPCM nibbles decode to 4 16-bit PCM samples.

All multi-byte values are encoded in little-endian format. The length of a single block of DK3 data is encoded in the nBlockAlign field of an AVI file's WAVEFORMATEX header.

The DK3 algorithm encodes a sum channel and a difference channel, rather than left and right channels, using the standard IMA ADPCM algorithm and tables. Note that the encoding implies that the format only supports stereo data. A block of DK3 has a 16-byte preamble with the following information:

bytes 0-1     unknown
bytes 2-3     sample rate
bytes 4-9     unknown
bytes 10-11   initial sum channel predictor
bytes 12-13   initial diff channel predictor
byte 14       initial sum channel index
byte 15       initial diff channel index 

After processing the block preamble, a stream of DK3 data is decoded nibble by nibble, just like many ADPCM algorithms. The low nibble is decoded first (bits 3-0), then the high nibble. When decoding the stream, it is useful to conceptualize it as a stream of nibbles:

n0 n1 n2 n3 n4 n5 n6 n7 ... 

where the nibbles were arranged in the original bytestream as:

byte0 byte1 byte2 byte3
 n1n0  n3n2  n5n4  n7n6 ... 

Each set of 3 nibbles decodes to 4 16-bit PCM samples using this process (note that the diff value is initialized to the same value as the diff predictor):

  • get next ADPCM nibble in stream
  • update sum channel predictor and index using nibble
  • get next ADPCM nibble in stream
  • update diff channel predictor and index using nibble
  • diff value = (diff value + diff predictor) / 2
  • next left channel PCM sample = sum channel + diff value
  • next right channel PCM sample = sum channel - diff value
  • get next ADPCM nibble in stream
  • update sum channel predictor and index using nibble
  • next left channel PCM sample = sum channel + diff value
  • next right channel PCM sample = sum channel - diff value