19,577
edits
m (→IMA ADPCM decoding ("4" flag): -_-) |
m (replaced formula GIFs with Math markup; replaced nowiki tags around equals signs with new {{=}} magic word) |
||
(18 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
{{OBD_File_Header | type=SNDD | prev=QTNA | next=StNA | name=Sound Data | family= | {{OBD_File_Header | type=SNDD | prev=QTNA | next=StNA | name=Sound Data | family=General | align=center}} | ||
:''For metadata instances used to group sounds together, randomize them, adjust their volume or frequency, etc, see [[OSBD]] and its subtypes: [[OSAm]], [[OSIm]] and [[OSGr]].'' | :''For metadata instances used to group sounds together, randomize them, adjust their volume or frequency, etc, see [[OSBD]] and its subtypes: [[OSAm]], [[OSIm]] and [[OSGr]].'' | ||
SNDD instances is where Oni stores sound data. In Vanilla Oni game data, sounds are either mono or stereo waveforms (with sampling frequencies of either 22.05 kHz or 44.1 kHz), compressed to save on storage space. Both the PC and Mac versions use a form of [[wp: | SNDD instances is where Oni stores sound data. In Vanilla Oni game data, sounds are either mono or stereo waveforms (with sampling frequencies of either 22.05 kHz or 44.1 kHz), compressed to save on storage space. Both the PC and Mac versions use a form of [[wp:Adaptive differential pulse-code modulation|ADPCM]] compression (Adaptive Differential Pulse-Code Modulation), where 16-bit sound samples are encoded as 4-bit "nibbles" (resulting roughly in a 4:1 compression ratio as compared to uncompressed 16-bit [[wp:Pulse-code modulation|PCM]]). | ||
*On PC (both retail and demo), sounds are encoded using Microsoft's ADPCM codec (implemented in [[wp:FFmpeg|FFmpeg]] as '''adpcm_ms'''). See [https://wiki.multimedia.cx/index.php/Microsoft_ADPCM HERE] for a quick description. | *On PC (both retail and demo), sounds are encoded using Microsoft's ADPCM codec (implemented in [[wp:FFmpeg|FFmpeg]] as '''adpcm_ms'''). See [https://wiki.multimedia.cx/index.php/Microsoft_ADPCM HERE] for a quick description. | ||
*On Mac, sounds are encoded using QuickTime's IMA4 codec (implemented in | *On Mac, sounds are encoded using QuickTime's IMA4 codec (implemented in FFmpeg as '''adpcm_ima_qt'''). See [https://wiki.multimedia.cx/index.php/Apple_QuickTime_IMA_ADPCM HERE] for a quick description. | ||
*On PS2, sounds are encoded using Sony's VAG codec (a.k.a. Sony PSX ADPCM, or '''adpcm_psx''' in FFmpeg). See [https://www.psdevwiki.com/ps3/Multimedia_Formats_and_Tools#VAG HERE] for a quick description. | |||
As a unique feature of Oni game data, SNDD files have a significantly different structure depending on the engine version. For PC retail (.dat/.raw storage, no .sep files), the SNDD files are larger and include a 50-byte chunk of data that is equivalent to the "fmt " chunk of a WAVE file. For the other two versions (PC demo and Mac, .dat/.raw/.sep storage), this 50-byte block is missing. It turns out that the extra format data allows the PC retail to support both MS ADPCM and IMA4, as well as uncompressed PCM, whereas PC demo and Mac engines only support MS ADPCM and IMA4, respectively. (It has not been confirmed whether the PC retail engine supports other WAVE formats beyond PCM and MS ADPCM, such as Mu-Law or A-Law PCM, IEEE float PCM, etc.) The PS2 engine uses the same short data header as for PC demo and Mac, but the waveform is stored as VAG (a.k.a. PSX ADPCM) and resides in a completely separate SOUNDS folder, accessed through an additional layer of indexation beyond the usual .dat/.raw./.sep logic (not unlike PS2 TXMPs which rely on color palettes stored in additional level#_palette.pal files). | |||
---- | ---- | ||
For clarity, the simpler and more straightforward SNDDs of PC demo and Mac are documented first, followed by the more complex and versatile PC retail SNDDs. | For clarity, the simpler and more straightforward SNDDs of PC demo and Mac are documented first, followed by the more complex and versatile PC retail SNDDs. | ||
:(Historically, though, the PC retail implementation is older, and the PC demo and Mac versions were trimmed-down iterations of PC retail.) | :(Historically, though, the PC retail implementation is older, and the PC demo and Mac versions were trimmed-down iterations of PC retail.) | ||
The exotic PS2 storage is documented last, after which we list some legacy tips (for manual sound conversion) and known issues/limitations, as well as the current sound capabilities of OniX and OniSplit. | |||
==Mac and PC demo== | ==Mac and PC demo== | ||
The below example was taken from Mac Oni. In PC demo the file would look the same, except for possibly different res_id (at 0x00) and smaller raw data size (at 0x10). | The below example was taken from Mac Oni. In PC demo the file would look the same, except for possibly different res_id (at 0x00) and smaller raw data size (at 0x10). | ||
[[ | [[Image:sndd_alm.gif]] | ||
{{Table}} | {{Table}} | ||
Line 40: | Line 44: | ||
:The bottom line is that, both for PC demo SNDDs and for Mac SNDDs, the "1" flag should always be set. | :The bottom line is that, both for PC demo SNDDs and for Mac SNDDs, the "1" flag should always be set. | ||
The compressed storage of .raw data is described in the following two sections. | The compressed storage of .raw data is described in the following two sections. | ||
===IMA4 ADPCM .raw data (Mac)=== | ===IMA4 ADPCM .raw data (Mac)=== | ||
''For an overview of the IMA ADPCM algorithm and IMA4 header (if interested), see [https://wiki.multimedia.cx/index.php/Apple_QuickTime_IMA_ADPCM HERE]. For an actual implementation example, see | ''For an overview of the IMA ADPCM algorithm and IMA4 header (if interested), see [https://wiki.multimedia.cx/index.php/Apple_QuickTime_IMA_ADPCM HERE]. For an actual implementation example, see FFmpeg.'' | ||
:The IMA4 ADPCM stream data (stored in the .raw file) consists of 34-byte blocks (in the case of stereo, there is an even number of such blocks, with Left and Right blocks interleaved). | :The IMA4 ADPCM stream data (stored in the .raw file) consists of 34-byte blocks (in the case of stereo, there is an even number of such blocks, with Left and Right blocks interleaved). | ||
:The first two bytes of each block form a header that sets the initial predictor (upper 9 bits) and step (lower 7 bits) for decoding the block's samples. Typically they are used only for the first block, or for sudden changes of the waveform's value range. | :The first two bytes of each block form a header that sets the initial predictor (upper 9 bits) and step (lower 7 bits) for decoding the block's samples. Typically they are used only for the first block, or for sudden changes of the waveform's value range. | ||
Line 94: | Line 99: | ||
===MS ADPCM .raw data (PC demo)=== | ===MS ADPCM .raw data (PC demo)=== | ||
''For a detailed overview of the ADPCM algorithm (if interested), see [https://wiki.multimedia.cx/index.php/Microsoft_ADPCM HERE]. For an actual implementation example, see | ''For a detailed overview of the ADPCM algorithm (if interested), see [https://wiki.multimedia.cx/index.php/Microsoft_ADPCM HERE]. For an actual implementation example, see FFmpeg.'' | ||
:The MS ADPCM stream data (stored in .raw) consists of 512- or 1024-byte blocks (512 bytes for 22.05 kHz mono, 1024 bytes for 22.05 kHz stereo) | :The MS ADPCM stream data (stored in .raw) consists of 512- or 1024-byte blocks (512 bytes for 22.05 kHz mono, 1024 bytes for 22.05 kHz stereo) | ||
:Each block starts with a 7- or 14-byte header (7 bytes for mono, 14 bytes for stereo), which includes the 16-bit values of the block's first two samples. | :Each block starts with a 7- or 14-byte header (7 bytes for mono, 14 bytes for stereo), which includes the 16-bit values of the block's first two samples. | ||
Line 103: | Line 108: | ||
==PC retail== | ==PC retail== | ||
Below is the .dat file part used in the PC retail version. | Below is the .dat file part used in the PC retail version. | ||
[[ | [[Image:sndd_all.gif]] | ||
{{Table}} | {{Table}} | ||
Line 226: | Line 134: | ||
:The '''channel count''' is specified in the 50-byte header if said header is enabled, otherwise it is inferred from [[OBD:OSBD/OSGr|OSGr]]. | :The '''channel count''' is specified in the 50-byte header if said header is enabled, otherwise it is inferred from [[OBD:OSBD/OSGr|OSGr]]. | ||
:As for '''compression''', PC retail actually has ''three'' primary compression modes, commanded by the flag values. Only the first is used in Vanilla Oni. | :As for '''compression''', PC retail actually has ''three'' primary compression modes, commanded by the flag values. Only the first is used in Vanilla Oni. | ||
===WAVE-like format header ("8" flag)=== | ===WAVE-like format header ("8" flag)=== | ||
If the "8" flag of the SNDD (at 0x08) is ON and the "4" flag is OFF (as is always the case in Vanilla Oni), the 50-byte block is interpreted as a standard "fmt " chunk that you find in WAVE files (see [[/wav|HERE]] for details). | If the "8" flag of the SNDD (at 0x08) is ON and the "4" flag is OFF (as is always the case in Vanilla Oni), the 50-byte block is interpreted as a standard "fmt " chunk that you find in WAVE files (see [[/wav|HERE]] for details). | ||
[[ | [[Image:sndd_hd.gif]] | ||
{{Table}} | {{Table}} | ||
{{OBDth}} | {{OBDth}} | ||
{{OBDtr| 0x0C | int16 |FFFFC8| 02 00 | 2 | format ID (2 | {{OBDtr| 0x0C | int16 |FFFFC8| 02 00 | 2 | format ID (2 {{=}} MS ADPCM format) | ||
:<small>'''N.B.''' At the time of writing, only "1" (linear PCM) and "2" (MS ADPCM) are known to work in Oni; in Vanilla Oni, only MS ADPCM is ever used.</small>}} | :<small>'''N.B.''' At the time of writing, only "1" (linear PCM) and "2" (MS ADPCM) are known to work in Oni; in Vanilla Oni, only MS ADPCM is ever used.</small>}} | ||
{{OBDtr| 0x0E | int16 |C8FFC8| 01 00 | 1 | number of channels (1 | {{OBDtr| 0x0E | int16 |C8FFC8| 01 00 | 1 | number of channels (1 {{=}} mono) | ||
:<small>'''N.B.''' Both PCM and ADPCM support only mono and stereo sounds, i.e., 1 or 2 channels.</small>}} | :<small>'''N.B.''' Both PCM and ADPCM support only mono and stereo sounds, i.e., 1 or 2 channels.</small>}} | ||
{{OBDtr| 0x10 | int32 |C8FFFF| 22 56 00 00 | 22050 | sample rate in Hz (samples per second), a.k.a. "sampling frequency" }} | {{OBDtr| 0x10 | int32 |C8FFFF| 22 56 00 00 | 22050 | sample rate in Hz (samples per second), a.k.a. "sampling frequency" }} | ||
{{OBDtr| 0x14 | int32 |FFC8FF| 93 2B 00 00 | 11155 | | {{OBDtr| 0x14 | int32 |FFC8FF| 93 2B 00 00 | 11155 | ADPCM average data rate: <math>\frac{\text{samples per second}*\text{block alignment}}{\text{samples per block}}</math> }} | ||
:<small>'''N.B.''' For PCM, the data rate is simply | :<small>'''N.B.''' For PCM, the data rate is simply ''samples per second*block alignment'', seeing as each sample gets its own block.</small> | ||
:<small>'''N.B.''' For ADPCM, the average data rate is based on whole ADPCM blocks (not accounting for how Oni truncates the .raw data).</small>}} | :<small>'''N.B.''' For ADPCM, the average data rate is based on whole ADPCM blocks (not accounting for how Oni truncates the .raw data).</small>}} | ||
{{OBDtr| 0x18 | int16 |FFC800| 02 00 | 512 | block alignment a.k.a "block size", in bytes | {{OBDtr| 0x18 | int16 |FFC800| 02 00 | 512 | block alignment a.k.a. "block size", in bytes | ||
:<small>'''N.B.''' The block size is trivially 2 bytes for PCM mono (one 16-bit sample) and 4 bytes for PCM stereo (Left and Right 16-bit samples).</small> | :<small>'''N.B.''' The block size is trivially 2 bytes for PCM mono (one 16-bit sample) and 4 bytes for PCM stereo (Left and Right 16-bit samples).</small> | ||
:<small>'''N.B.''' For ADPCM, Oni's Vanilla data always uses 512 bytes per channel for 22050 Hz waveforms, and 1024 bytes for 44.1 kHz mono (see below).}} | :<small>'''N.B.''' For ADPCM, Oni's Vanilla data always uses 512 bytes per channel for 22050 Hz waveforms, and 1024 bytes for 44.1 kHz mono (see below).}} | ||
Line 247: | Line 156: | ||
{{OBDtrBK|1=Special extended ADPCM wav format header (black outline); fully ignored if the format ID is 1 }} | {{OBDtrBK|1=Special extended ADPCM wav format header (black outline); fully ignored if the format ID is 1 }} | ||
{{OBDtr| 0x1C | int16 |C87C64| 20 00 | 32 | size of the extra ADPCM parameters, in bytes; typically always 32 }} | {{OBDtr| 0x1C | int16 |C87C64| 20 00 | 32 | size of the extra ADPCM parameters, in bytes; typically always 32 }} | ||
{{OBDtr| 0x1E | int16 |B0C3D4| F4 03 | 1012 | | {{OBDtr| 0x1E | int16 |B0C3D4| F4 03 | 1012 | samples per block: <math>\dfrac{(\text{block alignment}-7*\text{number of channels})*8}{\text{bits per sample}*\text{number of channels}}+2</math> }} | ||
{{OBDtr| 0x20 | int16 |E7CEA5| 07 00 | 7 | number of the following coefficient pairs; always 7 in practice }} | {{OBDtr| 0x20 | int16 |E7CEA5| 07 00 | 7 | number of the following coefficient pairs; always 7 in practice }} | ||
|-align=center valign=top | |-align=center valign=top | ||
| 0x22 || int16-16 || bgcolor="#FFDDDD" | 00 01 00 00 || 256, 0 || rowspan=7 align=left | The coefficient pairs themselves (always the same in practice).<br> | | 0x22 || int16-16 || bgcolor="#FFDDDD" | 00 01 00 00 || 256, 0 || rowspan=7 align=left | The coefficient pairs themselves (always the same in practice).<br><math>\begin{array}{|c|c||c|} \text{coefficient set} & \text{coefficient 1} & \text{coefficient 2} \\ | ||
\hline | |||
0 & 256 & 0\\ | |||
1 & 512 & -256\\ | |||
2 & 0 & 0\\ | |||
3 & 192 & 64\\ | |||
4 & 240 & 0\\ | |||
5 & 460 & -208\\ | |||
6 & 392 & -232 | |||
\end{array} </math> | |||
|-align=center valign=top | |-align=center valign=top | ||
| 0x26 || int16-16 || bgcolor="#FFDDDD" | 00 02 00 FF || 512, -256 | | 0x26 || int16-16 || bgcolor="#FFDDDD" | 00 02 00 FF || 512, -256 | ||
Line 349: | Line 267: | ||
===IMA ADPCM decoding ("4" flag)=== | ===IMA ADPCM decoding ("4" flag)=== | ||
If the "4" flag of the SNDD (at 0x08) is ON, then (regardless of the " | If the "4" flag of the SNDD (at 0x08) is ON, then (regardless of the "8" flag), the .raw data will be interpreted as IMA ADPCM blocks, and the 50-byte format block will be mostly ignored, as well as the ".raw data size" field at 0x40. | ||
Here is what the .dat part of the above '''SNDDcomguy_dth2''' would have looked like in IMA4 mode: | Here is what the .dat part of the above '''SNDDcomguy_dth2''' would have looked like in IMA4 mode: | ||
Line 456: | Line 374: | ||
:It is possible that the "1" and "2" flags used to affect playback in raw PCM mode (something about swapping the .raw data to allow both for Little Endian and Big Endian PCM samples), but currently they do not seem to have any effect. | :It is possible that the "1" and "2" flags used to affect playback in raw PCM mode (something about swapping the .raw data to allow both for Little Endian and Big Endian PCM samples), but currently they do not seem to have any effect. | ||
===raw | ==PS2 implementation== | ||
The PS2 implementation has an unorthodox approach to raw data when it comes to SNDDs. Here are the .dat parts of the SNDDs in a PS2 level0_Final.dat file. | |||
{|cellpadding=3 cellspacing=0 style="line-height:13px" | |||
-- | {{HexRow|0xD5EA0| | ||
|01|5B|0E|00|01|00|00|00|00|00|00|00|9E|01|'''''00'''''|'''''00'''''| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|00|00|00|00|FF|FF|FF|FF|C8|C8|C8|C8|FF|FF|FF|FF| | |||
|00|00|00|00|00|00|00|00|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
{{HexRow|0xD5EB0| | |||
|20|F7|00|00|'''''40'''''|'''''30'''''|'''''76'''''|'''''13'''''|AD|DE|AD|DE|AD|DE|AD|DE| | |||
|C8|C8|C8|C8|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
{{HexRow|0xD5EC0| | |||
|01|5C|0E|00|01|00|00|00|00|00|00|00|9E|01|'''''01'''''|'''''00'''''| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|00|00|00|00|FF|FF|FF|FF|C8|C8|C8|C8|FF|FF|FF|FF| | |||
|00|00|00|00|00|00|00|00|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
{{HexRow|0xD5ED0| | |||
|A0|F6|00|00|'''''C0'''''|'''''37'''''|'''''77'''''|'''''13'''''|AD|DE|AD|DE|AD|DE|AD|DE| | |||
|C8|C8|C8|C8|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
{{HexRow|0xD5EE0| | |||
|01|5D|0E|00|01|00|00|00|00|00|00|00|8F|02|'''''02'''''|'''''00'''''| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|00|00|00|00|FF|FF|FF|FF|C8|C8|C8|C8|FF|FF|FF|FF| | |||
|00|00|00|00|00|00|00|00|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
{{HexRow|0xD5EF0| | |||
|40|86|01|00|'''''C0'''''|'''''2E'''''|'''''78'''''|'''''13'''''|AD|DE|AD|DE|AD|DE|AD|DE| | |||
|C8|C8|C8|C8|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
{{HexRow|0xD5F00| | |||
|01|5E|0E|00|01|00|00|00|00|00|00|00|91|02|'''''03'''''|'''''00'''''| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|00|00|00|00|FF|FF|FF|FF|C8|C8|C8|C8|FF|FF|FF|FF| | |||
|00|00|00|00|00|00|00|00|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
{{HexRow|0xD5F10| | |||
|40|87|01|00|'''''40'''''|'''''B5'''''|'''''79'''''|'''''13'''''|AD|DE|AD|DE|AD|DE|AD|DE| | |||
|C8|C8|C8|C8|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
{{HexRow|0xD5F20| | |||
|01|5F|0E|00|01|00|00|00|00|00|00|00|3C|01|'''''04'''''|'''''00'''''| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|00|00|00|00|FF|FF|FF|FF|C8|C8|C8|C8|FF|FF|FF|FF| | |||
|00|00|00|00|00|00|00|00|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
{{HexRow|0xD5F30| | |||
|60|BC|00|00|'''''00'''''|'''''0A'''''|'''''75'''''|'''''13'''''|AD|DE|AD|DE|AD|DE|AD|DE| | |||
|C8|C8|C8|C8|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|FF|FF|FF|FF|FF|FF|FF|FF|C8|C8|C8|C8|C8|C8|C8|C8| | |||
|C8|C8|C8|C8|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF|FF| | |||
|°°°°°°°°°°°°°°°° | |||
}} | |||
|} | |||
(Yes, the PS2 retail level0_Final.dat only has those five sounds. All the other sounds (weapons, particles, impacts, footsteps, etc) are stored per-chapter, which causes a lot of duplicates but supposedly lightens the memory usage for a given level.) | |||
The layout is similar to the PC demo and Mac SNDDs described above - and indeed the SNDD template checksum is the same for PC demo, Mac and PS2, implying that the data structure in the .dat is the same. However, there are two major novelties/anomalies (apart from all the music being mono), emphasized above with '''''bold italic'''''. First, the five .raw offsets at the end of each SNDD are obviously not pointers into level0_Final.raw or level0_Final.sep (the .raw's size is 4 MB, the .sep's 6MB, and the pointers are in the 311 MB range, possibly pointing into a memory region where the sounds will be stored at runtime). Second, the 2-byte padding field between the duration and the .raw storage size is obviously not blank here; rather, it is an index into a file called SOUNDS\LEVEL0\SOUND.DAT, which looks like this: | |||
0x00: '''''00 00 20 F7 00 00 00 00 00 00''''' 01 00 A0 F6 00 00 | |||
0x10: 20 F7 00 00 '''''02 00 40 86 01 00 C0 ED 01 00''''' 03 00 | |||
0x20: 40 87 01 00 00 74 03 00 '''''04 00 60 BC 00 00 40 FB''''' | |||
0x30: '''''04 00''''' FE FF | |||
These are 5 blocks of 10 bytes each, followed by the two bytes FE FF which signal the end of the file. For each sound there is a 2-byte index, then a 4-byte data size (including padding), then the offset at which the data is stored in the SOUNDS\LEVEL0\SOUND.SEP file. The .SEP data for each sound consists of 32 blank bytes followed by a large number of VAG packets (16 bytes each), the final VAG packet having a terminating bit set. At the very end of the .SEP file is a terminating pair of bytes, FE FF, same as in the .DAT file. The file SOUNDS\LEVEL0\SOUND.RAW exists in the same folder, but has no data except for the two bytes FE FF. | |||
For all the levels other than LEVEL0 (i.e., game chapters), some of the sounds are stored in SOUNDS\LEVEL#\SOUND.RAW (the size is about 1 MB for all chapters). RAW storage is indicated by a zero .SEP offset in the corresponding block of the SOUNDS\LEVEL#\SOUND.DAT file (note, however, that the first sound in SOUND.SEP also has a zero offset). As an example, here is a fragment of SOUNDS\LEVEL1\SOUND.DAT featuring the first .RAW-resident sounds. | |||
0x690: '''''A8 00 70 3B 02 00 22 B2 83 00''''' A9 00 50 98 00 00 | |||
0x6A0: 92 ED 85 00 '''''AA 00 20 80 00 00 E2 85 86 00''''' AB 00 | |||
0x6B0: F0 F2 00 00 02 06 87 00 '''''AC 00 10 2D 00 00 00 00''''' | |||
0x6C0: '''''00 00''''' AD 00 C0 1B 00 00 00 00 00 00 '''''AE 00 40 0B''''' | |||
0x6D0: '''''00 00 00 00 00 00''''' AF 00 F0 11 00 00 00 00 00 00 | |||
0x6E0: '''''B0 00 20 24 00 00 F2 F8 87 00''''' B1 00 50 25 00 00 | |||
0x6F0: 12 1D 88 00 '''''B2 00 10 58 00 00 62 42 88 00''''' B3 00 | |||
0x700: 20 1F 00 00 72 9A 88 00 '''''B4 00 A0 26 00 00 00 00''''' | |||
0x710: '''''00 00''''' B5 00 F0 0E 00 00 00 00 00 00 '''''B6 00 B0 09''''' | |||
0x720: '''''00 00 00 00 00 00''''' B7 00 B0 0B 00 00 00 00 00 00 | |||
Here the .SEP offset field is zero for entries 0xAC, 0xAD, 0xAE and 0xAF, then non-zero for the next four entries, and zero again for the following four. The start of the SOUNDS\LEVEL1\SOUND.RAW file looks as follows | |||
0x00: '''''AC 00''''' 10 2D 00 00 '''''00 00 00 00 00 00 00 00 00 00''''' | |||
0x10: '''''00 00 00 00 00 00''''' 00 00 00 00 00 00 00 00 00 00 | |||
0x20: 00 00 00 00 00 00 '''''1A 00 24 00 00 01 10 00 02 10''''' | |||
0x30: '''''0F 00 11 1F 1F 1E''''' 1A 00 01 1F 10 0F 11 21 3E 10 | |||
0x40: 20 0F 20 41 D2 E3 | |||
Here AC 00 is the 2-byte index of the sound (the same as in the SOUNDS\LEVEL1\SOUND.DAT and in the corresponding SNDD in level1_Final.dat), then there is the 4-byte data size (also the same as announced in the .dat and .DAT), followed by the same data as in the .SEP (32 zero bytes, then some 16-byte VAG packets, the last packet having a terminating bit set). At the very end of the .RAW file is a terminating pair of bytes, FE FF. | |||
:'''N.B.''' It appears that SOUND.RAW is loaded in its entirely when a level starts, whereas sound data from a level's SOUND.SEP is (re)loaded on-demand. Accordingly, SOUND.RAW typically contains short recurrent sounds (gunshots, impacts, footsteps, hurt sounds, etc). Permanent storage is not decided based on size alone, though: for example, the rather long SNDDheliflyby2 (6 seconds) is stored in SOUND.RAW, whereas the much shorter SNCCconsole-locked (1.5 seconds long) is stored in SOUND.SEP. | |||
:'''N.B.''' A level's SOUND.DAT and SOUND.SEP always start with the same 5 entries (music segments) as in LEVEL0/SOUND.DAT and LEVEL0/SOUND.SEP, including the terminating code FE FF at 0x5B7A0. Those segments, indexed as 0 through 4 (same as in LEVEL0/SOUND.DAT), do not have a corresponding SNDD in the chapter's level#_Final (i.e., the 16-bit indices of the SNDD instances in level#_Final always start at 5 except for level0_Final, which only has those five SNDDs). It would seem that the sound indices listed in SOUND.DAT need to be unique across all the loaded level files. It would also seem that the duplicated storage of level0 music in all 14 chapters is highly suboptimal, using up 5 MB of disk space, and that the LEVEL0/SOUND.* files are redundant. | |||
:'''N.B.''' Because of how the contents of LEVEL0/SOUND.SEP is included at the start of each level's LEVEL#/SOUND.SEP, complete with the terminating FE FF code, the data for the following, level-specific sounds is shifted by two bytes, i.e., from then on the starting offsets of each sound and VAG packet look like 0x.......2 rather than 0x.......0 (this is reflected by the offsets in LEVEL#/SOUND.DAT). This terminating code in the middle of the .SEP file probably serves no purpose whatsoever (the reading routines stop whenever they encounter a terminating bit in a VAG packet). | |||
==Exporting and importing tips== | ==Exporting and importing tips== | ||
Line 480: | Line 489: | ||
:The PC retail engine formally allows for an arbitrary sample rate (and a corresponding data rate) to be specified in the WAVfmt header, but actually the engine interprets all waveforms as 22.05 kHz. | :The PC retail engine formally allows for an arbitrary sample rate (and a corresponding data rate) to be specified in the WAVfmt header, but actually the engine interprets all waveforms as 22.05 kHz. | ||
:*If the WAVEfmt header is enabled and specifies MS ADPCM encoding (format ID 2), then the sample rate and data rate are completely ignored (you can fill those fields with zeroes or garbage, and it will still work). | :*If the WAVEfmt header is enabled and specifies MS ADPCM encoding (format ID 2), then the sample rate and data rate are completely ignored (you can fill those fields with zeroes or garbage, and it will still work). | ||
:*If the WAVEfmt header is enabled and specifies PCM storage (format ID 1), then fully arbitrary (inconsistent) sample rate and data rate will cause playback glitches or interruption, whereas mutually consistent pairs ("data rate" equal to "sample rate"x"block size") are | :*If the WAVEfmt header is enabled and specifies PCM storage (format ID 1), then fully arbitrary (inconsistent) sample rate and data rate will cause playback glitches or interruption, whereas mutually consistent pairs ("data rate" equal to "sample rate"x"block size") are eventually ignored. In other words, you have to specify a valid sample rate and data rate, but the engine will end up using 22.05 kHz anyway. | ||
:*If the IMA4 header is enabled (overriding WAVEfmt), then the header only specifies the channel count and the "number of packets", and the sample/data rates are again completely ignored. | :*If the IMA4 header is enabled (overriding WAVEfmt), then the header only specifies the channel count and the "number of packets", and the sample/data rates are again completely ignored. | ||
Line 486: | Line 495: | ||
:PCM playback is only known to work in PC retail builds, either with IMA4 and WAVEfmt headers disabled (the stream is simply copied to the output buffer, as Little-Endian 16-bit linear PCM, with the channel count specified at OSGr level) or with the WAVEfmt header enabled (in which case the stream can have a custom bit depth and channel count). | :PCM playback is only known to work in PC retail builds, either with IMA4 and WAVEfmt headers disabled (the stream is simply copied to the output buffer, as Little-Endian 16-bit linear PCM, with the channel count specified at OSGr level) or with the WAVEfmt header enabled (in which case the stream can have a custom bit depth and channel count). | ||
:In the case of PC demo, the PCM playback fails (the engine identifies the stream as already uncompressed and skips the initialization of ACM headers, but then proceeds with decompression anyway and stops because of zero input size). This causes no problems for impulse sounds (other than silence), but results in a crash for looping permutations (the same playback keeps failing over and over). | :In the case of PC demo, the PCM playback fails (the engine identifies the stream as already uncompressed and skips the initialization of ACM headers, but then proceeds with decompression anyway and stops because of zero input size). This causes no problems for impulse sounds (other than silence), but results in a crash for looping permutations (the same playback keeps failing over and over). | ||
:On the Mac, the stream is interpreted as IMA4 regardless of the 0x00000001 flag, therefore if you put PCM data in the .raw, it will play back as noise. | :On the Mac, the stream is interpreted as IMA4 regardless of the 0x00000001 flag, therefore if you put PCM data in the .raw, it will play back as noise. | ||
==Known data issues== | ==Known data issues== | ||
;PC SNDD data | ;PC SNDD data | ||
:The MS ADPCM data used by Vanilla Oni on Windows (both retail and demo) is somewhat coarser (lossier) than the IMA4 ADPCM used on Mac. This is because of the much larger block size, and how the same predictor must be used for the whole block: whenever a block spans both high- and low-amplitude samples, the predictor adapts to the higher amplitudes, and the low-amplitude resolution is lost. | :The MS ADPCM data used by Vanilla Oni on Windows (both retail and demo) is somewhat coarser (lossier) than the IMA4 ADPCM used on Mac. This is because of the much larger block size, and how the same predictor must be used for the whole block: whenever a block spans both high- and low-amplitude samples, the predictor adapts to the higher amplitudes, and the low-amplitude resolution is lost. | ||
:As a minor issue, the lack of an exact sample count (similar to a WAVE's "fact") makes it impossible to specify an odd number of samples for a mono block (because there is no way to tell if the last byte's second nibble counts or not). It is also impossible to have only one sample in the last block, be it for mono stereo (because the block header reads as two samples by default). | :As a minor issue, the lack of an exact sample count (similar to a WAVE's "fact") makes it impossible to specify an odd number of samples for a mono block (because there is no way to tell if the last byte's second nibble counts as data or not). It is also impossible to have only one sample in the last block, be it for mono or stereo (because the block header reads as two samples by default). | ||
;Mac SNDD data | ;Mac SNDD data | ||
Line 516: | Line 526: | ||
Alternatively, both the flags and the uint at 0x0E can be used to specify a custom sample rate and/or block size (either as fully custom values or as power-of-two multiplicative factors), without the need for a detailed WAVEfmt header. Still, it is probably easiest to either adhere to the standard parameters (without too many extra flags) or go fully custom and read all the parameters from a standard-compliant WAVEfmt header. | Alternatively, both the flags and the uint at 0x0E can be used to specify a custom sample rate and/or block size (either as fully custom values or as power-of-two multiplicative factors), without the need for a detailed WAVEfmt header. Still, it is probably easiest to either adhere to the standard parameters (without too many extra flags) or go fully custom and read all the parameters from a standard-compliant WAVEfmt header. | ||
==Notes== | ==Notes== | ||
<references/> | <references/> | ||
---- | ---- | ||
{{OBD_File_Footer | type=SNDD | prev=QTNA | next=StNA | name=Sound Data | family= | {{OBD_File_Footer | type=SNDD | prev=QTNA | next=StNA | name=Sound Data | family=General}} | ||
{{OBD}} | {{OBD}} |