Purple MoonSound
The music reads the room
How Skies of Arcadia's boss battles change their music with the fight, and why almost none of the game's music is a recording.
If you’ve played Skies of Arcadia, you already know this one by heart: the big battles keep an eye on your health bar, and the soundtrack changes with the fight. Fall into danger and the music turns tense; get the boss on the ropes and the triumphant theme kicks in.
I, on the other hand, met the game through its decompiled code, so I found out the other way round: by reading a function that watches everyone’s HP and wondering why a music task would care. Fans know that it happens. This post is about how, and the answer turns out to be delightfully simple.
But first, a detour, because you need to know what a “song” even is in this game.
The music is played, not recorded
Almost none of the music on the disc is a recording. Each background song is a
sequence: note data, a lot like a MIDI file, that plays a bank of short instrument
samples live, through Factor 5’s MusyX sound engine. On the GameCube disc each song is
a pair of files in the /sound/ folder:
mXXXX.info: the song itself and its instrument directory;mXXXX.samp: the instrument samples, in the GameCube’s 4-bit DSP-ADPCM format.
There are 119 of these pairs. Every time a song plays, the console is performing it, note by note, mixing the voices at 32 kHz.
Only a handful of things are real recordings, stored as stereo streams: the intro and title music, and the voice clips. The title theme is a 22,050 Hz stream, 93.4 seconds long, and the title screen uses it as a clock. It waits for the music to finish, and after about 92 seconds (0x1686B milliseconds, to be exact) it gives up on you and plays the opening demo instead.
A side effect of playing the music live: every instrument is only as good as the rate
its sample was stored at. In m0231, half the instrument samples are stored below
16 kHz, and a few at just 2 to 3 kHz. That’s a big part of why the GameCube music
sounds a little duller than the Dreamcast original. More on that below.
Ordinary battles don’t care
The port let us test this directly. Its cheat tools can poke anyone’s HP in the middle of a battle, so in an ordinary battle we dropped Vyse to 3 HP, and then the enemy to 3 HP. The music didn’t flinch. Ordinary battles play one song from the first turn to the victory fanfare.
Boss battles score the fight
The big battles are different. Every battle loads a battle bank, m0458 to m0462,
and two of them, m0460 and m0462, aren’t songs at all. They’re sets of
segments: 14 in m0460, 15 in m0462.
Something has to pick which segment plays, and that something is a little task the
game runs every frame, at fn_8007D42C in the decompilation. (Before anyone had worked
out what it did, its notes called it a “party recovery advisor”, which tells you it
reads HP and not much else.) It watches everyone’s HP and keeps the battle in one of
three moods:
- Even: the normal battle loop.
- Danger: any party member under 25% HP, or the party as a whole under 40%.
- Winning: the enemies are down to under half their total HP.
There’s one lovely detail in the rules. You enter Danger at 25% for anyone or 40% for the party, but you only leave it once nobody is under 25% and the party is back up to 75%. That gap is hysteresis, the same trick a thermostat uses so it doesn’t click on and off all day. A quick heal that just scrapes you out of trouble doesn’t make the music flip-flop between tense and calm every turn.
When the mood changes, the game stops the music, plays a short sting to mark the change, and then loops the new mood’s segments, round and round:
| Bank | Even | Danger | Winning | Stings |
|---|---|---|---|---|
| m0460 | 0–3 | 4–5 | 6–7 | 8–13 |
| m0462 | 0–7 | (plays Even) | 8–11 | 12–14 |
And that’s the whole trick. The game never mutes instruments, never changes the tempo, never cross-fades two songs. It only starts and stops whole segments, with a sting to cover the seam. (The one place the game does reach into a playing song is the world map, where the ambience turns six tracks up or down depending on how close your ship is to certain points on the map.)
A confession: we know all this from reading the code, and that code matches the retail game byte for byte, so it’s exactly what shipped. But the port can’t yet play far enough into the game to reach one of these battles, so we haven’t heard it switch moods in a run. That’s one to look forward to.
It all started on the Dreamcast
Legends is a port of the 2000 Dreamcast original, and its sound is a conversion of the Dreamcast’s. You can still see the seams:
- Same names. Every GameCube song has a Dreamcast twin with the same name.
m0231.infoon the GameCube isM0231.MLTon the Dreamcast. The GameCube code still asks for files like/sound/m5000000.mlt..mltis the Dreamcast’s sound bank format, and the GameCube loader quietly throws the extension away and loads.infoand.sampinstead. Nobody ever went back and changed the names. - Same battle design. The Dreamcast battle banks are cut into segments the same
way: 14 in
M0460, 15 inM0462. So the mood system almost certainly came over from the original. - A whole different engine. On the Dreamcast the music runs on the sound chip’s own
ARM7 CPU, under Sega’s driver,
BONITO.DRV(“bonito ver.2.00”, dated 7 February 2000). The game writes 16-byte commands into sound memory, along the lines of “play sequence 0 of bank 2 on port 0”, and the driver plays them on the chip’s 64 voices at 44.1 kHz. - The GameCube has more sample data, not less.
m0231.sampis 504 KB. The Dreamcast version of that song has a 62 KB bank of its own (plus 1.08 MB of instruments shared by every song). So the one-disc GameCube release didn’t squeeze the music to save space. The difference in quality comes from the conversion: samples re-encoded to ADPCM at odd rates, a 32 kHz mix instead of 44.1 kHz, and an engine without the Dreamcast chip’s per-voice filters. - The title theme is the same recording. The Dreamcast stores it as plain 16-bit audio at 44.1 kHz, the GameCube as 4-bit ADPCM at half that rate. Line the two up and they correlate at 0.9998: the same master. The GameCube copy just carries a layer of ADPCM hiss, about 28 to 32 dB below the music.
That last finding is behind one of the port’s plans: an optional Dreamcast music pack, built at install time from your own Dreamcast discs. And because the boss battles only ever switch whole segments, a recording of each segment is all a music pack needs to follow the fight, mood swings and all.