Audio & Music
The original's 93 sound channels played as the original plays them, and its ten music tracks, one per world (as of 2026-11-10).
| Part | Files | Code |
|---|---|---|
| Sound effects | Content/sounds/sound000.wav … sound092.wav (93 files) | Sound (src/GalaxyEggbert/Game/Sound.{hpp,cpp}), channel numbers in include/GalaxyEggbert/Def/SoundChannel.hpp |
| Vehicle motors | channels 15-18 and 28-31 | GalaxyEggbertGame::UpdateVehicleMotorSound(), StopVehicleMotorSound() |
| Music | Content/music/music000.ogg … music009.ogg (10 tracks, 27 MB) | GalaxyEggbertGame::UpdateMusic(), ApplyMusicVolume() in src/GalaxyEggbertCNA/GalaxyEggbertGame.cpp |
| Playback | — | CNA's XNA audio API (SoundEffect, SoundEffectInstance, Song, MediaPlayer) over SDL3_mixer |
Sound effects
A sound is a channel, Def::SoundChannel 0-92, and the channel number is the file number:
SoundChannel10 plays sound010.wav. The numbers are the original's and must never be renumbered.
Channel 0 is reserved; the game uses it to mean "no sound". The WAV files are copies of Mobile Eggbert's and are
played as they are.
Volume and pitch, as the original
Each channel has a volume multiplier and a pitch in XNA's octaves, ported with approval from Mobile Eggbert's
tableVolumePitch (kVolumePitch in Sound.cpp). The table's pitch replaces any other: most channels
play 0.1-0.5 octave up, the life-loss sounds (1, 2) and Blupi's push and pull efforts (38, 39) a whole octave up.
The pitch was missing until 2026-11-02, when every effect played lower than the original's (BD-0031).
The original's conflict rule is kept: a channel that is already playing is not restarted, except channel 10.
Each channel owns one SoundEffectInstance; Stop(channel) ends one channel (a crate push's sound when the
push ends) without silencing the others.
Positional sound
The listener is Blupi: the game calls SetListener() with his position every frame of play. A sound of the world
is played with PlayAt(channel, x, y, z) and is heard only within the original's reach of the view: the original
plays a sound on the screen and up to one screen beyond each side, and not at all farther (BD-0076). In 3D that is
15 cells from Blupi along X and along Z, and 11.25 cells up or down (Sound::kAudibleAcross,
kAudibleUpDown). Inside that box a sound plays at its table volume; there is no fading with distance and no
stereo panning (GP-0412).
The ground underfoot
Landing plays channel 3; there are no footsteps while walking, as in the original (BD-0037). Like the original's
Decor::SoundEnviron(), FootstepChannelFor(icon) swaps channel 3 for a material's own sound when the tile
underfoot is in one of seven icon ranges, and CeilingChannelFor(icon) swaps channel 4 (a ceiling met while rising)
for the next channel of the same pair.
| Tile icons | Landing | Ceiling |
|---|---|---|
| 32-34, 41-47, 139-143 | 78 | 79 |
| 1-28, 78-90, 250-260, 311-316, 324-329 | 80 | 81 |
| 284-303, 338 | 82 | 83 |
| 341-363 | 84 | 85 |
| 215-234 | 86 | 87 |
| 246-249 | 88 | 89 |
| 107-109 (grass) | 90 | 91 |
| anything else | 3 | 4 |
When sounds are left out
- Blupi hidden (the lollipop's Hide power): his own sounds (steps, landings, jumps, water, crate efforts,
taps and falls;
Sound::MutedWhileHidden()) are not played; the world's still are (BD-0075).PlayDirect()bypasses this for the few sounds the original played directly. - Replays: while an interrupted mission is being replayed to its resume point, or a demo file is being verified,
SetReplayMuted(true)silences everything. A demo playing in the main menu is heard, with the player's own sound settings. - Settings: the Setup's sound switch (
SetEnabled) and its effects-volume slider, 0-100 insavedata.txtaseffectsVolume=, which multiplies every channel's table volume (a Galaxy addition; the original has only sound on/off).
Vehicle motors
UpdateVehicleMotorSound() runs every tick of play and follows the original's AdaptMotorVehicleSound(): a
start sound when a motor starts, a loop while it runs (the "high" loop while the vehicle moves, the "low" one while it
idles, BlupiController::IsVehicleMotorHigh()), a stop sound when it stops. The skateboard has no motor.
| Vehicle | Start | Loop moving / idle | Stop |
|---|---|---|---|
| Helicopter | 15 | 16 / 18 | 17 |
| Jeep, tank, overcraft | 28 | 29 / 31 | 30 |
The loops belong to the live vehicle. StopVehicleMotorSound() stops all eight channels whenever the phase changes to
anything but Play (pause, menus, the next world), when an editor play-test world is replaced and when the in-game editor
is opened on a mission. Back in Play, the next update starts the current vehicle's loop again; the main menu and a new
world on foot keep no engine sound. This was verified against the audio instances' states with a dummy audio driver.
Music
The ten tracks are the original Speedy Eggbert 1 (1998) MIDI files SOUND/MUSIC000-009.BLP, rendered by
tools/music/render_music.py: libfluidsynth through ctypes with a General MIDI soundfont, then, with
--ogg, ffmpeg cuts the trailing silence, normalises loudness (EBU R128, -18 LUFS) and encodes Vorbis.
python3 tools/music/render_music.py <midi...> --out <dir> [--soundfont <file.sf2>] [--ogg]
One track per world
A world stores its track in the second byte of the header's sky-region word (World::musicTrack()): 0 is no music,
1-10 play music000.ogg-music009.ogg. The world generators default to the original level's own
music= value (ORIGINAL_MUSIC in tools/worldkit.py), and the 3D editor sets it from
its palette. In the shipped files, 15 of the 81 worlds are silent: the title scene, the main hub world001, the world
hubs world020-world120, the gallery world998 and the showcase world999 (the hub
world010 plays track 10).
When it plays
UpdateMusic() runs every update. A track plays while a world is shown in Play, Pause, the in-play Setup or the editor
(not its slot browser), if sound is on; never in autoplay, while a resume is replayed or a demo verified. The main menu is
silent. The track loops (MediaPlayer repeating) until the world's track changes. MediaPlayer's volume is
0.5 × the Setup's music slider / 100 (musicVolume=, 0-100): at full slider the music sits at half volume under
the effects (OR-0014's mix).
On the web
The web build does not pack the music. Content/music/ is staged beside GalaxyEggbert.html, and the first time a
world asks for a track the game fetches it into the virtual file system (emscripten_async_wget); until it has arrived,
nothing plays and the next frame asks again. A deployment that forgets to copy Content/music/ next to the page
simply has no music.
The audio backend
The game uses only CNA's XNA audio and media classes. In the shipped builds CNA plays them through SDL3 and SDL3_mixer,
which CNA builds from its own third_party/; the Linux bundle includes the SDL3_mixer library and the Windows staging
copies SDL3_mixer.dll. Browsers start audio only after a user gesture, so on the web the first sound may wait for a
click or key press.
Tests
VerifyInteractionSystem (CTest) checks Sound::MutedWhileHidden() against the original's list,
Sound::Audible() at the edges of the box, and every range of FootstepChannelFor() and
CeilingChannelFor(). It links Sound without loading any file, so every Play() there is silent.
VerifyGESaveData checks that the two volumes default to 100, clamp and survive a save.