This folder holds The Broken Seal's sound: the game's cue layer over the sound driver, and the driver itself, Nintendo's MusicPlayer2000 as this game links it.
See also: Runtime, whose VBlank handler steps the music's ramps and enters the mixer.
| File | What it holds |
|---|---|
| sound.c | the cue layer: playing a cue (a song, a sound effect, a fanfare or a fade), the music's volume, tempo and pitch ramps, the fanfare's return to the music, and the audio test |
| engine.c | the driver: the sound hardware, the players and their tracks, the song commands and the CGB channels |
| audio.c | the driver's library calls: starting, stopping, fading and resuming songs on the players, and the PCM key-to-frequency step |
| tables.c | the driver's tables: the song command handlers and the frequency tables |
| empty_callback.c | the driver's do-nothing callback |
| mixer.s, channel_update_entry.s, clear_buffer.s, umul_high.s | the driver's handwritten ARM and Thumb routines, each proven in its header |
engine.c, audio.c and tables.c are compiled with pret's old_agbcc, the sound route of the Makefile (SOUND_C), as pret builds its MusicPlayer2000 C; the rest with the game's compiler. The songs and samples are in games/tbs/sound, and the songs' names in songs.h (MUS_, SE_). Link order is in games/tbs/objects.tsv. The unit headers are in games/tbs/include/unit/sound/, the driver's structures in games/tbs/include/sound.
| Prefix | What it acts on |
|---|---|
| AudioCommand_, Audio_, AudioTest_ | the cue layer and the audio test |
| AudioEngine_ | the sound hardware and the driver's work |
| MusicPlayer_, MusicTrack_, MusicCommand_ | players, tracks and song commands |
| Cgb_, CgbChannel_, CgbNote_ | the CGB channels |
| Sound_, Runtime_ | things the engine names elsewhere |
The spellings some sites keep to match, and the comment markers, are in Matching.
The game plays every sound through one call, AudioCommand_Play(id), and the number decides what kind of sound it is. Its low 12 bits are the cue and its top four bits flags (AUDIO_CUE_START_SILENT starts music at volume 0). A cue from 100 up (AUDIO_SONG_FIRST) is a song of the song table played on the player its row names; a row that names player 7 takes the first free player counting down from 7 to 4, and player 7 again if none is free, so up to four such effects sound at once and a fifth cuts off player 7's. A cue from 80 to 99 (AUDIO_JINGLE_FIRST) is a fanfare. Below 80 is music. Three cues are controls instead: 17 fades the music out, 18 keeps the music that is playing, and 0x121 fades player 3. Asking for the music that is already playing does nothing, so a scene can name its music on every entry without restarting it.
A fanfare does not stop the music. AudioCommand_Play sets the music's volume to 0 at once and plays the fanfare, and sets a delay of ten frames. Once a VBlank, MusicPlayer_StepVolumeAndPitchTowardTargets (called from System_VBlankHandler) counts the delay down; when it reaches 1 it waits for the fanfare's player to stop, and then sets the music's target volume back to full. The same routine steps the volume, and the combined tempo and pitch, towards their targets by a fixed step each VBlank, and stops on the target when a step would cross it. So the music resumes where it was, fading back in, and nothing in the waiting code has to know a fanfare played.
The driver is called from two places that can interrupt each other: the game calls the library (audio.c, engine.c) from its frame loop, and the VBlank handler enters the mixer last of all (SoundDriver_EnterMixer). They share one word, work->lock, and each player has one of its own.
The word is both a signature and a lock. SOUND_LOCK is 0x68736d53, the bytes Smsh; AudioEngine_Initialize sets it to 0 while it clears the driver's work and to SOUND_LOCK when the driver is ready. Every library call that changes the driver's state checks that the word is exactly SOUND_LOCK, adds 1 for the length of its work, and puts SOUND_LOCK back. The mixer's entry runs only when the word is exactly SOUND_LOCK, and its exit stores the signature again. So:
XXX: whether a dropped call ever matters in the game is not known.
The PCM mixer is copied into IWRAM (Ram_SoundMixer, 0x03007000) so that it runs from fast memory, and part of it is replaced per song: Sound_LoadPresetParameters copies one of five 152-byte records of mixer code over it. Music uses record 3 for three songs (MUS_067, MUS_070 and MUS_075) and record 2 for the rest. XXX: what the records change in the mix, and why those three songs, is not known.
The driver's names follow pret's for MusicPlayer2000 where its structure is the same; the cue layer's come from what AudioCommand_Play does with each range of numbers, and the songs' from songs.h, where a song without a proven name keeps its number (MUS_070, SE_188).
Still unknown:
Source: src/sound