fix(audio-miniaudio): restart sound groups in reset() to restore audio after loadGame#224
Merged
Conversation
…o after loadGame MiniAudioManager::reset() released individual PlayingAudio entries via stopAllAudioImmediately() but never restarted the four ma_sound_group nodes. If a prior pauseAudio() had stopped the groups (e.g. via the ESC menu used to pick 'Load'), they stayed stopped across loadGame(). New sounds attach to the stopped groups via ma_sound_init_from_data_source() and are then silently reaped by processPlayingList() because ma_sound_is_playing() returns false. From the player's perspective this manifests as 'no sound after loading a save' — and toggling the ESC menu appears to fix it because the pause/unpause cycle finally calls resumeAudio() -> ma_sound_group_start(). Fix: restart the groups in MiniAudioManager::reset() so audio works immediately after a save load. Guarded by the per-type *On flags so the call is skipped when openDevice() never initialised the groups (e.g. audio disabled).
coolswood
marked this pull request as draft
July 18, 2026 15:05
coolswood
marked this pull request as ready for review
July 18, 2026 15:25
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes silent audio immediately after loading a save game on the MiniAudio backend (default on macOS/Linux). After this fix, sound works the moment the save finishes loading — the user no longer needs to open and close the ESC menu to "wake up" audio.
Problem
Reproduction (verified on macOS, Beta 14, MiniAudio backend):
Root cause
When the user opens the ESC menu to pick "Load",
GameLogic::setGamePaused(TRUE)runs and eventually reachesMiniAudioManager::pauseAudio(), which callsma_sound_group_stop()on all four groups (m_soundGroup,m_sound3DGroup,m_speechGroup,m_musicGroup).GameState::loadGame()then callsTheGameEngine->reset()→SubsystemInterfaceList::resetAll()→MiniAudioManager::reset(). Insidereset():AudioManager::reset()only touches volume variables.stopAllAudioImmediately()releases each individualPlayingAudio(viama_sound_uninit()), clearingm_playingSounds/m_playing3DSounds/m_playingStreams.ma_sound_groupnodes. They stay in the stopped state set by the earlierpauseAudio().After the load, every newly created sound is attached to one of those stopped groups via
ma_sound_init_from_data_source(... groupToUse, ...). miniaudio does not output audio from children of a stopped group, andMiniAudioManager::processPlayingList()seesma_sound_is_playing()returnfalsefor them and immediately reaps them as "finished". Net result: a fully silent game with an empty playing list — the user perceives this as "no audio at all".Why the ESC menu "fixes" it:
setGamePaused(TRUE)→pauseGameSound(TRUE)— sinceGameLogic::reset()clearedm_pauseSound = FALSE, the early-return atGameLogic.cpp:4545does not fire, som_pauseSoundbecomesTRUEandpauseAudio()is invoked again (idempotent — groups are already stopped).setGamePaused(FALSE)→pauseGameSound(FALSE)—m_pauseSound == TRUEandpaused == FALSE, so the early-return does not fire, andresumeAudio()finally runs →ma_sound_group_start()on all groups. Audio is restored.Fix
Restart the four groups in
MiniAudioManager::reset()so that any prior stopped state frompauseAudio()is cleared by the time new sounds start being created after a load.The guard via the per-type
*Onflags (set toFALSEbysetOn(false, AudioAffect_All)whenopenDevice()fails to initialise the engine) ensures we never callma_sound_group_start()on an uninitialisedma_sound_group— which would happen ifopenDevice()returned early beforema_sound_group_init()ran (e.g.TheGlobalData->m_audioOn == false, or any of thema_*_initcalls inopenDevicefailing).ma_sound_group_start()is idempotent, so calling it on an already-started group during normalreset()paths (e.g. engine teardown, level transitions) is harmless.Why this layer and not GameLogic / GameState
GameLogic/GameStatefree of audio-backend-specific concerns.MiniAudioManager::reset()self-consistent: it now guarantees the device + groups are in a state ready to play, mirroring what the OpenAL backend achieves implicitly (per-sourcealSourcePlayin its ownresumeAudio).OpenALAudioManager::reset()does not have this bug because itspauseAudio()/resumeAudio()operate per-source (alSourceStop/alSourcePlay), andstopAllAudioImmediately()fully tears down sources — so the concept of a lingering "stopped group" does not exist there.Verification
macos-vulkan.Load Gamecompletes — no ESC toggle needed.AI usage disclosure
Per
CONTRIBUTING.md: this change is the result of human + AI collaboration. The bug was diagnosed together (static analysis of the load/audio code paths plus live testing); the fix was written and verified by the human author of this PR.Checklist
// GeneralsX @bugfix author DD/MM/YYYY ...inline comment conventionfix(audio-miniaudio): ...)MiniAudioManager.cpp