From 534f31e1c4dd30f9c1afa81fde0fdc238f6a1133 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:17:21 +1000 Subject: [PATCH] refactor: hide miniaudio behind PIMPL in SoundEngine --- targets/app/common/Audio/SoundEngine.cpp | 83 ++++++++++++++---------- targets/app/common/Audio/SoundEngine.h | 26 +++++--- 2 files changed, 66 insertions(+), 43 deletions(-) diff --git a/targets/app/common/Audio/SoundEngine.cpp b/targets/app/common/Audio/SoundEngine.cpp index ea647e328..543d15ee6 100644 --- a/targets/app/common/Audio/SoundEngine.cpp +++ b/targets/app/common/Audio/SoundEngine.cpp @@ -117,13 +117,28 @@ char SoundEngine::m_szRedistName[] = {"redist64"}; // Linux specific functions #if defined(__linux__) -SoundEngine::SoundEngine() {} + +// PIMPL'd state for the miniaudio backend. Defined here so SoundEngine.h +// stays free of miniaudio.h. +struct SoundEngineMiniAudio { + ma_engine engine{}; + ma_engine_config engineConfig{}; + ma_sound musicStream{}; +}; + +struct MiniAudioSound { + ma_sound sound; + AUDIO_INFO info; + bool active; +}; + +SoundEngine::SoundEngine() : m_audio(std::make_unique()) {} +SoundEngine::~SoundEngine() = default; std::vector m_activeSounds; void SoundEngine::init(Options* pOptions) { app.DebugPrintf("---SoundEngine::init\n"); random = new Random(); - memset(&m_engine, 0, sizeof(ma_engine)); - memset(&m_engineConfig, 0, sizeof(ma_engine_config)); + *m_audio = SoundEngineMiniAudio{}; m_musicStreamActive = false; m_StreamState = eMusicStreamState_Idle; m_iMusicDelay = 0; @@ -149,15 +164,15 @@ void SoundEngine::init(Options* pOptions) { sizeof(int) * (static_cast(eSoundType_MAX) + static_cast(eSFX_MAX))); memset(m_ListenerA, 0, sizeof(AUDIO_LISTENER) * XUSER_MAX_COUNT); - m_engineConfig = ma_engine_config_init(); - m_engineConfig.listenerCount = MAX_LOCAL_PLAYERS; + m_audio->engineConfig = ma_engine_config_init(); + m_audio->engineConfig.listenerCount = MAX_LOCAL_PLAYERS; - if (ma_engine_init(&m_engineConfig, &m_engine) != MA_SUCCESS) { + if (ma_engine_init(&m_audio->engineConfig, &m_audio->engine) != MA_SUCCESS) { app.DebugPrintf("Failed to initialize miniaudio engine\n"); return; } - ma_engine_set_volume(&m_engine, 1.0f); + ma_engine_set_volume(&m_audio->engine, 1.0f); m_MasterMusicVolume = 1.0f; m_MasterEffectsVolume = 1.0f; @@ -166,7 +181,7 @@ void SoundEngine::init(Options* pOptions) { m_bSystemMusicPlaying = false; } -void SoundEngine::destroy() { ma_engine_uninit(&m_engine); } +void SoundEngine::destroy() { ma_engine_uninit(&m_audio->engine); } void SoundEngine::play(int iSound, float x, float y, float z, float volume, float pitch) { @@ -222,7 +237,7 @@ void SoundEngine::play(int iSound, float x, float y, float z, float volume, s->info.pitch = pitch; s->info.bIs3D = true; - if (ma_sound_init_from_file(&m_engine, finalPath, MA_SOUND_FLAG_ASYNC, + if (ma_sound_init_from_file(&m_audio->engine, finalPath, MA_SOUND_FLAG_ASYNC, nullptr, nullptr, &s->sound) == MA_SUCCESS) { ma_sound_set_spatialization_enabled(&s->sound, MA_TRUE); ma_sound_set_min_distance(&s->sound, 2.0f); @@ -274,7 +289,7 @@ void SoundEngine::playUI(int iSound, float volume, float pitch) { s->info.pitch = pitch; s->info.bIs3D = false; - if (ma_sound_init_from_file(&m_engine, finalPath, MA_SOUND_FLAG_ASYNC, + if (ma_sound_init_from_file(&m_audio->engine, finalPath, MA_SOUND_FLAG_ASYNC, nullptr, nullptr, &s->sound) == MA_SUCCESS) { ma_sound_set_spatialization_enabled(&s->sound, MA_FALSE); ma_sound_set_volume(&s->sound, volume * m_MasterEffectsVolume); @@ -410,14 +425,14 @@ int SoundEngine::OpenStreamThreadProc(void* lpParameter) { const char* ext = strrchr(soundEngine->m_szStreamName, '.'); if (soundEngine->m_musicStreamActive) { - ma_sound_stop(&soundEngine->m_musicStream); - ma_sound_uninit(&soundEngine->m_musicStream); + ma_sound_stop(&soundEngine->m_audio->musicStream); + ma_sound_uninit(&soundEngine->m_audio->musicStream); soundEngine->m_musicStreamActive = false; } ma_result result = ma_sound_init_from_file( - &soundEngine->m_engine, soundEngine->m_szStreamName, - MA_SOUND_FLAG_STREAM, nullptr, nullptr, &soundEngine->m_musicStream); + &soundEngine->m_audio->engine, soundEngine->m_szStreamName, + MA_SOUND_FLAG_STREAM, nullptr, nullptr, &soundEngine->m_audio->musicStream); if (result != MA_SUCCESS) { app.DebugPrintf( @@ -427,8 +442,8 @@ int SoundEngine::OpenStreamThreadProc(void* lpParameter) { return 0; } - ma_sound_set_spatialization_enabled(&soundEngine->m_musicStream, MA_FALSE); - ma_sound_set_looping(&soundEngine->m_musicStream, MA_FALSE); + ma_sound_set_spatialization_enabled(&soundEngine->m_audio->musicStream, MA_FALSE); + ma_sound_set_looping(&soundEngine->m_audio->musicStream, MA_FALSE); soundEngine->m_musicStreamActive = true; @@ -503,19 +518,19 @@ void SoundEngine::playMusicTick() { } ma_sound_set_spatialization_enabled( - &m_musicStream, + &m_audio->musicStream, m_StreamingAudioInfo.bIs3D ? MA_TRUE : MA_FALSE); if (m_StreamingAudioInfo.bIs3D) { ma_sound_set_position( - &m_musicStream, m_StreamingAudioInfo.x, + &m_audio->musicStream, m_StreamingAudioInfo.x, m_StreamingAudioInfo.y, m_StreamingAudioInfo.z); } - ma_sound_set_pitch(&m_musicStream, m_StreamingAudioInfo.pitch); + ma_sound_set_pitch(&m_audio->musicStream, m_StreamingAudioInfo.pitch); ma_sound_set_volume( - &m_musicStream, + &m_audio->musicStream, m_StreamingAudioInfo.volume * getMasterMusicVolume()); - ma_sound_start(&m_musicStream); + ma_sound_start(&m_audio->musicStream); m_StreamState = eMusicStreamState_Playing; } @@ -531,8 +546,8 @@ void SoundEngine::playMusicTick() { case eMusicStreamState_Stop: if (m_musicStreamActive) { - ma_sound_stop(&m_musicStream); - ma_sound_uninit(&m_musicStream); + ma_sound_stop(&m_audio->musicStream); + ma_sound_uninit(&m_audio->musicStream); m_musicStreamActive = false; } SetIsPlayingStreamingCDMusic(false); @@ -591,7 +606,7 @@ void SoundEngine::playMusicTick() { // volume change required? if (m_musicStreamActive) ma_sound_set_volume( - &m_musicStream, + &m_audio->musicStream, m_StreamingAudioInfo.volume * fMusicVol); } else if (m_StreamingAudioInfo.bIs3D && m_validListenerCount > 1 && @@ -616,7 +631,7 @@ void SoundEngine::playMusicTick() { } } ma_sound_set_position( - &m_musicStream, + &m_audio->musicStream, m_StreamingAudioInfo.x - m_ListenerA[iClosest].vPosition.x, m_StreamingAudioInfo.y - m_ListenerA[iClosest].vPosition.y, m_StreamingAudioInfo.z - m_ListenerA[iClosest].vPosition.z); @@ -645,9 +660,9 @@ void SoundEngine::playMusicTick() { // check the status of the stream - this is for when a track completes // rather than is stopped by the user action - if (m_musicStreamActive && !ma_sound_is_playing(&m_musicStream) && - ma_sound_at_end(&m_musicStream)) { - ma_sound_uninit(&m_musicStream); + if (m_musicStreamActive && !ma_sound_is_playing(&m_audio->musicStream) && + ma_sound_at_end(&m_audio->musicStream)) { + ma_sound_uninit(&m_audio->musicStream); m_musicStreamActive = false; SetIsPlayingStreamingCDMusic(false); SetIsPlayingStreamingGameMusic(false); @@ -660,23 +675,23 @@ void SoundEngine::updateMiniAudio() { for (size_t i = 0; i < MAX_LOCAL_PLAYERS; i++) { if (m_ListenerA[i].bValid) { ma_engine_listener_set_position( - &m_engine, 0, m_ListenerA[i].vPosition.x, + &m_audio->engine, 0, m_ListenerA[i].vPosition.x, m_ListenerA[i].vPosition.y, m_ListenerA[i].vPosition.z); - ma_engine_listener_set_direction(&m_engine, 0, + ma_engine_listener_set_direction(&m_audio->engine, 0, m_ListenerA[i].vOrientFront.x, m_ListenerA[i].vOrientFront.y, m_ListenerA[i].vOrientFront.z); - ma_engine_listener_set_world_up(&m_engine, 0, 0.0f, 1.0f, 0.0f); + ma_engine_listener_set_world_up(&m_audio->engine, 0, 0.0f, 1.0f, 0.0f); break; } } } else { - ma_engine_listener_set_position(&m_engine, 0, 0.0f, 0.0f, 0.0f); - ma_engine_listener_set_direction(&m_engine, 0, 0.0f, 0.0f, 1.0f); - ma_engine_listener_set_world_up(&m_engine, 0, 0.0f, 1.0f, 0.0f); + ma_engine_listener_set_position(&m_audio->engine, 0, 0.0f, 0.0f, 0.0f); + ma_engine_listener_set_direction(&m_audio->engine, 0, 0.0f, 0.0f, 1.0f); + ma_engine_listener_set_world_up(&m_audio->engine, 0, 0.0f, 1.0f, 0.0f); } for (auto it = m_activeSounds.begin(); it != m_activeSounds.end();) { diff --git a/targets/app/common/Audio/SoundEngine.h b/targets/app/common/Audio/SoundEngine.h index d6b49b4af..d769d859e 100644 --- a/targets/app/common/Audio/SoundEngine.h +++ b/targets/app/common/Audio/SoundEngine.h @@ -4,13 +4,19 @@ class Options; class C4JThread; class Random; +#include #include #include "platform/PlatformTypes.h" #include "app/common/Audio/Consoles_SoundEngine.h" #include "app/linux/Iggy/include/rrCore.h" #include "minecraft/sounds/SoundTypes.h" -#include "miniaudio.h" + +// Forward-declare the miniaudio backing state. The full struct lives in +// SoundEngine.cpp where miniaudio.h is actually included. This keeps +// miniaudio.h out of the 9 minecraft files that include SoundEngine.h +// via the 4j include chain. +struct SoundEngineMiniAudio; constexpr float SFX_3D_MIN_DISTANCE = 1.0f; constexpr float SFX_3D_MAX_DISTANCE = 16.0f; @@ -90,15 +96,15 @@ typedef struct { char chName[64]; #endif } AUDIO_INFO; -struct MiniAudioSound { - ma_sound sound; - AUDIO_INFO info; - bool active; -}; + +// MiniAudioSound's definition lives in SoundEngine.cpp alongside the +// miniaudio backend; consumers of this header don't need to see it. + class SoundEngine : public ConsoleSoundEngine { static const int MAX_SAME_SOUNDS_PLAYING = 8; // 4J added public: SoundEngine(); + ~SoundEngine(); virtual void destroy(); #if defined(_DEBUG) void GetSoundName(char* szSoundName, int iSound); @@ -142,9 +148,11 @@ private: int GetRandomishTrack(int iStart, int iEnd); - ma_engine m_engine; - ma_engine_config m_engineConfig; - ma_sound m_musicStream; + // Miniaudio engine + music stream PIMPL'd into SoundEngineMiniAudio, + // defined in SoundEngine.cpp. Owned via unique_ptr; the destructor + // is out-of-line so the unique_ptr instantiation can see the full + // type. + std::unique_ptr m_audio; bool m_musicStreamActive; static char m_szSoundPath[];