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).

PartFilesCode
Sound effectsContent/sounds/sound000.wav … sound092.wav (93 files)Sound (src/GalaxyEggbert/Game/Sound.{hpp,cpp}), channel numbers in include/GalaxyEggbert/Def/SoundChannel.hpp
Vehicle motorschannels 15-18 and 28-31GalaxyEggbertGame::UpdateVehicleMotorSound(), StopVehicleMotorSound()
MusicContent/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 iconsLandingCeiling
32-34, 41-47, 139-1437879
1-28, 78-90, 250-260, 311-316, 324-3298081
284-303, 3388283
341-3638485
215-2348687
246-2498889
107-109 (grass)9091
anything else34

When sounds are left out

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.

VehicleStartLoop moving / idleStop
Helicopter1516 / 1817
Jeep, tank, overcraft2829 / 3130

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.

Verified. Linux desktop: checked live (the demos with sound, the motor loops' states). Web, in headless Chrome: all 93 channels load, and the music of mission 11 plays (checked 2026-11-09). Android: the APK builds and starts in an emulator; audio on a real device is not verified.

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.

Related pages