CSNG (File Format): Difference between revisions
Jump to navigation
Jump to search
Multi-track SNG clarifications
imported>Jackoalan |
imported>Jackoalan (Multi-track SNG clarifications) |
||
Line 1: | Line 1: | ||
The '''CSNG format''' contains MIDI data. It appears in Metroid Prime 1 and 2. It is essentially MusyX's | The '''CSNG format''' contains MIDI data. It appears in Metroid Prime 1 and 2. It is essentially MusyX's GameCube SNG music format, with a custom header. | ||
__TOC__ | __TOC__ | ||
Line 7: | Line 7: | ||
All offsets are relative to the start of the main header (after the custom header). | All offsets are relative to the start of the main header (after the custom header). | ||
Timings are represented in ''ticks'' | Overall, SNG functions similar to a Type-1 (multi-track) MIDI file, with (up to) 64 tracks | ||
merging their events into 16 General-MIDI sequencer channels. In addition to the multi-track | |||
structure, SNG supports storing MIDI regions as indexed ''patterns'', so songs with repetitive | |||
refrains can save on memory by referencing patterns more than once. | |||
Timings are represented in ''ticks''. Unlike MIDI, the tick-resolution is fixed at 384 | |||
ticks per beat (e.g. 120 beats-per-minute works out to <code>384 * 120 / 60 = 768</code> ticks-per-second). | ticks per beat (e.g. 120 beats-per-minute works out to <code>384 * 120 / 60 = 768</code> ticks-per-second). | ||
Line 37: | Line 42: | ||
| 0x10 | | 0x10 | ||
| 4 | | 4 | ||
| ''' | | '''SNG File Length''' | ||
|- | |- | ||
| 0x14 | | 0x14 | ||
Line 52: | Line 57: | ||
| 0x0 | | 0x0 | ||
| 4 | | 4 | ||
| ''' | | '''Track Index Offset'''; usually 0x18 for GCN games | ||
|- | |- | ||
| 0x4 | | 0x4 | ||
| 4 | | 4 | ||
| ''' | | '''Region Data Index Offset''' | ||
|- | |- | ||
| 0x8 | | 0x8 | ||
| 4 | | 4 | ||
| '''Channel Map Offset''' | | '''Channel Map Offset''' | ||
|- | |- | ||
| 0xC | | 0xC | ||
| 4 | | 4 | ||
| '''Tempo Table Offset'''; | | '''Tempo Table Offset'''; 0x0 if tempo doesn't change | ||
|- | |- | ||
| 0x10 | | 0x10 | ||
Line 75: | Line 80: | ||
|- | |- | ||
| 0x18 | | 0x18 | ||
| colspan=2 {{unknown|End of header}} | | colspan=2 {{unknown|End of header}} | ||
|} | |} | ||
=== | ===Region Info=== | ||
The ''track index'' has offsets relating the first '''region info''' for each track. | |||
There is a sequence of at least 2 region info structures populating each track, last one acting as terminator: | |||
{| class="wikitable" | {| class="wikitable" | ||
Line 93: | Line 96: | ||
| 0x0 | | 0x0 | ||
| 4 | | 4 | ||
| '''Start Tick'''; time-point to begin executing | | '''Start Tick'''; time-point to begin executing region data | ||
|- | |- | ||
| 0x4 | | 0x4 | ||
Line 101: | Line 104: | ||
| 0x8 | | 0x8 | ||
| 2 | | 2 | ||
| ''' | | '''Region Data Index'''; used to select region data via the ''region data index''; value is negative on a dummy ''region info'' to terminate track regions | ||
|- | |- | ||
| 0xA | | 0xA | ||
| 2 | | 2 | ||
| ''' | | {{unknown|'''Unknown'''}} | ||
|- | |- | ||
| 0xC | | 0xC | ||
| colspan=2 {{unknown|End of region info}} | |||
| colspan=2 {{unknown|End of | |||
|} | |} | ||
=== | ===Region Data=== | ||
Here begins a free-form blob of indexed | Here begins a free-form blob of indexed region data. It starts with a variable-length | ||
'''u32 array''' of | '''u32 array''' of SNG offsets for each region, then the region data itself. | ||
==== | ====Region Data Header==== | ||
{| class="wikitable" | {| class="wikitable" | ||
Line 137: | Line 128: | ||
| 0x0 | | 0x0 | ||
| 4 | | 4 | ||
| ''' | | '''Region Data Header Size'''; size of the header ''after'' this field (always 0x8) | ||
|- | |- | ||
| 0x4 | | 0x4 | ||
| 4 | | 4 | ||
| '''Pitch Wheel Data Offset'''; (absolute | | '''Pitch Wheel Data Offset'''; (absolute SNG-offset) 0x0 if no pitch-wheel messages in region | ||
|- | |- | ||
| 0x8 | | 0x8 | ||
| 4 | | 4 | ||
| '''Mod Wheel Data Offset'''; (absolute | | '''Mod Wheel Data Offset'''; (absolute SNG-offset) 0x0 if no mod-wheel messages in region | ||
|- | |- | ||
| 0xC | | 0xC | ||
| colspan=2 {{unknown|End of | | colspan=2 {{unknown|End of region data}} | ||
|} | |} | ||
==== | ====Region Commands==== | ||
After the | After the region data header, the actual playback commands begin. There are only 2 types of commands | ||
in | in SNG: ''note'' and ''control change''. | ||
=====Delta Time RLE===== | =====Delta Time RLE===== | ||
Line 187: | Line 178: | ||
this is a '''note command'''. | this is a '''note command'''. | ||
Unlike MIDI, which has separate commands for note-on/note-off, | Unlike MIDI, which has separate commands for note-on/note-off, SNG attaches a ''note length'' value | ||
to a note-on command, which is then able to track its own lifetime. | to a note-on command, which is then able to track its own lifetime. | ||
Line 235: | Line 226: | ||
|} | |} | ||
=====End Of | =====End Of Region===== | ||
When the two bytes following the delta-time == 0xffff, this | When the two bytes following the delta-time == 0xffff, this region has no more commands. | ||
====Continuous Pitch / Modulation Data==== | ====Continuous Pitch / Modulation Data==== | ||
If the pitch or mod offsets in a | If the pitch or mod offsets in a region are non-zero, they point to a buffer of RLE-compressed | ||
(delta-tick, delta-value) pairs, decoding to signed 16-bit precision. The decoder must track | (delta-tick, delta-value) pairs, decoding to signed 16-bit precision. The decoder must track | ||
the absolute time and value, summing each consecutive update for the current time/values. | the absolute time and value, summing each consecutive update for the current time/values. | ||
Line 282: | Line 273: | ||
===Channel Map=== | ===Channel Map=== | ||
This is a simple '''u8 table''' mapping 64 | This is a simple '''u8 table''' mapping 64 SNG tracks to 16 MIDI channels for instrument selection via the [[AGSC (File Format)#MIDI Setup Entry|SongGroup MIDI-Setup]]. | ||
===Tempo Table=== | ===Tempo Table=== | ||
When the | When the SNG has a non-zero tempo table offset, this song features tempo changes. | ||
The change events are simple absolute-tick / BPM pairs. | The change events are simple absolute-tick / BPM pairs. | ||