VIMA

From MultimediaWiki
Jump to: navigation, search

This is one of the audio codecs used in LucasArts games. Note that this document outlines the codec with respect to Grim Fandango. Finally, please understand that it's not unlikely that certain sections of this document are not 100% accurate, as they are all based on empirical data.

Use in Grim Fandango

VIMA is used in Grim's Smush animations (SNM), iMuse audio tracks, and incidentals in the game. Smush has additional headers that describe each audio frame.

Smush Audio Frame Headers

A sound frame starts with one of two headers, where each header starts with the following:

0x00|"Wave" FOURCC|4 bytes big endian
0x04|Header size  |4 bytes big endian

Variant 1

0x08|# of output PCM samples, per channel|4 bytes big endian
0x0C|VIMA data

Variant 2

0x08|0xffffffff (in all observed cases)  |4 bytes big endian
0x0C|File ID                             |4 bytes big endian
0x10|# of output PCM samples, per channel|4 bytes big endian
0x14|VIMA data

Notes

The first two fields in Variant 2 are actually not used in the decoding process. The first field is used to differentiate Variant 2 headers from Variant 1 headers. "File ID" was apparently used by the original decoders to flag certain files as being "similar" to avoid reinitializing the audio driver/buffers. This was useful in the event that a particular sound (or group of similar sounds) was played repeatedly.

VIMA Headers

A VIMA data stream starts with one of two headers, depending on the number of channels.

Mono

0x00|Step index hint|1 byte unsigned
0x01|PCM hint       |2 bytes signed big endian
0x03|Encoded data

Stereo

If the Step index hint MSB is turned on, we invert all its bits, and the file is now stereo.

0x00|Left step index hint |1 byte unsigned
0x01|Left PCM hint        |2 bytes signed big endian
0x03|Right step index hint|1 byte unsigned
0x04|Right PCM hint       |2 bytes signed big endian
0x06|Left data
0xXX|Right data

Codec

The codec itself is a slight variation of the IMA ADPCM codec, and so uses similar lookup tables. The step table is identical; the step index table is split up into several tables, which are in turn indexed into using another table. Other differences include variable sample sizes [4, 7] bits, and PCM and step index "hints" per channel. The aforementioned "hints" are used as baseline values, per channel, which each channel's processing may use in calculating outputs.

The codec also contains "keyframes" (identical in concept to those found in video files), which serve as unreferenced raw PCM samples to build up on.

A decompressed sample is 16 bits signed big endian. The reason a 32-bit signed value is used in calculation is to avoid wrap-around errors. This value is truncated to 16-bit bounds at the end of the algorithm.

Data Ordering

It's important to note that encoded data is not interleaved. The following "diagram" (as such) shows what a stereo data stream looks like, which is not much more than "Hints, Encoded Data". A mono data stream is intuitively analogous.

|Left Hints|Right Hints|Compressed Left Channel Data|Compressed Right Channel Data|

Decoding

The decoding process, per channel, looks roughly like this:

decode_main()
{
  step_index = from channel hint, signed 32-bit PCM data = from channel hint
  for each output_sample
  { 
     lookup_size = size_table[step_index]
     lookup = next lookup_size bits of input stream
     if lookup has all non-MSB bits turned on, it's a keyframe
     {
        signed 32-bit PCM data = next 2 bytes of stream
     }      
     else
     {
        if lookup has MSB turned on
        {
           turn MSB off
           diff is negative.
        }
        decode_sample()
     }
  
     step_index += step_index_tables[lookup_size - 2][lookup]
     clamp step_index to [0, 88]
  }
}
decode_sample()
{
  predict_table_index = (lookup << (7 - lookup_size)) | (step_index << 6);
  diff = predict_table[predict_table_index] + (step_table[step_index] >> (lookup_size - 1))

  if lookup is zero
  {
     diff = 0
  }

  signed 32-bit pcm data += diff negative ? -diff : diff
  clamp signed 32-bit pcm data to [-0x8000, 0x7fff]
}

Lookup Tables

The format makes use of the following lookup tables. As described above, these tables are similar in spirit and identical in purpose to those of IMA ADPCM.

Step Table

int16_t step_table[] = 
{
   7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34,
   37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
   157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494,
   544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552,
   1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026,
   4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
   11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623,
   27086, 29794, 32767
};

Sample Size Table

uint8_t size_table[] = 
{
   4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
   4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
   4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
   7, 7, 7, 7, 7
};

Step Index Tables

int8_t index_table1[] = 
{
   -1, 4, -1, 4
};
int8_t index_table2[] = 
{
   -1, -1, 2, 6, -1, -1, 2, 6
};
int8_t index_table3[] = 
{
   -1, -1, -1, -1, 1, 2, 4, 6,
   -1, -1, -1, -1, 1, 2, 4, 6
};
int8_t index_table4[] = 
{
   -1, -1, -1, -1, -1, -1, -1, -1,
    1,  1,  1,  2,  2,  4,  5,  6,
   -1, -1, -1, -1, -1, -1, -1, -1,
    1,  1,  1,  2,  2,  4,  5,  6
};
int8_t index_table5[] = 
{
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
    1,  1,  1,  1,  1,  2,  2,  2,
    2,  4,  4,  4,  5,  5,  6,  6,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
    1,  1,  1,  1,  1,  2,  2,  2,
    2,  4,  4,  4,  5,  5,  6,  6
};
int8_t index_table6[] = 
{
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
    1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  2,  2,  2,  2,  2,  2,
    2,  2,  4,  4,  4,  4,  4,  4,
    5,  5,  5,  5,  6,  6,  6,  6,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
    1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  2,  2,  2,  2,  2,  2,
    2,  2,  4,  4,  4,  4,  4,  4,
    5,  5,  5,  5,  6,  6,  6,  6
};

Step Index Index Table

This table is used to index into the above step index tables.

int8_t* step_index_tables[] = 
{
   index_table1, index_table2, index_table3,
   index_table4, index_table5, index_table6
};

VIMA Predict Table

Please see the VIMA Predict Table on a separate page.