Actions

SCHG

Difference between revisions of "Sonic Jam/Sounds"

From Sonic Retro

m (SWORLD.SND Map: Typo)
(Added information on how Music and SFX are handled in Sonic World, and by extension, information on the Saturn's implementation of the MIDI standard. Minor edits to the Samples section.)
Line 1: Line 1:
 
__NOTOC__
 
__NOTOC__
 
{{SCHG SJ}}
 
{{SCHG SJ}}
==Samples==
+
==PCM Samples==
According to my research so far, most samples are in a signed 8-bit mono format, with a sample rate of 22050Hz. Each byte is a building block (a "sample") of the sample as a whole.
+
According to my research so far, most PCM samples are in a signed 8-bit mono format, with a sample rate of 22050Hz. Each byte is a building block (one "sample") of the PCM sample as a whole.
  
 
There are some exceptions, though.
 
There are some exceptions, though.
  
Some samples are "stereo", in that the sample is in the left ear, while there's data/code in the right ear. The stereo format has every other sample starting with the first be in the left ear, and every other sample starting with the second be in the right ear. Due to every other sample being used, these have a halved sample rate; 11025Hz instead of 22050Hz.
+
Some PCM samples are "stereo", in that the sample is in the left ear, while there's data/code in the right ear. The stereo format has every other sample starting with the first be in the left ear, and every other sample starting with the second be in the right ear. Due to every other sample being used, these have a halved sample rate; 11025Hz instead of 22050Hz.
  
 
In addition, some simply have different sample rates despite being mono.
 
In addition, some simply have different sample rates despite being mono.
Line 12: Line 12:
 
==SWORLD.SND Map==
 
==SWORLD.SND Map==
  
Here's a map of samples that can be found in SWORLD.SND. Start and end addresses are inclusive; they're part of the sample.
+
Here's a map of PCM samples that can be found in SWORLD.SND. Start and end addresses are inclusive; they're part of the sample.
  
 
NOTE: Sample start and end times are currently estimates based on looking at the waveform and patterns in the hex. Finding the exact start and end of each sample will likely require digging into the code.
 
NOTE: Sample start and end times are currently estimates based on looking at the waveform and patterns in the hex. Finding the exact start and end of each sample will likely require digging into the code.
Line 100: Line 100:
 
| $4C721 || $4ED8D || Jump ||
 
| $4C721 || $4ED8D || Jump ||
 
|}
 
|}
 +
 +
==Music and SFX==
 +
 +
The music in Sonic World (and interestingly, the SFX in both Sonic World and the Genesis ports) is performed in real-time via a variation of the MIDI standard (as opposed to just playing back a prerecorded audio file).
 +
 +
A Saturn MIDI file (at least as Sonic Jam handles them) consists of a Header, and a series of Tracks, which consist of a Tempo Track, and Play Data.
 +
 +
===MIDI Header===
 +
 +
A MIDI Header is very simple, consisting of only two components:
 +
{|class="prettytable" style="width:auto;"
 +
! Function || Size || Notes
 +
|-
 +
| Number of Tracks || word ||
 +
|-
 +
| Offset of Track 1 || long || From the top of the header.
 +
|-
 +
| Offset of Track 2 || long || From the top of the header.
 +
|-
 +
| etc. || ||
 +
|}
 +
 +
===Tempo Track===
 +
 +
A Tempo Track defines the Resolution of a track, the Tempo, any Tempo Changes that occur, and when they happen.
 +
{|class="prettytable" style="width:auto;"
 +
! Function || Size || Notes
 +
|-
 +
| Resolution || word || Number of MIDI ticks per quarter-note (typically $01E0). Holding all else equal, lowering the value will slow the apparent tempo. Must be between $0018 and $03C0, inclusive, otherwise the song won't play.
 +
|-
 +
| Number of Tempos || word || The number of Tempo Duration and Tempo pairs defined below. Must have at least 1 pair.
 +
|-
 +
| Length of Tempo Track || word || In bytes. Counting starts at MSB of Resolution.
 +
|-
 +
| Length of Tempo List || word || In bytes. Counting starts at MSB of first Tempo Duration. Must be a multiple of 8 (4 bytes for each Tempo Duration, 4 bytes for each Tempo).
 +
|-
 +
| Tempo 1 Duration || long || The amount of time (in MIDI ticks) to play the song at the following tempo for, before changing to the next tempo (when applicable; otherwise this is ignored).
 +
|-
 +
| Tempo 1 || long || Tempo to play the song at, in microseconds per quarter-note.
 +
|-
 +
| Tempo 2 Duration || long ||
 +
|-
 +
| Tempo 2 || long ||
 +
|-
 +
| etc. || ||
 +
|}
 +
 +
Any tempo changes done within a loop won't work as intended on subsequent loops. The progression of tempos only goes forward, won't jump backwards when a loop is triggered, and the last tempo will play forever rather than wrapping back to the start of the list. This looks to be intentional, reading the Saturn docs.
 +
 +
===Play Data===
 +
 +
The Play Data of a track consists of a continuous series of MIDI events. Each MIDI event consists of a Status byte, and a variable number of parameter bytes, ranging from 0 to 4. All possible MIDI events are defined below.
 +
 +
====$00 - $7F: Note On====
 +
 +
For Note On events, the upper nybble of Status is bit flags.
 +
{|class="prettytable" style="width:auto;"
 +
! Bit Mask || Function
 +
|-
 +
| $10 || Mutes the note when set, essentially changing this event to a rest.
 +
|-
 +
| $20 || Extension bit for Delta Time parameter.
 +
|-
 +
| $40 || Extension bit for Length parameter.
 +
|}
 +
 +
The lower nybble of Status is the channel to play the note on.
 +
 +
Note On events have 4 parameters.
 +
{|class="prettytable" style="width:auto;"
 +
! Parameter || Function || Notes
 +
|-
 +
| $01 || Pitch || Middle C is $3C.
 +
|-
 +
| $02 || Volume || Clamped from $00 to $7F.
 +
|-
 +
| $03 || Length || How long the note plays, in MIDI ticks.
 +
|-
 +
| $04 || Delta Time || How long to wait before triggering this event, in MIDI ticks.
 +
|}
 +
 +
====$80 - $8F: Song Flow====
 +
 +
These events are for manipulating the song's flow.
 +
{|class="prettytable" style="width:auto;"
 +
! Status || Event || Parameters || Notes
 +
|-
 +
| $80 || Invalid || N/A || Abruptly terminates the song, cutting off any active notes.
 +
|-
 +
| $81 || Reference? || 3: Offset (High), Offset (Low), Count || Copies [Count] events, starting with the one at [Offset] from the start of the track. Needs confirmation.
 +
|-
 +
| $82 || Loop Start/End || 1: Delta Time || First call is where loop starts, second call is where the loop ends. Delta Time is in MIDI ticks.
 +
|-
 +
| $83 || End Song || 0 || Immediately ends the song, letting any active notes play out.
 +
|-
 +
| $84 - $87 || Invalid || N/A || Has the same behavior as event $80.
 +
|-
 +
| $88 - $8B || No-op? || 0 || Doesn't seem to actually do anything, as far as I can tell. Might be for spacing reasons? Could use more investigation.
 +
|-
 +
| $8C || One-eighth rest || 0 || Waits for the length of a one-eighth note before proceeding to the next event.
 +
|-
 +
| $8D || Quarter rest || 0 || Ditto, for a quarter note.
 +
|-
 +
| $8E || Whole rest || 0 || Ditto, for a whole note.
 +
|-
 +
| $8F || 2x Whole rest || 0 || Ditto, for two whole notes.
 +
|}
 +
 +
====$90 - $9F: Invalid====
 +
 +
On testing, these all seem to behave the same as event $80.
 +
 +
====$A0 - $AF: Unused====
 +
 +
The MIDI Standard and the Saturn’s docs seem to imply these are supposed to be Polyphonic Key Pressure events; however, the Saturn docs state (inconsistently) that this is unimplemented. Maybe this is just a ghost of that? I certainly don’t notice any difference when I fiddle around with it.
 +
 +
For any who wish to play around with them, they have 3 parameters, the last of which is Delta Time.
 +
 +
====$B0 - $BF: Control Change====
 +
 +
For Control Change events, the lower nybble of Status is the channel to change the control for.
 +
 +
Control Change events have 3 parameters.
 +
{|class="prettytable" style="width:auto;"
 +
! Parameter || Function || Notes
 +
|-
 +
| $01 || Control ID || The control to change. Could use some investigation and documenting.
 +
|-
 +
| $02 || Value || The value to set the control to.
 +
|-
 +
| $03 || Delta Time || How long to wait before triggering this event, in MIDI ticks.
 +
|}
 +
 +
====$C0 - $CF: Program Change====
 +
 +
For Program Change events, the lower nybble of Status is the channel to change the program for.
 +
 +
Program Change events have 2 parameters.
 +
{|class="prettytable" style="width:auto;"
 +
! Parameter || Function || Notes
 +
|-
 +
| $01 || Program || The program to change the channel to. Could use some investigation and documenting.
 +
|-
 +
| $02 || Delta Time || How long to wait before triggering this event, in MIDI ticks.
 +
|}
 +
 +
====$D0 - $DF: Unused====
 +
 +
Similar to the $A_ events. Docs imply they are an unimplemented ghost of Channel Pressure. I can’t notice any changes when I fiddle with it.
 +
 +
2 parameters, the last is Delta Time.
 +
 +
====$E0 - $EF: Pitch Wheel====
 +
 +
For Pitch Wheel events, the lower nybble of Status is the channel to apply the pitch wheel to.
 +
 +
Pitch Wheel events have 2 parameters.
 +
{|class="prettytable" style="width:auto;"
 +
! Parameter || Function || Notes
 +
|-
 +
| $01 || Pitch || The pitch to set the wheel to. Could use some investigation and documenting.
 +
|-
 +
| $02 || Delta Time || How long to wait before triggering this event, in MIDI ticks.
 +
|}
 +
 +
====$F0 - $FF: System Events====
 +
 +
I've yet to see any of these in use, and none of them seem particularly useful for ROM hacking purposes (assuming the docs are accurate).
 +
 +
If someone wants to document them, feel free.
  
 
{{SCHGuides}}
 
{{SCHGuides}}
 
[[Category:SCHG: Sonic Jam]]
 
[[Category:SCHG: Sonic Jam]]

Revision as of 00:42, 23 April 2024

SCHG: Sonic Jam
Main Article
Sonic World

Models
Animations
Textures
Sounds
RAM Editing

PCM Samples

According to my research so far, most PCM samples are in a signed 8-bit mono format, with a sample rate of 22050Hz. Each byte is a building block (one "sample") of the PCM sample as a whole.

There are some exceptions, though.

Some PCM samples are "stereo", in that the sample is in the left ear, while there's data/code in the right ear. The stereo format has every other sample starting with the first be in the left ear, and every other sample starting with the second be in the right ear. Due to every other sample being used, these have a halved sample rate; 11025Hz instead of 22050Hz.

In addition, some simply have different sample rates despite being mono.

SWORLD.SND Map

Here's a map of PCM samples that can be found in SWORLD.SND. Start and end addresses are inclusive; they're part of the sample.

NOTE: Sample start and end times are currently estimates based on looking at the waveform and patterns in the hex. Finding the exact start and end of each sample will likely require digging into the code.

Start Address End Address Description Notes
$004A2 $02D59 Flute (Low)
$02D5A $06A79 Flute (High)
$06A79 $073B1 Snare Drum
$08D54 $09677 Bongo (Low)
$09678 $09CDB Bongo (High)?
$09CDC $0B189 Tom-Tom (Low)?
$0B18A $0C4A2 Maracas?
$0C4A3 $0CFD1 Tom-Tom (High)?
$0FF92 $112B1 Bass Guitar Stereo. Sample's in left ear.
$112B2 $120F0 Trumpet
$120F1 $14D31 Brass
$14D32 $1512F Sawtooth? Unsure if sample(s). Stereo, sawtooth in left ear, but right ear's a pair of sawtooth samples that seem to be stereo too.
$15130 $16719 Steel Drum
$1671A $1CB3B Bass Drum(?) Stereo. Sample's in left ear.
$1CB3C $215D2 Piano (Low)
$215D3 $2611D Piano (High)
$2611E $2982E Synth(?) "Doo"
$2982F $2AD8F Electric Piano Has an almost bell sound to it.
$2AD90 $2EEC7 Synth Pad
$2EEC8 $2F287 Saxophone? Sample's too short to easily tell.
$2F948 $2FEDD Museum Select
$2FEDE $311FD Museum Confirm Not full sound, just the sample used to play it.
$311FE $3234D Unknown
$3234E $3294F Unknown
$32950 $32BAF Mission Button Played on press and release.
$32BB0 $358D1 Big Ring Warp
$35B22 $36548 Landing Played when Sonic lands on the ground.
$36549 $36E68 Unknown
$36E69 $37A11 Spring Mono, sample rate 11025Hz.
$37A12 $381FD Unknown Unsure if sample. Stereo, left ear is sine wave, right ear seems like noise.
$381FE $399F9 Code Monitor Played on interaction.
$399FA $3BE83 Wind Unused.
$3BE84 $3DCF3 Honk Unused.
$3DCF4 $3FBDE Unknown Sounds like rustling, but sounds different from the in-game trees/bushes.
$3FBDF $40020 Unknown
$40021 $423D1 Electric Organ Stereo, sample's in left, right is data/code.
$423D2 $44DD1 Splash
$44DD2 $46E65 Waterfall
$46E66 $4A863 Ring Pick-up
$4A864 $4C720 Balloon Pop
$4C721 $4ED8D Jump

Music and SFX

The music in Sonic World (and interestingly, the SFX in both Sonic World and the Genesis ports) is performed in real-time via a variation of the MIDI standard (as opposed to just playing back a prerecorded audio file).

A Saturn MIDI file (at least as Sonic Jam handles them) consists of a Header, and a series of Tracks, which consist of a Tempo Track, and Play Data.

MIDI Header

A MIDI Header is very simple, consisting of only two components:

Function Size Notes
Number of Tracks word
Offset of Track 1 long From the top of the header.
Offset of Track 2 long From the top of the header.
etc.

Tempo Track

A Tempo Track defines the Resolution of a track, the Tempo, any Tempo Changes that occur, and when they happen.

Function Size Notes
Resolution word Number of MIDI ticks per quarter-note (typically $01E0). Holding all else equal, lowering the value will slow the apparent tempo. Must be between $0018 and $03C0, inclusive, otherwise the song won't play.
Number of Tempos word The number of Tempo Duration and Tempo pairs defined below. Must have at least 1 pair.
Length of Tempo Track word In bytes. Counting starts at MSB of Resolution.
Length of Tempo List word In bytes. Counting starts at MSB of first Tempo Duration. Must be a multiple of 8 (4 bytes for each Tempo Duration, 4 bytes for each Tempo).
Tempo 1 Duration long The amount of time (in MIDI ticks) to play the song at the following tempo for, before changing to the next tempo (when applicable; otherwise this is ignored).
Tempo 1 long Tempo to play the song at, in microseconds per quarter-note.
Tempo 2 Duration long
Tempo 2 long
etc.

Any tempo changes done within a loop won't work as intended on subsequent loops. The progression of tempos only goes forward, won't jump backwards when a loop is triggered, and the last tempo will play forever rather than wrapping back to the start of the list. This looks to be intentional, reading the Saturn docs.

Play Data

The Play Data of a track consists of a continuous series of MIDI events. Each MIDI event consists of a Status byte, and a variable number of parameter bytes, ranging from 0 to 4. All possible MIDI events are defined below.

$00 - $7F: Note On

For Note On events, the upper nybble of Status is bit flags.

Bit Mask Function
$10 Mutes the note when set, essentially changing this event to a rest.
$20 Extension bit for Delta Time parameter.
$40 Extension bit for Length parameter.

The lower nybble of Status is the channel to play the note on.

Note On events have 4 parameters.

Parameter Function Notes
$01 Pitch Middle C is $3C.
$02 Volume Clamped from $00 to $7F.
$03 Length How long the note plays, in MIDI ticks.
$04 Delta Time How long to wait before triggering this event, in MIDI ticks.

$80 - $8F: Song Flow

These events are for manipulating the song's flow.

Status Event Parameters Notes
$80 Invalid N/A Abruptly terminates the song, cutting off any active notes.
$81 Reference? 3: Offset (High), Offset (Low), Count Copies [Count] events, starting with the one at [Offset] from the start of the track. Needs confirmation.
$82 Loop Start/End 1: Delta Time First call is where loop starts, second call is where the loop ends. Delta Time is in MIDI ticks.
$83 End Song 0 Immediately ends the song, letting any active notes play out.
$84 - $87 Invalid N/A Has the same behavior as event $80.
$88 - $8B No-op? 0 Doesn't seem to actually do anything, as far as I can tell. Might be for spacing reasons? Could use more investigation.
$8C One-eighth rest 0 Waits for the length of a one-eighth note before proceeding to the next event.
$8D Quarter rest 0 Ditto, for a quarter note.
$8E Whole rest 0 Ditto, for a whole note.
$8F 2x Whole rest 0 Ditto, for two whole notes.

$90 - $9F: Invalid

On testing, these all seem to behave the same as event $80.

$A0 - $AF: Unused

The MIDI Standard and the Saturn’s docs seem to imply these are supposed to be Polyphonic Key Pressure events; however, the Saturn docs state (inconsistently) that this is unimplemented. Maybe this is just a ghost of that? I certainly don’t notice any difference when I fiddle around with it.

For any who wish to play around with them, they have 3 parameters, the last of which is Delta Time.

$B0 - $BF: Control Change

For Control Change events, the lower nybble of Status is the channel to change the control for.

Control Change events have 3 parameters.

Parameter Function Notes
$01 Control ID The control to change. Could use some investigation and documenting.
$02 Value The value to set the control to.
$03 Delta Time How long to wait before triggering this event, in MIDI ticks.

$C0 - $CF: Program Change

For Program Change events, the lower nybble of Status is the channel to change the program for.

Program Change events have 2 parameters.

Parameter Function Notes
$01 Program The program to change the channel to. Could use some investigation and documenting.
$02 Delta Time How long to wait before triggering this event, in MIDI ticks.

$D0 - $DF: Unused

Similar to the $A_ events. Docs imply they are an unimplemented ghost of Channel Pressure. I can’t notice any changes when I fiddle with it.

2 parameters, the last is Delta Time.

$E0 - $EF: Pitch Wheel

For Pitch Wheel events, the lower nybble of Status is the channel to apply the pitch wheel to.

Pitch Wheel events have 2 parameters.

Parameter Function Notes
$01 Pitch The pitch to set the wheel to. Could use some investigation and documenting.
$02 Delta Time How long to wait before triggering this event, in MIDI ticks.

$F0 - $FF: System Events

I've yet to see any of these in use, and none of them seem particularly useful for ROM hacking purposes (assuming the docs are accurate).

If someone wants to document them, feel free.

Sonic Community Hacking Guide
General
SonED2 Manual | Subroutine Equivalency List
Game-Specific
Sonic the Hedgehog (16-bit) | Sonic the Hedgehog (8-bit) | Sonic CD (prototype 510) | Sonic CD | Sonic CD (PC) | Sonic CD (2011) | Sonic 2 (Simon Wai prototype) | Sonic 2 (16-bit) | Sonic 2 (Master System) | Sonic 3 | Sonic 3 & Knuckles | Chaotix | Sonic Jam | Sonic Jam 6 | Sonic Adventure | Sonic Adventure DX: Director's Cut | Sonic Adventure DX: PC | Sonic Adventure (2010) | Sonic Adventure 2 | Sonic Adventure 2: Battle | Sonic Adventure 2 (PC) | Sonic Heroes | Sonic Riders | Sonic the Hedgehog (2006) | Sonic & Sega All-Stars Racing | Sonic Unleashed (Xbox 360/PS3) | Sonic Colours | Sonic Generations | Sonic Forces
Technical information
Sonic Eraser | Sonic 2 (Nick Arcade prototype) | Sonic CD (prototype; 1992-12-04) | Dr. Robotnik's Mean Bean Machine | Sonic Triple Trouble | Tails Adventures | Sonic Crackers | Sonic 3D: Flickies' Island | Sonic & Knuckles Collection | Sonic R | Sonic Shuffle | Sonic Advance | Sonic Advance 3 | Sonic Battle | Shadow the Hedgehog | Sonic Rush | Sonic Classic Collection | Sonic Free Riders | Sonic Lost World
Legacy Guides
The Nemesis Hacking Guides The Esrael Hacking Guides
ROM: Sonic 1 | Sonic 2 | Sonic 2 Beta | Sonic 3

Savestate: Sonic 1 | Sonic 2 Beta/Final | Sonic 3

Sonic 1 (English / Portuguese) | Sonic 2 Beta (English / Portuguese) | Sonic 2 and Knuckles (English / Portuguese)
Move to Sega Retro
Number Systems (or scrap) | Assembly Hacking Guide | 68000 Instruction Set | 68000 ASM-to-Hex Code Reference | SMPS Music Hacking Guide | Mega Drive technical information