AGSC (File Format): Difference between revisions

Jump to navigation Jump to search
>MrSinistar
No edit summary
imported>Jackoalan
 
(89 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 created by utilizing files generated from MusyX's SDK.
'''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.  


According to the debug maps in Metroid Prime, there are references to various "classes", with one of them controlling audio in the map, known as AudioGroupSet.  So presumably, AGSC is an abbreviation for '''Audio Group Set Class''' or '''Audio Group Set Cooked''', as all the game data had to be cooked into their final formats.
The audio codec used in AGSC is the standard GameCube DSP-ADPCM codec, but MusyX itself also offers uncompressed PCM as an option.


The audio codec used in AGSC is the standard GameCube DSP ADPCM codec, as described in the GameCube SDK.
{{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.}}
{{researchmoderate
|reason=The proj chunk and the sdir chunk still need some reverse-engineering.
}}


__TOC__
__TOC__
Line 13: Line 10:
== Format ==
== Format ==


The AGSC format is split up into four distinct data chunks; one for sound engine scripts, one for sound properties, one for sound metadata, and one for actual ADPCM sound data. The format is very similar between Metroid Prime 1 and 2; the main difference is the header, and some slight changes in the way the four chunks are organized. In Metroid Prime, each chunk begins with its own size value; in Metroid Prime 2, every chunk instead has its size listed at the beginning of the file, at the end of the header. In addition, in Metroid Prime, the third chunk is sound data, and the fourth is sound metadata; in Metroid Prime 2, it's the other way around.
The AGSC format is essentially four data chunks combined into one resource, each of which is a standard MusyX file. Of the four data chunks (pool, proj, samp, and sdir), there's one for sound engine scripts, one for sound properties, one for actual ADPCM sound data, and one for sound metadata. The main difference between Prime 1 and 2 is the header, and some slight changes in the way the four chunks are organized. In Metroid Prime, each chunk begins with its own size value; in Metroid Prime 2, every chunk instead has its size listed at the beginning of the file, at the end of the header. In addition, in Metroid Prime, the third chunk is samp, and the fourth is sdir; in Metroid Prime 2, it's the other way around.


=== Header ===
=== Header ===
Line 26: Line 23:
| 0x0
| 0x0
| '''D'''
| '''D'''
| '''Audio Directory'''. Always "Audio/." Zero-terminated.
| '''Audio Directory'''. Always "Audio/". Zero-terminated.
|-
|-
| 0x0 + D
| 0x0 + D
Line 52: Line 49:
|-
|-
| 0x4 + D
| 0x4 + D
| 2
| '''Group ID'''; 0xFFFF if unspecified
|-
| 0x6 + D
| 4
| 4
| '''Chunk 1 size'''
| '''Pool size'''
|-
|-
| 0x8 + D
| 0xA + D
| 4
| 4
| '''Chunk 2 size'''
| '''Project size'''
|-
|-
| 0xC + D
| 0xE + D
| 4
| 4
| '''Sound metadata chunk size'''
| '''Sample directory size'''
|-
|-
| 0x10 + D
| 0x12 + D
| 4
| 4
| '''ADPCM chunk size'''
| '''Sample size'''
|-
|-
| 0x14 + D
| 0x16 + D
| colspan=2 {{unknown|End of header}}
| colspan=2 {{unknown|End of header}}
|}
|}


=== MusyX chunks ===
=== Pool ===


After the AGSC header, there are four chunks: the Pool chunk, the Properties chunk, the Sample chunk and the Sample Directory chunk. Each chunk is originally a file created with muconv2, a tool that comes with MusyX's SDK.  Muconv2 takes the original project file and its associated sound assests and converts them into file formats that are read by the Gamecube's MusyX audio engine.  Retro took these four separate files and cooked them into the final AGSC files.
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.


==== POOL chunk ====
{| class="wikitable"
! Offset
! Size
! Description
|-
| 0x0
| 4
| '''SoundMacros Offset''' (always 0x10)
|-
| 0x4
| 4
| '''Tables Offset'''
|-
| 0x8
| 4
| '''Keymaps Offset'''
|-
| 0xC
| 4
| '''Layers Offset'''
|-
| 0x10
| colspan=2 {{unknown|End of entry}}
|}
 
==== ObjectID ====
 
After this are four tables of objects. Each object is identified with a 16-bit '''ObjectID'''.


The Pool chunk denotes MusyX's "SoundMacros", small scripts that apply various effects and commands on the sounds in the game. The chunk first calls out a command ID then the parameters of that particular command, which varies.
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.


Every 4 bytes is in little endian, so to read the data as originally formatted by the MusyX SDK before it was converted, take each long (4 bytes) and change to big endian.
==== SoundMacros ====


{|class="wikitable"
The first Pool table denotes MusyX's '''SoundMacros''', small scripts that apply various effects on the sounds in the game.  Each macro is composed of a header followed by a number of commands; each command specifies its type through a single-byte command ID, then specifies the parameters of that particular command, which vary.
 
The header of each SoundMacro is eight bytes, and is structured as follows:
 
{| class="wikitable"
! Offset
! Size
! Description
|-
| 0x0
| 4
| '''Chunk Size''' ''(note: includes the size value itself)''
|-
| 0x4
| 2
| '''SoundMacro ObjectID'''
|-
| 0x6
| 2
| '''Padding'''
|-
| 0x8
| colspan=2 {{unknown|Commands begin}}
|}
 
On the commands, each 4 bytes were originally little endian, but have been swapped to big endian in the AGSC files (despite not being longs). To read the data as originally formatted, every four bytes needs to be byte-swapped. Each command is 8 bytes, and is structured as follows:
 
{| class="wikitable"
! Offset
! Offset
! Size
! Size
Line 91: Line 150:
|-
|-
| 0x1
| 0x1
| 7
| '''Command arguments''' (varies between commands)
|-
| 0x8
| colspan=2 {{unknown|End of entry}}
|}
The SoundMacro will continue with commands until it terminates when the END command is executed.  The command ID for END is 0 and has null command arguments; the next SoundMacro begins after reading it.
These are the possible commands:
{| class="wikitable"
! width="2%" | ID
! width="7%" | Name
! colspan="91%" | Arguments
|-
| 0x1
| STOP
| width="13%" {{unknown|}}
| width="13%" {{unknown|}}
| width="13%" {{unknown|}}
| width="13%" {{unknown|}}
| width="13%" {{unknown|}}
| width="13%" {{unknown|}}
| width="13%" {{unknown|}}
|-
| 0x2
| SPLITKEY
| Keynumber
| colspan="2" | SoundMacro ID
| colspan="2" | SoundMacro step
| colspan="2" {{unknown|}}
|-
| 0x3
| SPLITVEL
| Velocity
| colspan="2" | SoundMacro ID
| colspan="2" | SoundMacro step
| colspan="2" {{unknown|}}
|-
| 0x4
| WAIT_TICKS
| Keyoff
| Random
| Sampleend
| Absolute
| ms switch
| colspan="2" | Ticks/Millisec.
|-
| 0x5
| LOOP
| Keyoff
| Random
| Sampleend
| colspan="2" | SoundMacro step
| colspan="2" | Times
|-
| 0x6
| GOTO
| {{unknown|}}
| colspan="2" | SoundMacro ID
| colspan="2" | SoundMacro step
| colspan="2" {{unknown|}}
|-
| 0x7
| WAIT_MS
| Keyoff
| Random
| Sample end
| Absolute
| {{unknown|}}
| colspan="2" | Millisec.
|-
| 0x8
| PLAYMACRO
| Addnote
| colspan="2" | SoundMacro ID
| colspan="2" | SoundMacro step
| Priority
| MaxVoices
|-
| 0x9
| SENDKEYOFF
| Variable
| Last started
| colspan="5" {{unknown|}}
|-
| 0xA
| SPLITMOD
| Mod value
| colspan="2" | SoundMacro ID
| colspan="2" | SoundMacro step
| colspan="2" {{unknown|}}
|-
| 0xB
| PIANOPAN
| Scale
| Centerkey
| Centerpan
| colspan="4" {{unknown|}}
|-
| 0xC
| SETADSR
| colspan="2" | Table-ID (ADSR)
| DLS mode
| colspan="4" {{unknown|}}
|-
| 0xD
| SCALEVOLUME
| Scale
| Add
| colspan="2" | Table-ID (Curve)
| Org vol
| colspan="2" {{unknown|}}
|-
| 0xE
| PANNING
| Pan position
| colspan="2" | Time ms
| Width
| colspan="3" {{unknown|}}
|-
| 0xF
| ENVELOPE
| Scale
| Add
| colspan="2" | Table-ID (Curve)
| Org vol
| colspan="2" {{unknown|}}
|-
| 0x10
| STARTSAMPLE
| colspan="2" | Sample-ID
| Mode
| colspan="4" | Offset
|-
| 0x11
| STOPSAMPLE
| colspan="7" {{unknown|}}
|-
| 0x12
| KEYOFF
| colspan="7" {{unknown|}}
|-
| 0x13
| SPLITRND
| RND
| colspan="2" | SoundMacro ID
| colspan="2" | SoundMacro step
| colspan="2" {{unknown|}}
|-
| 0x14
| FADE-IN
| Scale
| Add
| colspan="2" | Table-ID (Curve)
| ms switch
| colspan="2" | Ticks/Millisec.
|-
| 0x15
| SPANNING
| Pan position
| colspan="2" | Time ms
| Width
| colspan="3" {{unknown|}}
|-
| 0x16
| SETADSRCTRL
| Attack
| Decay
| Sustain
| Relase
| colspan="3" {{unknown|}}
|-
| 0x17
| RNDNOTE
| Note Lo
| Detune
| Note Hi
| Fixed/Free
| Abs/Rel
| colspan="2" {{unknown|}}
|-
| 0x18
| ADDNOTE
| Add
| Detune
| org key
| {{unknown|}}
| ms switch
| colspan="2" | Ticks/Millisec.
|-
| 0x19
| SETNOTE
| Key
| Detune
| colspan="2" {{unknown|}}
| ms switch
| colspan="2" | Ticks/Millisec.
|-
| 0x1A
| LASTNOTE
| Add
| detune
| colspan="2" {{unknown|}}
| ms switch
| colspan="2" | Ticks/Millisec.
|-
| 0x1B
| PORTAMENTO
| Port. State
| Port. Type
| colspan="2" {{unknown|}}
| ms switch
| colspan="2" | Ticks/Millisec.
|-
| 0x1C
| VIBRATO
| Level note
| Level fine
| Modwheel flag
| {{unknown|}}
| ms switch
| colspan="2" | Ticks/Millisec.
|-
| 0x1D
| PITCHSWEEP1
| Times
| colspan="2" | Add
| {{unknown|}}
| ms switch
| colspan="2" | Ticks/Millisec.
|-
| 0x1E
| PITCHSWEEP2
| Times
| colspan="2" | Add
| {{unknown|}}
| ms switch
| colspan="2" | Ticks/Millisec.
|-
| 0x1F
| SETPITCH
| colspan="3" | Frequency (Hz)
| colspan="2" | Fine
| colspan="2" {{unknown|}}
|-
| 0x20
| SETPITCHADSR
| colspan="2" | Table-ID (ADSR)
| {{unknown|}}
| Note range
| Detune range
| colspan="2" {{unknown|}}
|-
| 0x21
| SCALEVOLUME DLS
| colspan="2" | Scale
| Org vol
| colspan="4" {{unknown|}}
|-
| 0x22
| MOD2VIBRANGE
| Key range
| Cent range
| colspan="5" {{unknown|}}
|-
| 0x23
| SETUP TREMOLO
| colspan="2" | Tremolo scale
| {{unknown|}}
| colspan="2" | Modw. add scale
| colspan="2" {{unknown|}}
|-
| 0x24
| RETURN
| colspan="7" {{unknown|}}
|-
| 0x25
| GOSUB
| {{unknown|}}
| colspan="2" | SoundMacro ID
| colspan="2" | SoundMacro step
| colspan="2" {{unknown|}}
|-
| 0x28
| TRAP_EVENT
| Event
| colspan="2" | SoundMacro ID
| colspan="2" | SoundMacro step
| colspan="2" {{unknown|}}
|-
| 0x29
| UNTRAP_EVENT
| Event
| colspan="2" {{unknown|}}
| colspan="2" {{unknown|}}
| colspan="2" {{unknown|}}
|-
| 0x2A
| SEND_MESSAGE
| IsVar
| colspan="2" | Macro
| VID
| Variable
| colspan="2" {{unknown|}}
|-
| 0x2B
| GET_MESSAGE
| Variable
| colspan="6" {{unknown|}}
|-
| 0x2C
| GET_VID
| Variable
| PLAY_MACRO
| colspan="5" {{unknown|}}
|-
| 0x30
| ADDAGECOUNT
| {{unknown|}}
| colspan="2" | Add
| colspan="4" {{unknown|}}
|-
| 0x31
| SETAGECOUNT
| {{unknown|}}
| colspan="2" | Counter
| colspan="4" {{unknown|}}
|-
| 0x32
| SENDFLAG
| Flag-ID
| Value
| colspan="5" {{unknown|}}
|-
| 0x33
| PITCHWHEELR
| Range up
| Range down
| colspan="5" {{unknown|}}
|-
| 0x36
| SETPRIORITY
| Prio
| colspan="6" {{unknown|}}
|-
| 0x37
| ADDPRIORITY
| {{unknown|}}
| colspan="2" | Add
| colspan="4" {{unknown|}}
|-
| 0x38
| AGECNTSPEED
| colspan="3" {{unknown|}}
| colspan="4" | Time
|-
| 0x39
| AGECNTVEL
| {{unknown|}}
| colspan="2" | AGE Base
| colspan="2" | AGE Scale
| colspan="2" {{unknown|}}
|-
| 0x40
| VOL_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x41
| PAN_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x42
| PitchW_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x43
| ModW_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x44
| PEDAL_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x45
| PORTA_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x46
| REVERB_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x47
| SPAN_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x48
| DOPPLER_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x49
| TREMOLO_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x4A
| PREA_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x4B
| PREB_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x4C
| POSTB_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x4D
| AUXAFX_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x4E
| AUXBFX_SELECT
| MIDI Contr.
| colspan="2" | Scaling percentage
| Combine
| is var.
| Fine scaling
| {{unknown|}}
|-
| 0x50
| SETUP_LFO
| LFO Nr.
| colspan="2" | Period in ms
| colspan="4" {{unknown|}}
|-
| 0x58
| MODE_SELECT
| DLS vol
| ITD
| colspan="5" {{unknown|}}
|-
| 0x59
| SET_KEYGROUP
| group
| kill
| colspan="5" {{unknown|}}
|-
| 0x5A
| SRCMODE_SELECT
| SRC type
| Type 0 SRC filter
| colspan="5" {{unknown|}}
|-
| 0x60
| ADD_VARS
| Var/Ctrl
| A =
| Var/Ctrl
| B +
| Var/Ctrl
| C
| {{unknown|}}
|-
| 0x61
| SUB_VARS
| Var/Ctrl
| A =
| Var/Ctrl
| B -
| Var/Ctrl
| C
| {{unknown|}}
|-
| 0x62
| MUL_VARS
| Var/Ctrl
| A =
| Var/Ctrl
| B *
| Var/Ctrl
| C
| {{unknown|}}
|-
| 0x63
| DIV_VARS
| Var/Ctrl
| A =
| Var/Ctrl
| B /
| Var/Ctrl
| C
| {{unknown|}}
|-
| 0x64
| ADDI_VARS
| Var/Ctrl
| A =
| Var/Ctrl
| B +
| colspan="2" | Immediate
| {{unknown|}}
|-
| 0x65
| SET_VAR
| Ctrl
| A =
| colspan="1" {{unknown|}}
| colspan="2" | Immediate
| colspan="2" {{unknown|}}
|-
| 0x70
| IF_EQUAL
| Ctrl
| A ==
| Ctrl
| B
| Not
| colspan="2" | SoundMacro Step
|-
| 0x71
| IF_LESS
| Ctrl
| A <
| Ctrl
| B
| Not
| colspan="2" | SoundMacro Step
|-
| 0x0
| END
| colspan="7" {{unknown|}}
|}
After the last soundmacro, the table terminated by a value of 0xFFFF.
==== Tables ====
'''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"
! Offset
! Size
! Description
|-
| 0x0
| 4
| '''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
| 2
| '''Sustain'''; percentage mapped between [0x0,0x1000]
|-
| 0x6
| 2
| '''Release time'''; in milliseconds
|-
| 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
| '''Pan'''
|-
| 0x4
| 1
| 1
| '''Command argument''' (varies between commands)
| '''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
| 0x2
| 1
| 1
| '''Command argument''' (varies between commands)
| '''Key Lo'''
|-
|-
| 0x3
| 0x3
| 1  
| 1
| '''Command argument''' (varies between commands)
| '''Key Hi'''
|-
|-
| 0x4
| 0x4
| 1
| 1
| ''Flip next four bytes'' '''Command argument''' (varies between commands)
| '''Transpose'''
|-
|-
| 0x5
| 0x5
| 1
| 1
| '''Command argument''' (varies between commands)
| '''Volume'''
|-
|-
| 0x6
| 0x6
| 1
| 1
| '''Command argument''' (varies between commands)
| '''Priority Offset'''
|-
|-
| 0x7
| 0x7
| 1  
| 1
| '''Command argument''' (varies between commands)
| '''Surround Pan'''; (0: extreme forward, 64: center, 127: extreme rearward)
|-
|-
| 0x8
| 0x8
| colspan=2 {{unknown|End of entry}}
| 1
| '''Pan'''; (0: extreme left, 64: center, 127: extreme right)
|-
| 0xC
| colspan=2 {{Unknown|Padded to 12 bytes}}
|}
|}


The Soundmacro terminates when the END command is read.  The command ID for END is 0 and has no command arguments, so when 0x00000000 is read, start the next SoundMacro.  The entire chunk terminates when 0xFFFF is read.  
The entire Pool chunk is terminated by a value of 0xFFFF.


==== PROJ chunk ====
=== Project ===


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.


==== SAMP chunk ====
Structurally, the Project is the root of the Audio Group tree, defining one or more ''Song Groups'' or ''SFX Groups''
 
{|class="wikitable"
! Offset
! Size
! Description
|-
| 0x0
| 4
| '''Group end offset''' (points to next group in project)
|-
| 0x2
| 2
| '''Group ID'''
|-
| 0x4
| 2
| '''Group Type'''; 0 for SongGroup (for use with [[CSNG (File Format)|CSNG]]), 1 for SFXGroup.
|-
| 0x8
| 4
| '''SoundMacro ID table offset'''
|-
| 0xC
| 4
| '''Sample ID table offset'''
|-
| 0x10
| 4
| '''Tables table offset'''
|-
| 0x14
| 4
| '''Keymaps table offset'''
|-
| 0x18
| 4
| '''Layers table offset'''
|-
| 0x1C
| 4
| '''Normal page table''' (SongGroup) / '''SFX table offset''' (SFXGroup)
|-
| 0x20
| 4
| '''Drum page table offset''' (SongGroup)
|-
| 0x24
| 4
| '''MIDI Setup table offset''' (SongGroup)
|-
| 0x20
| colspan=2 {{unknown|End of group header}}
|}
 
After the header are a number of data tables.
 
==== SoundMacro ID Table ====
 
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.
 
{|class="wikitable"
! Offset
! Size
! Description
|-
| 0x0
| 2
| '''DefineID'''; referenced by game code
|-
| 0x2
| 2
| '''ObjectID'''
|-
| 0x4
| 1
| '''Priority'''; voices are limited, so priority is used to play more important sounds over others
|-
| 0x5
| 1
| '''Max number of voices'''
|-
| 0x6
| 1
| '''Definite Velocity'''; volume (usually 7F)
|-
| 0x7
| 1
| '''Panning'''
|-
| 0x8
| 1
| '''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 ===


The Sample chunk is all the sound data encoded using the standard Gamecube DSP ADPCM codec.  It can be decoded the same way as a [[DSP (File Format)|DSP]] file. Each sound's size is padded to 32 bytes before the next sound's data begins.
The Sample chunk is all the sound data encoded using the standard Gamecube DSP ADPCM codec.  It can be decoded the same way as a [[DSP (File Format)|DSP]] file. Each sound's size is padded to 32 bytes before the next sound's data begins.


==== SDIR chunk ====
=== Sample Directory ===


The Sample Directory chunk (chunk 4 in Metroid Prime, chunk 3 in Metroid Prime 2) is made up of two sets of tables. The structure of both these tables is identical between both games.
The Sample Directory chunk (chunk 4 in Metroid Prime, chunk 3 in Metroid Prime 2) is made up of two sets of tables. The structure of both these tables is identical between both games.


===== Table A =====
==== Table A ====


The first metadata table has one entry per sound, and is terminated with 0xFFFFFFFF; since there's no known sound count anywhere in the file, the only way to read this correctly is to read until you reach the terminator value. Each entry is 0x20 bytes long.
The first metadata table has one entry per sound, and is terminated with 0xFFFFFFFF; since there's no known sound count anywhere in the file, the only way to read this correctly is to read until you reach the terminator value. Each entry is 0x20 bytes long.
Line 147: Line 1,200:
| 0x0
| 0x0
| 2
| 2
| '''Sound ID''' (note: not 100% confirmed)
| '''Sound ID'''
|-
|-
| 0x2
| 0x2
| 2
| 2
| Padding.
| '''Padding'''; always 0
|-
|-
| 0x4
| 0x4
Line 162: Line 1,215:
|-
|-
| 0xC
| 0xC
| 2
| 1
| {{unknown|'''Unknown'''; appears to always be 0x3C00}}
| '''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 170: Line 1,227:
|-
|-
| 0x10
| 0x10
| 4
| 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 189: Line 1,250:
|}
|}


===== Table B =====
==== Table B ====


These are accessed through the offsets in table A's entries; note that it might not match the sound count, because the same entry in this table can be used with multiple sounds. Each entry is 0x28 bytes long.
These are accessed through the offsets in table A's entries; note that it might not match the sound count, because the same entry in this table can be used with multiple sounds. Each entry is 0x28 bytes long.
Line 200: Line 1,261:
| 0x0
| 0x0
| 2
| 2
| {{unknown|'''Unknown'''}}
| {{unknown|'''Unknown'''; always 8}}
|-
|-
| 0x2
| 0x2
| 1
| '''Initial predictor/scale''' (matches first frame header)
|-
| 0x3
| 1
| '''Loop predictor/scale''' (matches loop start frame header)
|-
| 0x4
| 2
| 2
| {{unknown|'''Unknown'''}}
| '''Loop context sample history 2'''
|-
|-
| 0x4
| 0x6
| 4
| 2
| {{unknown|'''Unknown'''}}
| '''Loop context sample history 1'''
|-
|-
| 0x8
| 0x8
Line 217: Line 1,286:
| colspan=2 {{unknown|End of entry}}
| colspan=2 {{unknown|End of entry}}
|}
|}
=== ADPCM Data ===


== Tools ==
== Tools ==


* [https://drive.google.com/file/d/0B9MLV21H7SDvemgwN1daYnliSjA/view?usp=sharing Prime Audio Decoder] by Parax will dump all sound effects contained in a given AGSC file.
* [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]]
Anonymous user

Navigation menu