AGSC (File Format): Difference between revisions
→Normal / Drum Page Entry
>MrSinistar (→Tables) |
imported>Jackoalan |
||
(54 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
'''AGSC''' is the sound effect format for Metroid Prime and Metroid Prime 2: Echoes. Each AGSC file contains a group of sound effects. The first two Metroid Prime games utilize the MusyX audio engine created by Factor5 and as a result, AGSC files are essentially just embedded MusyX files. | '''AGSC''' is the sound effect format for Metroid Prime and Metroid Prime 2: Echoes. Each AGSC file contains a group of sound effects. The first two Metroid Prime games utilize the MusyX audio engine created by Factor5 and as a result, AGSC files are essentially just embedded MusyX files. | ||
The audio codec used in AGSC is the standard GameCube DSP ADPCM codec. | The audio codec used in AGSC is the standard GameCube DSP-ADPCM codec, but MusyX itself also offers uncompressed PCM as an option. | ||
{{todo|Better descriptions for how SoundMacros work and a description for what each command does.}} | {{todo|Better descriptions for how SoundMacros work and a description for what each command does.}} | ||
{{ | {{research|minor|The proj and sdir chunks have a couple unknowns left.}} | ||
| | |||
}} | |||
__TOC__ | __TOC__ | ||
Line 25: | Line 23: | ||
| 0x0 | | 0x0 | ||
| '''D''' | | '''D''' | ||
| '''Audio Directory'''. Always "Audio/. | | '''Audio Directory'''. Always "Audio/". Zero-terminated. | ||
|- | |- | ||
| 0x0 + D | | 0x0 + D | ||
Line 51: | Line 49: | ||
|- | |- | ||
| 0x4 + D | | 0x4 + D | ||
| 2 | |||
| '''Group ID'''; 0xFFFF if unspecified | |||
|- | |||
| 0x6 + D | |||
| 4 | | 4 | ||
| '''Pool size''' | | '''Pool size''' | ||
|- | |- | ||
| | | 0xA + D | ||
| 4 | | 4 | ||
| '''Project size''' | | '''Project size''' | ||
|- | |- | ||
| | | 0xE + D | ||
| 4 | | 4 | ||
| '''Sample size''' | | '''Sample directory size''' | ||
|- | |- | ||
| | | 0x12 + D | ||
| 4 | | 4 | ||
| '''Sample | | '''Sample size''' | ||
|- | |- | ||
| | | 0x16 + D | ||
| colspan=2 {{unknown|End of header}} | | colspan=2 {{unknown|End of header}} | ||
|} | |} | ||
Line 72: | Line 74: | ||
=== Pool === | === Pool === | ||
The Pool chunk contains tables for SoundMacros, ADSR, keymaps, and layers, if applicable. It starts with a 16-byte header before the different data tables begin. | The Pool chunk contains sub-chunk tables for SoundMacros, ADSR, keymaps, and layers, if applicable. It starts with a 16-byte header before the different data tables begin. | ||
{| class="wikitable" | {| class="wikitable" | ||
Line 98: | Line 100: | ||
| colspan=2 {{unknown|End of entry}} | | colspan=2 {{unknown|End of entry}} | ||
|} | |} | ||
==== ObjectID ==== | |||
After this are four tables of objects. Each object is identified with a 16-bit '''ObjectID'''. | |||
Factor5 designed ObjectIDs to used in a polymorphic manner. The top 2 bits of the ID are used | |||
to differentiate between SoundMacros, Keymaps, and Layers. If the ID passes a mask of 0x4000, | |||
the object is a ''Keymap''. If the ID passes a mask of 0x8000, the object is a ''Layer''. | |||
Otherwise, the object is assumed to be a ''SoundMacro''. ''Tables'' don't require this type of | |||
polymorphism due to the context in which they are accessed. | |||
==== SoundMacros ==== | ==== SoundMacros ==== | ||
Line 112: | Line 124: | ||
| 0x0 | | 0x0 | ||
| 4 | | 4 | ||
| ''' | | '''Chunk Size''' ''(note: includes the size value itself)'' | ||
|- | |- | ||
| 0x4 | | 0x4 | ||
| 2 | | 2 | ||
| '''SoundMacro | | '''SoundMacro ObjectID''' | ||
|- | |- | ||
| 0x6 | | 0x6 | ||
| 2 | | 2 | ||
| '''Padding''' | |||
|- | |- | ||
| 0x8 | | 0x8 | ||
Line 431: | Line 443: | ||
|- | |- | ||
| 0x29 | | 0x29 | ||
| UNTRAP_EVENT | |||
| Event | |||
| colspan="2" {{unknown|}} | |||
| colspan="2" {{unknown|}} | |||
| colspan="2" {{unknown|}} | |||
|- | |||
| 0x2A | |||
| SEND_MESSAGE | | SEND_MESSAGE | ||
| IsVar | | IsVar | ||
| colspan="2" | Macro | | colspan="2" | Macro | ||
| VID | | VID | ||
| | | Variable | ||
| colspan="2" {{unknown|}} | | colspan="2" {{unknown|}} | ||
|- | |- | ||
| | | 0x2B | ||
| GET_MESSAGE | | GET_MESSAGE | ||
| | | Variable | ||
| colspan="6" {{unknown|}} | | colspan="6" {{unknown|}} | ||
|- | |- | ||
| | | 0x2C | ||
| GET_VID | | GET_VID | ||
| | | Variable | ||
| PLAY_MACRO | | PLAY_MACRO | ||
| colspan="5" {{unknown|}} | | colspan="5" {{unknown|}} | ||
Line 473: | Line 492: | ||
| colspan="5" {{unknown|}} | | colspan="5" {{unknown|}} | ||
|- | |- | ||
| | | 0x36 | ||
| SETPRIORITY | | SETPRIORITY | ||
| Prio | | Prio | ||
| colspan="6" {{unknown|}} | | colspan="6" {{unknown|}} | ||
|- | |- | ||
| | | 0x37 | ||
| ADDPRIORITY | | ADDPRIORITY | ||
| {{unknown|}} | | {{unknown|}} | ||
Line 484: | Line 503: | ||
| colspan="4" {{unknown|}} | | colspan="4" {{unknown|}} | ||
|- | |- | ||
| | | 0x38 | ||
| AGECNTSPEED | | AGECNTSPEED | ||
| colspan="3" {{unknown|}} | | colspan="3" {{unknown|}} | ||
| colspan="4" | Time | | colspan="4" | Time | ||
|- | |- | ||
| | | 0x39 | ||
| AGECNTVEL | | AGECNTVEL | ||
| {{unknown|}} | | {{unknown|}} | ||
Line 703: | Line 722: | ||
| colspan="2" | Immediate | | colspan="2" | Immediate | ||
| {{unknown|}} | | {{unknown|}} | ||
|- | |||
| 0x65 | |||
| SET_VAR | |||
| Ctrl | |||
| A = | |||
| colspan="1" {{unknown|}} | |||
| colspan="2" | Immediate | |||
| colspan="2" {{unknown|}} | |||
|- | |- | ||
| 0x70 | | 0x70 | ||
Line 732: | Line 759: | ||
'''Tables''' have two functions: for defining curves for volume scaling, or to be used as ADSR envelopes. | '''Tables''' have two functions: for defining curves for volume scaling, or to be used as ADSR envelopes. | ||
The tables continue until 0xffffffff terminator is reached. | |||
{| class="wikitable" | {| class="wikitable" | ||
Line 740: | Line 769: | ||
| 0x0 | | 0x0 | ||
| 4 | | 4 | ||
| '''Table size'''; | | '''Chunk Size''' | ||
|- | |||
| 0x4 | |||
| 2 | |||
| '''Table ObjectID''' | |||
|- | |||
| 0x6 | |||
| 2 | |||
| '''Padding''' | |||
|- | |||
| Chunk Size | |||
| colspan=2 {{Unknown|ADSR/Curve data}} | |||
|} | |||
===== ADSR ===== | |||
When the size of the table data is exactly 8, it may represent ADSR envelopes with this structure: | |||
'''Note:''' All fields of the envelope are ''little endian''. | |||
{| class="wikitable" | |||
! Offset | |||
! Size | |||
! Description | |||
|- | |||
| 0x0 | |||
| 2 | |||
| '''Attack time'''; in milliseconds | |||
|- | |||
| 0x2 | |||
| 2 | |||
| '''Decay time'''; in milliseconds | |||
|- | |- | ||
| 0x4 | | 0x4 | ||
| 2 | | 2 | ||
| ''' | | '''Sustain'''; percentage mapped between [0x0,0x1000] | ||
|- | |- | ||
| 0x6 | | 0x6 | ||
| 2 | | 2 | ||
| '''Release time'''; in milliseconds | |||
|- | |- | ||
| 0x8 | | 0x8 | ||
| colspan=2 {{unknown|End of ADSR}} | |||
|} | |||
===== DLS ADSR ===== | |||
MusyX can also express more advanced envelopes using a modified [[wikipedia:DLS format|DLS]] representation. | |||
This representation includes scaling coefficients to respond to played note and velocity | |||
(so slamming down a key harder plays longer). | |||
The attack and decay members are expressed in ''time-cents''. This may be converted to seconds using: | |||
<code>2<sup>timecents / (1200.0 * 65536.0)</sup></code> | |||
The attack and decay scale members are expressed as 0.1% increments in 16.16 fixed-point. | |||
This may be converted to a normalized factor using: | |||
<code>scale / (1000.0 * 65536.0)</code> | |||
'''Note:''' All fields of the envelope are ''little endian''. | |||
{| class="wikitable" | |||
! Offset | |||
! Size | |||
! Description | |||
|- | |||
| 0x0 | |||
| 4 | |||
| '''Attack time'''; in time-cents | |||
|- | |||
| 0x4 | |||
| 4 | |||
| '''Decay time'''; in time-cents | |||
|- | |||
| 0x8 | |||
| 2 | |||
| '''Sustain'''; percentage mapped between [0x0,0x1000] | |||
|- | |||
| 0xA | |||
| 2 | |||
| '''Release'''; in milliseconds | |||
|- | |||
| 0xC | |||
| 4 | |||
| '''Velocity to Attack Scale'''; 0.1% increments as 16.16 fixed-point | |||
|- | |||
| 0x10 | |||
| 4 | |||
| '''Key to Decay Scale'''; 0.1% increments as 16.16 fixed-point | |||
|- | |||
| 0x14 | |||
| colspan=2 {{unknown|End of DLS ADSR}} | |||
|} | |||
===== Curves ===== | |||
To express a volume curve, the table data is simply an arbitrarily-sized table of <code>uint8_t</code> values (although typically in MIDI range [0,127]) | |||
==== Keymaps ==== | |||
'''Keymaps''' are swappable, fixed-length tables mapping 128 MIDI keys to sound-producing objects. | |||
The keymaps continue until 0xffffffff terminator is reached. | |||
{| class="wikitable" | |||
! Offset | |||
! Size | |||
! Description | |||
|- | |||
| 0x0 | |||
| 4 | |||
| '''Chunk Size'''; (usually 0x1032) | |||
|- | |||
| 0x4 | |||
| 2 | |||
| '''Keymap ObjectID''' | |||
|- | |||
| 0x6 | |||
| 2 | |||
| '''Padding''' | |||
|- | |||
| Chunk Size | |||
| colspan=2 {{Unknown|128 Keymap entries}} | |||
|} | |||
===== Keymap Entry ===== | |||
{| class="wikitable" | |||
! Offset | |||
! Size | |||
! Description | |||
|- | |||
| 0x0 | |||
| 2 | |||
| '''ObjectID''' | |||
|- | |||
| 0x2 | |||
| 1 | |||
| '''Transpose''' | |||
|- | |||
| 0x3 | |||
| 1 | | 1 | ||
| ''' | | '''Pan''' | ||
|- | |- | ||
| | | 0x4 | ||
| 1 | |||
| '''Priority Offset''' | |||
|- | |||
| 0x8 | |||
| colspan=2 {{Unknown|Padded to 8 bytes}} | |||
|} | |||
==== Layers ==== | |||
'''Layers''' are one-to-many, ranged keyboard mappings to sound-producing objects. | |||
The layers continue until 0xffffffff terminator is reached. | |||
{| class="wikitable" | |||
! Offset | |||
! Size | |||
! Description | |||
|- | |||
| 0x0 | |||
| 4 | |||
| '''Chunk Size''' | |||
|- | |||
| 0x4 | |||
| 2 | |||
| '''Layer ObjectID''' | |||
|- | |||
| 0x6 | |||
| 2 | |||
| '''Padding''' | |||
|- | |||
| Chunk Size | |||
| colspan=2 {{Unknown|Layer data}} | |||
|} | |||
===== Layer Data ===== | |||
Within the layer data, there is a '''u32 count''' of layer range structs: | |||
{| class="wikitable" | |||
! Offset | |||
! Size | |||
! Description | |||
|- | |||
| 0x0 | |||
| 2 | |||
| '''ObjectID''' | |||
|- | |||
| 0x2 | |||
| 1 | | 1 | ||
| ''' | | '''Key Lo''' | ||
|- | |- | ||
| | | 0x3 | ||
| 1 | | 1 | ||
| ''' | | '''Key Hi''' | ||
|- | |- | ||
| | | 0x4 | ||
| 1 | | 1 | ||
| ''' | | '''Transpose''' | ||
|- | |- | ||
| | | 0x5 | ||
| 1 | | 1 | ||
| ''' | | '''Volume''' | ||
|- | |- | ||
| | | 0x6 | ||
| 1 | | 1 | ||
| ''' | | '''Priority Offset''' | ||
|- | |- | ||
| | | 0x7 | ||
| 1 | | 1 | ||
| ''' | | '''Surround Pan'''; (0: extreme forward, 64: center, 127: extreme rearward) | ||
|- | |- | ||
| | | 0x8 | ||
| 1 | | 1 | ||
| ''' | | '''Pan'''; (0: extreme left, 64: center, 127: extreme right) | ||
|- | |- | ||
| | | 0xC | ||
| colspan=2 {{ | | colspan=2 {{Unknown|Padded to 12 bytes}} | ||
|} | |} | ||
The entire Pool chunk is terminated by a value of 0xFFFF. | The entire Pool chunk is terminated by a value of 0xFFFF. | ||
Line 792: | Line 997: | ||
The Project properties chunk contains values for the sounds, including priority, polyphony, volume, etc. | The Project properties chunk contains values for the sounds, including priority, polyphony, volume, etc. | ||
Structurally, the Project is the root of the Audio Group tree, defining one or more ''Song Groups'' or ''SFX Groups'' | |||
{|class="wikitable" | {|class="wikitable" | ||
Line 800: | Line 1,007: | ||
| 0x0 | | 0x0 | ||
| 4 | | 4 | ||
| ''' | | '''Group end offset''' (points to next group in project) | ||
|- | |- | ||
| 0x2 | | 0x2 | ||
| 2 | | 2 | ||
| '''Group ID''' | |||
|- | |- | ||
| 0x4 | | 0x4 | ||
| 2 | | 2 | ||
| '''Group Type'''; 0 for SongGroup (for use with [[CSNG (File Format)|CSNG]]), 1 for SFXGroup. | |||
|- | |- | ||
| 0x8 | | 0x8 | ||
Line 832: | Line 1,039: | ||
| 0x1C | | 0x1C | ||
| 4 | | 4 | ||
| ''' | | '''Normal page table''' (SongGroup) / '''SFX table offset''' (SFXGroup) | ||
|- | |- | ||
| 0x20 | | 0x20 | ||
| 4 | | 4 | ||
| ''' | | '''Drum page table offset''' (SongGroup) | ||
|- | |- | ||
| 0x24 | | 0x24 | ||
| 4 | | 4 | ||
| ''' | | '''MIDI Setup table offset''' (SongGroup) | ||
|- | |- | ||
| 0x20 | | 0x20 | ||
| colspan=2 {{unknown|End of | | colspan=2 {{unknown|End of group header}} | ||
|} | |} | ||
Line 850: | Line 1,057: | ||
==== SoundMacro ID Table ==== | ==== SoundMacro ID Table ==== | ||
This is a table of shorts; there's no count value, so it's terminated with a value of 0xFFFF. It's a list of SoundMacro IDs present in the file. | This is a ranged-table of shorts; there's no count value, so it's terminated with a value of 0xFFFF. It's a list of SoundMacro IDs present in the file. Contiguous ranges are expressed by IDs with most-significant bit set (0x8000). The range begins on the marked ID and incrementally reaches the next ID in the list, including that ID. All other IDs are singular. | ||
==== Sample ID / Table / Keymap / Layer Tables ==== | |||
These function the same way as the SoundMacro ID table, but indexes other types of entities instead. | |||
'''Note:''' Keymap and Layer IDs in these tables have their top 2 bits (indicating their type) masked off. | |||
Keymaps must be OR'd with 0x4000 and Layers must be OR'd with 0x8000 in order to reconstruct the actual IDs. | |||
==== Normal / Drum Page Entry ==== | |||
Used to map [https://www.midi.org/specifications/item/gm-level-1-sound-set General MIDI program numbers] (instruments) | |||
to sound entities (macros, keymaps, layers) | |||
{|class="wikitable" | |||
! Offset | |||
! Size | |||
! Description | |||
|- | |||
| 0x0 | |||
| 2 | |||
| '''ObjectID''' | |||
|- | |||
| 0x2 | |||
| 1 | |||
| '''Priority'''; voices are limited, so priority is used to play more important sounds over others | |||
|- | |||
| 0x3 | |||
| 1 | |||
| '''Max number of voices''' | |||
|- | |||
| 0x4 | |||
| 1 | |||
| '''GM Program Number''' | |||
|- | |||
| 0x5 | |||
| 1 | |||
| '''Padding''' | |||
|} | |||
'''Note:''' The drum table is accessed when the MIDI channel is 10, otherwise the normal table is accessed. | |||
==== SFX Entry ==== | |||
Used to map auto-generated <code>#define</code> IDs (used by game code) to sound entities (macros, keymaps, layers) | |||
This table begins with a 16-bit count value, then 16 bits of padding. Each entry in the table is 10 bytes. | This table begins with a 16-bit count value, then 16 bits of padding. Each entry in the table is 10 bytes. | ||
Line 867: | Line 1,112: | ||
| 0x0 | | 0x0 | ||
| 2 | | 2 | ||
| ''' | | '''DefineID'''; referenced by game code | ||
|- | |- | ||
| 0x2 | | 0x2 | ||
| 2 | | 2 | ||
| ''' | | '''ObjectID''' | ||
|- | |- | ||
| 0x4 | | 0x4 | ||
| 1 | | 1 | ||
| ''' | | '''Priority'''; voices are limited, so priority is used to play more important sounds over others | ||
|- | |- | ||
| 0x5 | | 0x5 | ||
| 1 | | 1 | ||
| ''' | | '''Max number of voices''' | ||
|- | |- | ||
| 0x6 | | 0x6 | ||
Line 890: | Line 1,135: | ||
|- | |- | ||
| 0x8 | | 0x8 | ||
| | | 1 | ||
| '''Definite Key'''; The default pitch ( | | '''Definite Key'''; The default pitch - usually 0x3C (MIDI C4) | ||
|- | |||
| 0x9 | |||
| 1 | |||
| '''Padding''' | |||
|} | |||
==== MIDI Setup Entry ==== | |||
Table of fixed-length tables to map all 16 MIDI channels to program numbers | |||
(in-turn resolving to sound entities via the page table). | |||
Multiple MIDI Setups may be created to support Song data requiring totally different | |||
banks of instruments. | |||
Each MIDI Setup starts with a u16 '''MIDI-Setup-ID''', then 16-bits padding, then 16 entries of the following structure (one for each channel): | |||
{|class="wikitable" | |||
! Offset | |||
! Size | |||
! Description | |||
|- | |||
| 0x0 | |||
| 1 | |||
| '''Program Number''' | |||
|- | |||
| 0x1 | |||
| 1 | |||
| '''Volume''' | |||
|- | |||
| 0x2 | |||
| 1 | |||
| '''Panning''' | |||
|- | |||
| 0x3 | |||
| 1 | |||
| '''Reverb''' | |||
|- | |||
| 0x4 | |||
| 1 | |||
| '''Chorus''' | |||
|} | |} | ||
MIDI setups continue until the ''group end offset'' is reached. | |||
=== Sample === | === Sample === | ||
Line 928: | Line 1,215: | ||
|- | |- | ||
| 0xC | | 0xC | ||
| | | 1 | ||
| ''' | | '''Base Note'''; Corresponds to the MIDI note played in the sample, at the native sample-rate (which MusyX obtains from the INST chunk of .aiff files or SMPL chunk of .wav files, along with looping info). To play at a specified pitch in [[wikipedia:Cent (music)|cents]], set the playback sample rate using this formula: <code>sampleRate * 2<sup>((pitch - baseNote * 100) / 1200.0)</sup></code> | ||
|- | |||
| 0xD | |||
| 1 | |||
| '''Padding'''; always 0 | |||
|- | |- | ||
| 0xE | | 0xE | ||
Line 936: | Line 1,227: | ||
|- | |- | ||
| 0x10 | | 0x10 | ||
| | | 1 | ||
| '''Audio format''' <ol start="0"><li>DSP-ADPCM</li><li>DSP-ADPCM (Drum Sample)</li><li>PCM</li><li>N64-VADPCM (Legacy Format)</li></ol> | |||
|- | |||
| 0x11 | |||
| 3 | |||
| '''Number of samples''' | | '''Number of samples''' | ||
|- | |- | ||
Line 978: | Line 1,273: | ||
| 0x4 | | 0x4 | ||
| 2 | | 2 | ||
| '''Loop context sample history | | '''Loop context sample history 2''' | ||
|- | |- | ||
| 0x6 | | 0x6 | ||
| 2 | | 2 | ||
| '''Loop context sample history | | '''Loop context sample history 1''' | ||
|- | |- | ||
| 0x8 | | 0x8 | ||
Line 994: | Line 1,289: | ||
== Tools == | == Tools == | ||
* [https://drive.google.com/file/d/0B9MLV21H7SDvemgwN1daYnliSjA/view?usp=sharing Prime Audio Decoder] by | * [https://drive.google.com/file/d/0B9MLV21H7SDvemgwN1daYnliSjA/view?usp=sharing Prime Audio Decoder] by Aruki will dump all sound effects contained in a given AGSC file. | ||
[[Category:File Formats]] | [[Category:File Formats]] |