OBD:SNDD/aif

From OniGalore
< OBD:SNDD
Revision as of 21:35, 28 June 2022 by Iritscen (talk | contribs) (actually that section doesn't exist, and it's not needed anyway)
Jump to navigation Jump to search
.WAV files          .AIF files

Outside of Oni, the IMA4 codec is used for storing audio in .aif files, as one of the formats of the AIFC standard, which itself fits into a FORM container (a.k.a. "EA IFF 85").

Inside Oni, the AIFC storage format is completely stripped down: the raw content from the SSND section (i.e., the actual IMA4 blocks) is stored in the .raw part of Oni's SNDD, and the .dat part of the SNDD is filled in with basic storage parameters from SSND and COMM (channel count and either data size or number of frames).

No AIFC format other than "ima4" seems to be supported by Oni, therefore we are only documenting "ima4".

ima4 as stored in AIFC

Below is the header of one of Oni's sounds (mono) stored as an .aif file. Note the Big Endian order.

Sndm aif.gif

Offset Type Raw Hex Value Description
0x00 char[4] 46 4F 52 4D FORM identifier for the "EA IFF 85" standard
0x04 int32 00 00 2A 90 10896 size of the file from 0x08 to the end (= size of the raw IMA4 data + 50 bytes)
0x08 char[4] 41 49 46 43 AIFC identifier for the "AIFC" format (compressed aif file)
0x0C char[4] 43 4F 4D 4D COMM identifier announcing the following aif format header (COMM stands for "common")
0x10 block[26]     COMM data (a.k.a. AIFC format header, similar to a WAVE file's "fmt"; see below)
0x2A char[4] 53 53 4E 44 SSND identifier announcing the following aif data (actual stream + stream size + 2 more parameters)
0x2E int32 00 00 2A 66 10854 size of the following SSND data (= size of the raw IMA4 data + 8 bytes)
(since SSND is the last section, this is also the remaining file size, from 0x32 to the end)
0x32 int32 00 00 00 00 0 "offset"; determines where the first sample in the data starts; typically zero
0x36 int32 00 00 00 00 0 "block size"; used in conjunction with offset for block-aligning data; typically zero

Here is the detail of the format header at 0x10:

Sndd hdm.gif

Offset Type Raw Hex Value Description
0x10 int32 00 00 00 16 22 size of the following COMM data (AIFC format settings)
0x14 int16 00 01 1 number of channels (1 = mono)
0x16 int32 00 00 01 3F 319 File:Sndd hm1.gif
0x1A int16 00 10 16 bits per sample
0x1C float80 40 0D AC... 22050 samples per second (a.k.a. "frequency" or "sample rate")

(it's an 80 bit "IEEE Standard 754" floating point number)

0x26 char[4] 69 6D 61 34 ima4 compression type

Oni's support of IMA4 is limited to what you see above: 16 bits per sample, 22050 Hz sample rate, "ima4" compression type. The number of channels can be either 1 (mono) or 2 (stereo).

At 0x3A start the IMA4 frames, each of them 34 bytes long and coding for 64 samples. Here is a view of a stereo .aif file (based on Oni's SNDDalarm_loop.aif), showing how the IMA4 frames are organized.

Sndm aif2.gif

Offset Type Raw Hex Value Description
0x00 char[4] 46 4F 52 4D FORM identifier for the "EA IFF 85" standard
0x04 int32 00 01 16 4E 71246 size of the file from 0x08 to the end (= size of the raw IMA4 data + 50 bytes)
0x08 char[4] 41 49 46 43 AIFC identifier for the "AIFC" format (compressed aif file)
0x0C char[4] 43 4F 4D 4D COMM identifier announcing the following aif format header (COMM stands for "common")
0x10 int32 00 00 00 16 22 size of the following COMM data (AIFC format settings)
0x14 int16 00 02 2 number of channels (2 = stereo)
0x16 int32 00 00 04 17 1047 File:Sndd hm1.gif
0x1A int16 00 10 16 bits per sample
0x1C float80 40 0D AC... 22050 samples per second (a.k.a. "frequency" or "sample rate")

(it's an 80 bit "IEEE Standard 754" floating point number)

0x26 char[4] 69 6D 61 34 ima4 compression type
0x2A char[4] 53 53 4E 44 SSND identifier announcing the following aif data (actual stream + stream size + 2 more parameters)
0x2E int32 00 01 16 24 71204 size of the following SSND data (= size of the raw IMA4 data + 8 bytes)
(since SSND is the last section, this is also the remaining file size, from 0x32 to the end)
0x32 int32 00 00 00 00 0 "offset"; determines where the first sample in the data starts; typically zero
0x36 int32 00 00 00 00 0 "block size"; used in conjunction with offset for block-aligning data; typically zero
0x3A block[34] 00 00 07 ... 1st L frame (thin blue outline) the frame header is 0x0000, the first two nibbles are 0x0 and 0x7
0x5C block[34] 00 00 FF ... 1st R frame (thin red outline) the frame header is 0x0000, the first two nibbles are 0xF and 0xF
0x7E block[34] 00 0D A0 ... 2nd L frame (thick blue outline) the frame header is 0x000D, the first two nibbles are 0xA and 0x0
0xA0 block[34] FF 96 9A ... 2nd R frame (thick red outline) the frame header is 0xFF96, the first two nibbles are 0x9 and 0xA

Importing from AIFC into Oni

The frames should be copied to the .raw part of the SNDD. The size of the .raw data will be consistent with the size of the .aif file (at 0x04 above, right after FORM) or with the size of the SNDD section (at 0x2E above, right after SNDD), as well as with the number of frames (at 0x16) and channel count (at 0x14).

  • For the above mono example, there are 319 frames of 34 bytes each, or 10846 bytes of .raw data (8 bytes less than the 0x2A66 size at 0x2E and 50 bytes less than the 0x2A90 size at 0x04).
  • For the stereo example, there are 1047 frames of 68 bytes each (a stereo frame is the combination of a Left frame and a Right frame), or 71196 bytes (8 bytes less than 0x11624, 50 bytes less than 0x1164E).

The SNDD file in .dat receives the offset to the .raw data as well as several parameters that depend on the engine version.

Filling in Mac SNDD

Here is how the SNDD instance should look (in .dat) for the above mono example:

Sndd alm.gif

  • The "compressed" flag is set (because IMA4 is a compressed format; however, if the flag isn't set, the Mac engine will process the data as IMA4 anyway).
  • The "stereo" flag is not set (because the sound is mono).
  • The .raw data size is set to 10846=0x2A5E (319 frames of 34 bytes each, as detailed above). Note the Little Endian order.
  • The offset to the .raw data is 0x1B100 in this example (Little Endian as well).
  • The duration is computed from the number of samples: 319 frames of 64 samples each is 20416 samples, which at 22.05 kHz corresponds to 0.92589 seconds, or 55.5537 game ticks, truncated to 55=0x37.
  • The padding at the end is "DEAD" because this is a Vanilla .dat file (OniSplit pads with zeroes instead).

Here is how the SNDD instance looks (in an .oni file) for the above stereo example:

Sndd alm2.gif

Same as above, except:

  • The stereo flag is set; with the compressed flag also set, the bitset's value is 0x00000003.
  • The level ID is blank and the padding is 0000 instead of DEAD, because this is an .oni file.
  • The offset to the .raw data is 0x20 in this example (also because this is an .oni file).
  • The .raw data size is set to 71196 (1047 frames of 68 bytes each, as detailed above).
  • The duration is computed from the number of stereo samples: 1047 frames of 64 samples each is 67008 samples, which at 22.05 kHz corresponds to 3.0389 seconds, or 182.33 game ticks, truncated to 182=0xB6.

Filling in PC retail SNDD

Here is how the PC retail SNDD instance will look (in an .oni file) for the above stereo example:

Sndd alm4.gif

All the fields can be zeroed out or filled with garbage, except:

  • The "flags" bitset must be set to 0x00000004, or anything containing the "4" flag ("1" and "2" have no effect, and "8" is overridden by "4").
  • The channel count (2 in this case).
  • The "block size" field is used to store (somewhat counterintuitively) the number of frames (same as in the .aif file, but with Little Endian order).
  • The "duration" field, with the same value as above (the sound lasts 3.0389 seconds, or 182.33 game ticks, truncated to 182=0xB6).
  • The offset to the .raw data (the size of the .raw data is not required in this case; the number of frames is used instead).

Exporting from Oni to AIFC

From Mac SNDD

Start by collecting the data from the Mac SNDD. The size of the .raw data is explicitly stored at 0x10.

The channel count is either 2 or 1 depending on whether the "2" bit of the flags (at 0x08) is set: if the bitset's value is 0x00000001 then the sound is mono, if the bitset's value is 0x00000003 then the sound is stereo. Knowing the channel count, you deduce the frame count (number of 34- or 68- byte frames) by dividing the .raw size by either 34 (mono) or 68 (stereo); it should divide evenly.

Next you create an AIFC file based of the template described ABOVE and adjust it as follows (don't forget about Big Endian!).

  • The SNDD's .raw data must be copied into the SSND section of the AIFC file, starting at 0x3A. Copy as-is, no byte swapping required.
  • At 0x04, set the remaining file size to the size of the copied .raw data + 50 (Big Endian order!).
  • At 0x2E, set the remaining SSND size to the size of the copied .raw data + 8 (Big Endian order!).
  • At 0x14, fill in the channel count that you deduced from the SNDD's flags at 0x08 (Big Endian!).
  • At 0x16, fill in the number of frames that you canculated from the .raw data size (Big Endian!).

That is all. Save the file and it should work.

From PC retail SNDD

In Vanilla Oni (PC retail) there are no SNDDs that use IMA4 compression, so the situation described here is not very likely. If you need to do it anyway, then you essentially need to look up two things in the .dat part of the SNDD: the channel count at 0x0E and the frame count at 0x18. The frame count multiplied by 34 and by the channel count gives you the size of the .raw part (unlike in all the other situations, the size will not necessarily be stored explicitly in the SNDD, because the engine uses the frame count instead), which allows you to retrieve the IMA4 stream from the .raw file.

Next you create an AIFC file based of the template described ABOVE and adjust it as follows (don't forget about Big Endian!).

  • The SNDD's .raw data must be copied into the SSND section of the AIFC file, starting at 0x3A. Copy as-is, no byte swapping required.
  • At 0x04, set the remaining file size to the size of the copied .raw data + 50 (Big Endian order!).
  • At 0x2E, set the remaining SSND size to the size of the copied .raw data + 8 (Big Endian order!).
  • At 0x14, fill in the channel count (same as 0x0E in the SNDD, but swapped to Big Endian).
  • At 0x16, fill in the number of frames (same as 0x18 in the SNDD, but swapped to Big Endian).

That is all. Save the file and it should work.

Back to Mac SNDD (.dat part)