Nintendo MusicPlayer2000 ("Sappy" / "m4a")
Klonoa: Empire of Dreams uses the standard GBA sound engine known as Nintendo MusicPlayer2000, commonly referred to as "Sappy" or "m4a" in the decomp community. This is the same engine found in Pokemon, Kirby, Fire Emblem, and most first-party and many third-party GBA titles.
| Region | Address Range | Size | Content |
|---|---|---|---|
| Sound tables | 0x08117xxx | ~3 KB | Pitch, frequency, command dispatch LUTs |
| Voice tables | 0x08117CBC – 0x081189xx | ~3 KB | 3 instrument/voice group tables |
| PCM samples | 0x0811BB00 – 0x08163044 | ~300 KB | Signed 8-bit PCM waveform data |
| Song/track data | 0x0816CB04 – 0x08184604 | ~100 KB | Song headers + bytecode track data |
| Song pointer table | 0x08118AE4 | ~1.3 KB | 165 entries (39 songs + 126 SFX) |
Located at ROM_MUSIC_META_TABLE = 0x08118AE4.
Contains 165 entries of the form:
struct SongTableEntry {
u32 songHeaderPtr;
u32 flags; // songs: 0, SFX: encodes priority
};
struct SongHeader {
u8 numTracks;
u8 blockCount;
u16 priority;
u32 voiceGroupPtr;
u32 trackPtrs[numTracks];
};
Proprietary MIDI-like bytecode. Encodes notes (0x81–0xB0),
waits (0x00–0x7F), and control commands (tempo, instrument
select, volume, pan, pitch bend, loops, subroutines).
struct VoiceEntry {
u8 type; // see Voice/Instrument Types table
u8 baseKey;
u16 flags;
u32 samplePtr;
u32 adsr;
};
struct SampleHeader {
u32 flags;
u32 pitch;
u32 loopOffset;
u32 length;
// followed by signed 8-bit PCM data
};
Songs range from 3–8 tracks. Most use the main voice table at
0x08117FD4.
| ID | ROM Addr | Tracks | Priority | Voice Table | Est. Size |
|---|---|---|---|---|---|
| 0 | 0x0816CB04 | 7 | 0x9E | 0x08117FD4 | 200 |
| 1 | 0x0816CBCC | 4 | 0xA8 | 0x08117FD4 | 588 |
| 2 | 0x0816CE18 | 3 | 0x94 | 0x081185C8 | 572 |
| 3 | 0x0816D054 | 4 | 0x9E | 0x08117FD4 | 2440 |
| 4 | 0x0816D9DC | 6 | 0x80 | 0x08117FD4 | 1556 |
| 5 | 0x0816DFF0 | 6 | 0x80 | 0x08117FD4 | 2116 |
| 6 | 0x0816E834 | 6 | 0x80 | 0x08117FD4 | 2004 |
| 7 | 0x0816F008 | 7 | 0x80 | 0x08117FD4 | 1904 |
| 8 | 0x0816F778 | 6 | 0x80 | 0x08117FD4 | 1624 |
| 9 | 0x0816FDD0 | 7 | 0x80 | 0x08117FD4 | 2040 |
| 10 | 0x081705C8 | 6 | 0x80 | 0x08117FD4 | 2060 |
| 11 | 0x08170DD4 | 7 | 0x80 | 0x081185C8 | 1808 |
| 12 | 0x081714E4 | 7 | 0xA8 | 0x081185C8 | 536 |
| 13 | 0x081716FC | 4 | 0x99 | 0x08117FD4 | 756 |
| 14 | 0x081719F0 | 5 | 0x99 | 0x08117FD4 | 508 |
| 15 | 0x08171BEC | 3 | 0xB2 | 0x08117FD4 | 1156 |
| 16 | 0x08172070 | 6 | 0x99 | 0x08117FD4 | 440 |
| 17 | 0x08172228 | 4 | 0x99 | 0x08117FD4 | 660 |
| 18 | 0x081724BC | 6 | 0x99 | 0x08117FD4 | 988 |
| 19 | 0x08172898 | 5 | 0x99 | 0x08117FD4 | 1180 |
| 20 | 0x08172D34 | 7 | 0x99 | 0x08117FD4 | 1020 |
| 21 | 0x08173130 | 6 | 0x99 | 0x08117FD4 | 1176 |
| 22 | 0x081735C8 | 6 | 0x99 | 0x08117FD4 | 1756 |
| 23 | 0x08173CA4 | 5 | 0x99 | 0x08117FD4 | 1324 |
| 24 | 0x081741D0 | 6 | 0x99 | 0x08117FD4 | 488 |
| 25 | 0x081743B8 | 5 | 0x99 | 0x08117FD4 | 564 |
| 26 | 0x081745EC | 6 | 0x99 | 0x08117FD4 | 1644 |
| 27 | 0x08174C58 | 6 | 0x99 | 0x08117FD4 | 804 |
| 28 | 0x08174F7C | 6 | 0x94 | 0x08117FD4 | 920 |
| 29 | 0x08175314 | 5 | 0x99 | 0x081185C8 | 2284 |
| 30 | 0x08175C00 | 6 | 0x80 | 0x08117FD4 | 180 |
| 31 | 0x08175CB4 | 3 | 0x80 | 0x081185C8 | 712 |
| 32 | 0x08175F7C | 7 | 0x99 | 0x081185C8 | ~57K |
| 33 | 0x08183F7C | 3 | 0x9E | 0x08117CBC | 1672 |
| 34 | 0x08184604 | 8 | 0x9E | 0x081185C8 | — |
| Byte Range | Meaning |
|---|---|
| 0x00 – 0x7F | Wait / duration values |
| 0x80 | Rest |
| 0x81 – 0xB0 | Note on (MIDI note number) |
| 0xB1 | End of track (FINE) |
| 0xB2 | Goto (loop) |
| 0xB3 | Pattern call (subroutine) |
| 0xB4 | Pattern return |
| 0xBB | Set tempo (BPM) |
| 0xBC | Set key shift (transpose) |
| 0xBD | Select voice / instrument |
| 0xBE | Set volume |
| 0xBF | Set panning |
| 0xC0 | Pitch bend |
| 0xC1 – 0xCF | Various controllers |
| 0xCE | Extended command |
| Type | Value | Description |
|---|---|---|
| DirectSound | 0x00 | PCM sample playback |
| Square1 | 0x01 | CGB Square wave with sweep |
| Square2 | 0x02 | CGB Square wave |
| Wave | 0x03 | CGB Programmable wave |
| Noise | 0x04 | CGB Noise |
| KeySplit | 0x08 | Split by key range |
| Drumset | 0x40 | Percussion kit |
| Idx | Type | Key | Sample Ptr | ADSR |
|---|---|---|---|---|
| 0 | DirectSound | 60 | 0x08143F1C | 0x0000E7FF |
| 1 | DirectSound | 60 | 0x081446BC | 0x6BCBEC80 |
| 2 | Square2 | 60 | — | duty=2, len=512 |
| 3 | DirectSound | 60 | 0x081463A8 | 0x7FE500FF |
| 4 | Noise | 60 | — | period=15 |
| 5 | DirectSound | 60 | 0x08130D1C | 0x9500F9FF |
| 6 | DirectSound | 60 | 0x081229B0 | 0xA5E5E340 |
| 7 | DirectSound | 60 | 0x08128064 | 0x0000EBFF |
| 8 | DirectSound | 60 | 0x08148CCC | 0x3C33FD80 |
| 9 | Square1 | 60 | — | sweep=3, duty=5 |
| 10 | Square2 | 60 | — | duty=2, len=512 |
| 11 | Square2 | 60 | — | duty=3, len=1280 |
| 12 | Noise | 60 | — | duty=2 |
| 13 | Square1 | 60 | — | sweep=2, len=0xB02 |
| 14 | Square2 | 60 | — | duty=1, len=0xD02 |
| 15 | Drumset | 0 | 0x081188B0 | (sub-table) |
The m4a format is compatible with standard GBA music tools:
51 of 113 m4a/sound functions matched (byte-exact C reimplementations). Recent progress includes
major breakthroughs in the core MP2000 engine functions using register pinning via asm
barriers and the -ftst compiler flag.
The m4a engine is shared across virtually all GBA games. The pret/pokeemerald project has the most complete decompilation with established naming conventions. We align our function names with theirs for cross-project consistency:
22 functions have been renamed to match pokeemerald (m4aSongNumStart, MPlayStart, m4aMPlayAllStop, SoundClear, m4aSoundVSyncOff/On, m4aSoundMode, SampleFreqSet, TrackStop, MidiKeyToFreq, CgbOscOff, ply_voice/vol/pan/bend/bendr/lfos/lfodl/mod/keysh). Game-specific wrappers (StreamCmd_*, SoundEffect*) keep Klonoa-specific names.