From 8a802bcf85ab543871c8fd77ba4b9cdc801399a1 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 16 Jun 2024 09:26:04 -0400 Subject: [PATCH] Replace DirectSound with miniaudio (#21) * Add miniaudio * WIP * static const * Fix missing looping * Fix volume * Implement 3D sound, WIP * Remove unused WinMM code * Update README.md * Fixes * Fix naming * Fix naming * disable ma threading * Invert Z axis for OpenGL system * Update comment * Set rolloff * Fix minimize/maximize suspension * Rename function * SDL3: changed macro name --- .gitmodules | 3 + 3rdparty/miniaudio | 1 + CMakeLists.txt | 31 +- ISLE/isleapp.cpp | 7 +- LEGO1/LegoOmni.def | 1 - LEGO1/lego/legoomni/include/lego3dsound.h | 25 +- LEGO1/lego/legoomni/include/legocachsound.h | 35 ++- .../include/legoloadcachesoundpresenter.h | 12 +- .../lego/legoomni/include/legosoundmanager.h | 1 - LEGO1/lego/legoomni/src/audio/lego3dsound.cpp | 74 ++--- .../src/audio/lego3dwavepresenter.cpp | 8 +- .../lego/legoomni/src/audio/legocachsound.cpp | 125 +++----- .../src/audio/legoloadcachesoundpresenter.cpp | 4 +- .../legoomni/src/audio/legosoundmanager.cpp | 47 +-- LEGO1/omni/include/mxloopingmidipresenter.h | 38 --- LEGO1/omni/include/mxmidipresenter.h | 55 ---- LEGO1/omni/include/mxmisc.h | 2 - LEGO1/omni/include/mxmusicmanager.h | 61 ---- LEGO1/omni/include/mxmusicpresenter.h | 44 --- LEGO1/omni/include/mxobjectfactory.h | 4 +- LEGO1/omni/include/mxomni.h | 5 - LEGO1/omni/include/mxsoundmanager.h | 23 +- LEGO1/omni/include/mxwavepresenter.h | 44 ++- .../omni/src/audio/mxloopingmidipresenter.cpp | 51 --- LEGO1/omni/src/audio/mxmidipresenter.cpp | 131 -------- LEGO1/omni/src/audio/mxmusicmanager.cpp | 294 ------------------ LEGO1/omni/src/audio/mxmusicpresenter.cpp | 59 ---- LEGO1/omni/src/audio/mxsoundmanager.cpp | 98 +++--- LEGO1/omni/src/audio/mxwavepresenter.cpp | 217 ++++++------- LEGO1/omni/src/common/mxmisc.cpp | 8 - LEGO1/omni/src/common/mxobjectfactory.cpp | 2 - LEGO1/omni/src/common/mxpresenter.cpp | 6 +- LEGO1/omni/src/main/mxomni.cpp | 12 - README.md | 2 +- 34 files changed, 342 insertions(+), 1188 deletions(-) create mode 160000 3rdparty/miniaudio delete mode 100644 LEGO1/omni/include/mxloopingmidipresenter.h delete mode 100644 LEGO1/omni/include/mxmidipresenter.h delete mode 100644 LEGO1/omni/include/mxmusicmanager.h delete mode 100644 LEGO1/omni/include/mxmusicpresenter.h delete mode 100644 LEGO1/omni/src/audio/mxloopingmidipresenter.cpp delete mode 100644 LEGO1/omni/src/audio/mxmidipresenter.cpp delete mode 100644 LEGO1/omni/src/audio/mxmusicmanager.cpp delete mode 100644 LEGO1/omni/src/audio/mxmusicpresenter.cpp diff --git a/.gitmodules b/.gitmodules index 6eaccdb5..957a7f79 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "3rdparty/libsmacker"] path = 3rdparty/libsmacker url = https://github.com/foxtacles/libsmacker +[submodule "3rdparty/miniaudio"] + path = 3rdparty/miniaudio + url = https://github.com/mackron/miniaudio diff --git a/3rdparty/miniaudio b/3rdparty/miniaudio new file mode 160000 index 00000000..4a5b74be --- /dev/null +++ b/3rdparty/miniaudio @@ -0,0 +1 @@ +Subproject commit 4a5b74bef029b3592c54b6048650ee5f972c1a48 diff --git a/CMakeLists.txt b/CMakeLists.txt index 720e1609..b9e0cc02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,27 @@ set_property(TARGET libsmacker PROPERTY ARCHIVE_OUTPUT_NAME "libsmacker$<$:d>") +set_property(TARGET miniaudio PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/3rdparty/miniaudio/extras/miniaudio_split") +target_include_directories(miniaudio PRIVATE "${CMAKE_SOURCE_DIR}/3rdparty/miniaudio/extras/miniaudio_split") +# Disable most features since we don't need them. +target_compile_definitions(miniaudio PUBLIC + MA_ENABLE_ONLY_SPECIFIC_BACKENDS + MA_NO_DECODING + MA_NO_ENCODING + MA_NO_WAV + MA_NO_FLAC + MA_NO_MP3 + MA_NO_DEVICE_IO + MA_NO_RESOURCE_MANAGER + MA_NO_GENERATION + MA_NO_THREADING +) + add_library(tglrl STATIC LEGO1/tgl/d3drm/camera.cpp LEGO1/tgl/d3drm/device.cpp @@ -243,10 +264,6 @@ add_library(omni STATIC LEGO1/omni/src/action/mxdsstreamingaction.cpp LEGO1/omni/src/audio/mxaudiomanager.cpp LEGO1/omni/src/audio/mxaudiopresenter.cpp - LEGO1/omni/src/audio/mxloopingmidipresenter.cpp - LEGO1/omni/src/audio/mxmidipresenter.cpp - LEGO1/omni/src/audio/mxmusicmanager.cpp - LEGO1/omni/src/audio/mxmusicpresenter.cpp LEGO1/omni/src/audio/mxsoundmanager.cpp LEGO1/omni/src/audio/mxsoundpresenter.cpp LEGO1/omni/src/audio/mxwavepresenter.cpp @@ -314,7 +331,7 @@ add_library(omni STATIC register_lego1_target(omni) set_property(TARGET omni PROPERTY ARCHIVE_OUTPUT_NAME "omni$<$:d>") target_include_directories(omni PRIVATE "${CMAKE_SOURCE_DIR}/LEGO1/omni/include" "${CMAKE_SOURCE_DIR}/LEGO1" "${CMAKE_SOURCE_DIR}/util") -target_link_libraries(omni PRIVATE dsound winmm libsmacker) +target_link_libraries(omni PRIVATE winmm libsmacker miniaudio) add_library(lego1 SHARED LEGO1/define.cpp @@ -454,7 +471,7 @@ target_include_directories(lego1 PUBLIC "${CMAKE_SOURCE_DIR}/LEGO1/lego/legoomni target_include_directories(lego1 PUBLIC "${CMAKE_SOURCE_DIR}/LEGO1/lego/legoomni/include/actions") # Link libraries -target_link_libraries(lego1 PRIVATE tglrl viewmanager realtime mxdirectx roi geom anim Vec::Vec dxguid misc 3dmanager omni) +target_link_libraries(lego1 PRIVATE tglrl viewmanager realtime mxdirectx roi geom anim Vec::Vec dxguid misc 3dmanager miniaudio omni) foreach(tgt IN LISTS lego1_targets) target_link_libraries(${tgt} PRIVATE $<$:DirectX5::DirectX5> SDL3::SDL3) @@ -480,7 +497,7 @@ if (ISLE_BUILD_APP) target_link_libraries(isle PRIVATE SDL3::SDL3 iniparser-static) # Link DSOUND, WINMM, and LEGO1 - target_link_libraries(isle PRIVATE dsound winmm lego1) + target_link_libraries(isle PRIVATE winmm lego1) # Make sure filenames are ALL CAPS set_property(TARGET isle PROPERTY OUTPUT_NAME ISLE) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 8d5a703a..c9507e58 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -237,8 +237,7 @@ int SDL_AppInit(void** appstate, int argc, char** argv) { *appstate = NULL; - // Add subsystems as necessary later - if (SDL_Init(SDL_INIT_VIDEO) != 0 || SDL_Init(SDL_INIT_TIMER) != 0) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0) { SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", @@ -311,15 +310,17 @@ int SDL_AppEvent(void* appstate, const SDL_Event* event) // [library:window] // Remaining functionality to be implemented: - // Full screen - crashes when minimizing/maximizing + // Full screen - crashes when minimizing/maximizing, but this will probably be fixed once DirectDraw is replaced // WM_TIMER - use SDL_Timer functionality instead switch (event->type) { case SDL_EVENT_WINDOW_FOCUS_GAINED: g_isle->SetWindowActive(TRUE); + Lego()->Resume(); break; case SDL_EVENT_WINDOW_FOCUS_LOST: g_isle->SetWindowActive(FALSE); + Lego()->Pause(); break; case SDL_EVENT_WINDOW_CLOSE_REQUESTED: if (!g_closed) { diff --git a/LEGO1/LegoOmni.def b/LEGO1/LegoOmni.def index a3bbfe66..26c9e6b6 100644 --- a/LEGO1/LegoOmni.def +++ b/LEGO1/LegoOmni.def @@ -82,7 +82,6 @@ EXPORTS ?MSoundManager@@YAPAVMxSoundManager@@XZ ?MakeSourceName@@YAXPADPBD@Z ?MoveCursor@LegoVideoManager@@QAEXHH@Z -?MusicManager@@YAPAVMxMusicManager@@XZ ?NotificationManager@@YAPAVMxNotificationManager@@XZ ?Notify@MxCore@@UAEJAAVMxParam@@@Z ?Open@MxDSFile@@UAEJK@Z diff --git a/LEGO1/lego/legoomni/include/lego3dsound.h b/LEGO1/lego/legoomni/include/lego3dsound.h index dc7840a3..ddf34782 100644 --- a/LEGO1/lego/legoomni/include/lego3dsound.h +++ b/LEGO1/lego/legoomni/include/lego3dsound.h @@ -4,7 +4,7 @@ #include "decomp.h" #include "mxtypes.h" -#include +#include class LegoActor; class LegoROI; @@ -17,10 +17,10 @@ public: virtual ~Lego3DSound(); void Init(); - MxResult Create(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const char* p_name, MxS32 p_volume); + MxResult Create(ma_sound* p_sound, const char* p_name, MxS32 p_volume); void Destroy(); - MxU32 UpdatePosition(LPDIRECTSOUNDBUFFER p_directSoundBuffer); - void FUN_10011a60(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const char* p_name); + MxU32 UpdatePosition(ma_sound* p_sound); + void FUN_10011a60(ma_sound* p_sound, const char* p_name); void Reset(); MxS32 SetDistance(MxS32 p_min, MxS32 p_max); @@ -28,15 +28,14 @@ public: // Lego3DSound::`scalar deleting destructor' private: - LPDIRECTSOUND3DBUFFER m_ds3dBuffer; // 0x08 - LegoROI* m_roi; // 0x0c - LegoROI* m_positionROI; // 0x10 - MxBool m_enabled; // 0x14 - MxBool m_isActor; // 0x15 - LegoActor* m_actor; // 0x18 - double m_frequencyFactor; // 0x20 - DWORD m_dwFrequency; // 0x28 - MxS32 m_volume; // 0x2c + ma_sound* m_sound; + LegoROI* m_roi; // 0x0c + LegoROI* m_positionROI; // 0x10 + MxBool m_enabled; // 0x14 + MxBool m_isActor; // 0x15 + LegoActor* m_actor; // 0x18 + double m_frequencyFactor; // 0x20 + MxS32 m_volume; // 0x2c }; // GLOBAL: LEGO1 0x100db6c0 diff --git a/LEGO1/lego/legoomni/include/legocachsound.h b/LEGO1/lego/legoomni/include/legocachsound.h index de312a93..d1020679 100644 --- a/LEGO1/lego/legoomni/include/legocachsound.h +++ b/LEGO1/lego/legoomni/include/legocachsound.h @@ -5,6 +5,7 @@ #include "lego3dsound.h" #include "mxcore.h" #include "mxstring.h" +#include "mxwavepresenter.h" // VTABLE: LEGO1 0x100d4718 // SIZE 0x88 @@ -27,7 +28,7 @@ public: } virtual MxResult Create( - LPPCMWAVEFORMAT p_pwfx, + MxWavePresenter::WaveFormat& p_pwfx, MxString p_mediaSrcPath, MxS32 p_volume, MxU8* p_data, @@ -54,20 +55,24 @@ private: void CopyData(MxU8* p_data, MxU32 p_dataSize); MxString FUN_10006d80(const MxString& p_str); - LPDIRECTSOUNDBUFFER m_dsBuffer; // 0x08 - undefined m_unk0x0c[4]; // 0x0c - Lego3DSound m_sound; // 0x10 - MxU8* m_data; // 0x40 - MxU32 m_dataSize; // 0x44 - MxString m_unk0x48; // 0x48 - MxBool m_unk0x58; // 0x58 - PCMWAVEFORMAT m_wfx; // 0x59 - MxBool m_looping; // 0x69 - MxBool m_unk0x6a; // 0x6a - MxS32 m_volume; // 0x6c - MxBool m_unk0x70; // 0x70 - MxString m_unk0x74; // 0x74 - MxBool m_muted; // 0x84 + // [library:audio] WAVE_FORMAT_PCM (audio in .SI files only used this format) + static const MxU32 g_supportedFormatTag = 1; + + ma_audio_buffer m_buffer; + ma_sound m_cacheSound; + undefined m_unk0x0c[4]; // 0x0c + Lego3DSound m_sound; // 0x10 + MxU8* m_data; // 0x40 + MxU32 m_dataSize; // 0x44 + MxString m_unk0x48; // 0x48 + MxBool m_unk0x58; // 0x58 + MxWavePresenter::WaveFormat m_wfx; // 0x59 + MxBool m_looping; // 0x69 + MxBool m_unk0x6a; // 0x6a + MxS32 m_volume; // 0x6c + MxBool m_unk0x70; // 0x70 + MxString m_unk0x74; // 0x74 + MxBool m_muted; // 0x84 }; #endif // LEGOCACHSOUND_H diff --git a/LEGO1/lego/legoomni/include/legoloadcachesoundpresenter.h b/LEGO1/lego/legoomni/include/legoloadcachesoundpresenter.h index 6def0dee..8aaefcee 100644 --- a/LEGO1/lego/legoomni/include/legoloadcachesoundpresenter.h +++ b/LEGO1/lego/legoomni/include/legoloadcachesoundpresenter.h @@ -36,12 +36,12 @@ private: void Init(); void Destroy(MxBool p_fromDestructor); - LegoCacheSound* m_cacheSound; // 0x6c - MxU8* m_data; // 0x70 - MxU8* m_pData; // 0x74 - MxU32 m_dataSize; // 0x78 - MxBool m_unk0x7c; // 0x7c - PCMWAVEFORMAT m_pcmWaveFormat; // 0x7d + LegoCacheSound* m_cacheSound; // 0x6c + MxU8* m_data; // 0x70 + MxU8* m_pData; // 0x74 + MxU32 m_dataSize; // 0x78 + MxBool m_unk0x7c; // 0x7c + WaveFormat m_waveFormat; // 0x7d }; // SYNTHETIC: LEGO1 0x10018460 diff --git a/LEGO1/lego/legoomni/include/legosoundmanager.h b/LEGO1/lego/legoomni/include/legosoundmanager.h index 72638c63..bc345d7f 100644 --- a/LEGO1/lego/legoomni/include/legosoundmanager.h +++ b/LEGO1/lego/legoomni/include/legosoundmanager.h @@ -27,7 +27,6 @@ private: void Init(); void Destroy(MxBool p_fromDestructor); - LPDIRECTSOUND3DLISTENER m_listener; // 0x3c LegoCacheSoundManager* m_cacheSoundManager; // 0x40 }; diff --git a/LEGO1/lego/legoomni/src/audio/lego3dsound.cpp b/LEGO1/lego/legoomni/src/audio/lego3dsound.cpp index 508cd395..0193ec4a 100644 --- a/LEGO1/lego/legoomni/src/audio/lego3dsound.cpp +++ b/LEGO1/lego/legoomni/src/audio/lego3dsound.cpp @@ -26,7 +26,7 @@ Lego3DSound::~Lego3DSound() // FUNCTION: LEGO1 0x10011680 void Lego3DSound::Init() { - m_ds3dBuffer = NULL; + m_sound = NULL; m_roi = NULL; m_positionROI = NULL; m_actor = NULL; @@ -37,23 +37,20 @@ void Lego3DSound::Init() // FUNCTION: LEGO1 0x100116a0 // FUNCTION: BETA10 0x10039647 -MxResult Lego3DSound::Create(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const char* p_name, MxS32 p_volume) +MxResult Lego3DSound::Create(ma_sound* p_sound, const char* p_name, MxS32 p_volume) { m_volume = p_volume; if (MxOmni::IsSound3D()) { - p_directSoundBuffer->QueryInterface(IID_IDirectSound3DBuffer, (LPVOID*) &m_ds3dBuffer); - if (m_ds3dBuffer == NULL) { - return FAILURE; - } + m_sound = p_sound; - m_ds3dBuffer->SetMinDistance(15.0f, DS3D_IMMEDIATE); - m_ds3dBuffer->SetMaxDistance(100.0f, DS3D_IMMEDIATE); - m_ds3dBuffer->SetPosition(0.0f, 0.0f, -40.0f, DS3D_IMMEDIATE); - m_ds3dBuffer->SetConeOutsideVolume(-10000, DS3D_IMMEDIATE); + ma_sound_set_min_distance(m_sound, 15.0f); + ma_sound_set_max_distance(m_sound, 100.0f); + ma_sound_set_position(m_sound, 0.0f, 0.0f, 40.0f); + ma_sound_set_rolloff(m_sound, 10.0f); } - if (m_ds3dBuffer == NULL || p_name == NULL) { + if (m_sound == NULL || p_name == NULL) { return SUCCESS; } @@ -86,7 +83,7 @@ MxResult Lego3DSound::Create(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const char if (MxOmni::IsSound3D()) { const float* position = m_positionROI->GetWorldPosition(); - m_ds3dBuffer->SetPosition(position[0], position[1], position[2], DS3D_IMMEDIATE); + ma_sound_set_position(m_sound, position[0], position[1], -position[2]); } LegoEntity* entity = m_roi->GetEntity(); @@ -94,13 +91,11 @@ MxResult Lego3DSound::Create(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const char m_actor = ((LegoActor*) entity); } - p_directSoundBuffer->GetFrequency(&m_dwFrequency); - if (m_actor != NULL) { m_frequencyFactor = m_actor->GetSoundFrequencyFactor(); if (m_frequencyFactor != 0.0) { - p_directSoundBuffer->SetFrequency(m_frequencyFactor * m_dwFrequency); + ma_sound_set_pitch(p_sound, m_frequencyFactor); } } @@ -110,10 +105,7 @@ MxResult Lego3DSound::Create(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const char // FUNCTION: LEGO1 0x10011880 void Lego3DSound::Destroy() { - if (m_ds3dBuffer) { - m_ds3dBuffer->Release(); - m_ds3dBuffer = NULL; - } + m_sound = NULL; if (m_enabled && m_roi && CharacterManager()) { if (m_isActor) { @@ -129,7 +121,7 @@ void Lego3DSound::Destroy() // FUNCTION: LEGO1 0x100118e0 // FUNCTION: BETA10 0x10039a2a -MxU32 Lego3DSound::UpdatePosition(LPDIRECTSOUNDBUFFER p_directSoundBuffer) +MxU32 Lego3DSound::UpdatePosition(ma_sound* p_sound) { MxU32 updated = FALSE; @@ -146,8 +138,8 @@ MxU32 Lego3DSound::UpdatePosition(LPDIRECTSOUNDBUFFER p_directSoundBuffer) return FALSE; } - if (m_ds3dBuffer != NULL) { - m_ds3dBuffer->SetPosition(position[0], position[1], position[2], DS3D_IMMEDIATE); + if (m_sound != NULL) { + ma_sound_set_position(m_sound, position[0], position[1], -position[2]); } else { MxS32 newVolume = m_volume; @@ -165,8 +157,7 @@ MxU32 Lego3DSound::UpdatePosition(LPDIRECTSOUNDBUFFER p_directSoundBuffer) } newVolume = newVolume * SoundManager()->GetVolume() / 100; - newVolume = SoundManager()->GetAttenuation(newVolume); - p_directSoundBuffer->SetVolume(newVolume); + ma_sound_set_volume(p_sound, SoundManager()->GetAttenuation(newVolume)); } updated = TRUE; @@ -175,7 +166,7 @@ MxU32 Lego3DSound::UpdatePosition(LPDIRECTSOUNDBUFFER p_directSoundBuffer) if (m_actor != NULL) { if (abs(m_frequencyFactor - m_actor->GetSoundFrequencyFactor()) > 0.0001) { m_frequencyFactor = m_actor->GetSoundFrequencyFactor(); - p_directSoundBuffer->SetFrequency(m_frequencyFactor * m_dwFrequency); + ma_sound_set_pitch(p_sound, m_frequencyFactor); updated = TRUE; } } @@ -185,13 +176,13 @@ MxU32 Lego3DSound::UpdatePosition(LPDIRECTSOUNDBUFFER p_directSoundBuffer) // FUNCTION: LEGO1 0x10011a60 // FUNCTION: BETA10 0x10039d04 -void Lego3DSound::FUN_10011a60(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const char* p_name) +void Lego3DSound::FUN_10011a60(ma_sound* p_sound, const char* p_name) { - assert(p_directSoundBuffer); + assert(p_sound); if (p_name == NULL) { - if (m_ds3dBuffer != NULL) { - m_ds3dBuffer->SetMode(DS3DMODE_DISABLE, DS3D_IMMEDIATE); + if (m_sound != NULL) { + ma_sound_set_spatialization_enabled(m_sound, MA_FALSE); } } else { @@ -222,16 +213,10 @@ void Lego3DSound::FUN_10011a60(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const ch m_positionROI = m_roi; } - if (m_ds3dBuffer != NULL) { - DWORD dwMode; - m_ds3dBuffer->GetMode(&dwMode); - - if (dwMode & DS3DMODE_DISABLE) { - m_ds3dBuffer->SetMode(DS3DMODE_NORMAL, DS3D_IMMEDIATE); - } - + if (m_sound != NULL) { + ma_sound_set_spatialization_enabled(m_sound, MA_TRUE); const float* position = m_positionROI->GetWorldPosition(); - m_ds3dBuffer->SetPosition(position[0], position[1], position[2], DS3D_IMMEDIATE); + ma_sound_set_position(m_sound, position[0], position[1], -position[2]); } else { const float* position = m_positionROI->GetWorldPosition(); @@ -256,8 +241,7 @@ void Lego3DSound::FUN_10011a60(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const ch } newVolume = newVolume * SoundManager()->GetVolume() / 100; - newVolume = SoundManager()->GetAttenuation(newVolume); - p_directSoundBuffer->SetVolume(newVolume); + ma_sound_set_volume(p_sound, SoundManager()->GetAttenuation(newVolume)); } } @@ -266,13 +250,11 @@ void Lego3DSound::FUN_10011a60(LPDIRECTSOUNDBUFFER p_directSoundBuffer, const ch m_actor = ((LegoActor*) entity); } - p_directSoundBuffer->GetFrequency(&m_dwFrequency); - if (m_actor != NULL) { m_frequencyFactor = m_actor->GetSoundFrequencyFactor(); if (m_frequencyFactor != 0.0) { - p_directSoundBuffer->SetFrequency(m_frequencyFactor * m_dwFrequency); + ma_sound_set_pitch(p_sound, m_frequencyFactor); } } } @@ -299,12 +281,12 @@ void Lego3DSound::Reset() MxS32 Lego3DSound::SetDistance(MxS32 p_min, MxS32 p_max) { if (MxOmni::IsSound3D()) { - if (m_ds3dBuffer == NULL) { + if (m_sound == NULL) { return -1; } - m_ds3dBuffer->SetMinDistance(p_min, DS3D_IMMEDIATE); - m_ds3dBuffer->SetMaxDistance(p_max, DS3D_IMMEDIATE); + ma_sound_set_min_distance(m_sound, p_min); + ma_sound_set_max_distance(m_sound, p_max); return 0; } diff --git a/LEGO1/lego/legoomni/src/audio/lego3dwavepresenter.cpp b/LEGO1/lego/legoomni/src/audio/lego3dwavepresenter.cpp index 8083822e..55412b22 100644 --- a/LEGO1/lego/legoomni/src/audio/lego3dwavepresenter.cpp +++ b/LEGO1/lego/legoomni/src/audio/lego3dwavepresenter.cpp @@ -40,7 +40,7 @@ void Lego3DWavePresenter::StartingTickle() MxWavePresenter::StartingTickle(); - if (m_dsBuffer != NULL) { + if (ma_sound_get_engine(&(MxWavePresenter::m_sound))) { MxU16 extraLength; char* buff; m_action->GetExtra(extraLength, buff); @@ -49,9 +49,7 @@ void Lego3DWavePresenter::StartingTickle() m_compositePresenter->GetAction()->GetExtra(extraLength, buff); } - if (m_sound.Create(m_dsBuffer, buff, m_volume) != SUCCESS) { - m_dsBuffer->Release(); - m_dsBuffer = NULL; + if (m_sound.Create(&(MxWavePresenter::m_sound), buff, m_volume) != SUCCESS) { EndAction(); } } @@ -62,5 +60,5 @@ void Lego3DWavePresenter::StartingTickle() void Lego3DWavePresenter::StreamingTickle() { MxWavePresenter::StreamingTickle(); - m_sound.UpdatePosition(m_dsBuffer); + m_sound.UpdatePosition(&(MxWavePresenter::m_sound)); } diff --git a/LEGO1/lego/legoomni/src/audio/legocachsound.cpp b/LEGO1/lego/legoomni/src/audio/legocachsound.cpp index 7665e5d7..2080d27b 100644 --- a/LEGO1/lego/legoomni/src/audio/legocachsound.cpp +++ b/LEGO1/lego/legoomni/src/audio/legocachsound.cpp @@ -4,6 +4,8 @@ #include "misc.h" #include "mxomni.h" +#include + DECOMP_SIZE_ASSERT(LegoCacheSound, 0x88) // FUNCTION: LEGO1 0x100064d0 @@ -21,7 +23,8 @@ LegoCacheSound::~LegoCacheSound() // FUNCTION: LEGO1 0x100066d0 void LegoCacheSound::Init() { - m_dsBuffer = NULL; + SDL_zero(m_buffer); + SDL_zero(m_cacheSound); m_data = NULL; m_unk0x58 = FALSE; memset(&m_wfx, 0, sizeof(m_wfx)); @@ -35,59 +38,53 @@ void LegoCacheSound::Init() // FUNCTION: LEGO1 0x10006710 // FUNCTION: BETA10 0x10066505 MxResult LegoCacheSound::Create( - LPPCMWAVEFORMAT p_pwfx, + MxWavePresenter::WaveFormat& p_pwfx, MxString p_mediaSrcPath, MxS32 p_volume, MxU8* p_data, MxU32 p_dataSize ) { - WAVEFORMATEX wfx; - wfx.wFormatTag = p_pwfx->wf.wFormatTag; - wfx.nChannels = p_pwfx->wf.nChannels; - wfx.nSamplesPerSec = p_pwfx->wf.nSamplesPerSec; - wfx.nAvgBytesPerSec = p_pwfx->wf.nAvgBytesPerSec; - wfx.nBlockAlign = p_pwfx->wf.nBlockAlign; - wfx.wBitsPerSample = p_pwfx->wBitsPerSample; - wfx.cbSize = 0; + // [library:audio] These should never be null + assert(p_data != NULL && p_dataSize != 0); - DSBUFFERDESC desc; - memset(&desc, 0, sizeof(desc)); - desc.dwSize = sizeof(desc); + assert(p_pwfx.m_formatTag == g_supportedFormatTag); + assert(p_pwfx.m_bitsPerSample == 8 || p_pwfx.m_bitsPerSample == 16); - if (MxOmni::IsSound3D()) { - desc.dwFlags = - DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE | DSBCAPS_CTRL3D | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME; - } - else { - desc.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME; + CopyData(p_data, p_dataSize); + + ma_format format = p_pwfx.m_bitsPerSample == 16 ? ma_format_s16 : ma_format_u8; + ma_uint32 bytesPerFrame = ma_get_bytes_per_frame(format, p_pwfx.m_channels); + ma_uint32 bufferSizeInFrames = p_dataSize / bytesPerFrame; + ma_audio_buffer_config config = + ma_audio_buffer_config_init(format, p_pwfx.m_channels, bufferSizeInFrames, m_data, NULL); + config.sampleRate = p_pwfx.m_samplesPerSec; + + if (ma_audio_buffer_init(&config, &m_buffer) != MA_SUCCESS) { + return FAILURE; } - desc.dwBufferBytes = p_dataSize; - desc.lpwfxFormat = &wfx; - - if (SoundManager()->GetDirectSound()->CreateSoundBuffer(&desc, &m_dsBuffer, NULL) != DS_OK) { + if (ma_sound_init_from_data_source( + SoundManager()->GetEngine(), + &m_buffer, + MxOmni::IsSound3D() ? 0 : MA_SOUND_FLAG_NO_SPATIALIZATION, + NULL, + &m_cacheSound + ) != MA_SUCCESS) { return FAILURE; } m_volume = p_volume; MxS32 volume = m_volume * SoundManager()->GetVolume() / 100; - MxS32 attenuation = SoundManager()->GetAttenuation(volume); - m_dsBuffer->SetVolume(attenuation); + ma_sound_set_volume(&m_cacheSound, SoundManager()->GetAttenuation(volume)); - if (m_sound.Create(m_dsBuffer, NULL, m_volume) != SUCCESS) { - m_dsBuffer->Release(); - m_dsBuffer = NULL; + if (m_sound.Create(&m_cacheSound, NULL, m_volume) != SUCCESS) { return FAILURE; } - if (p_data != NULL && p_dataSize != 0) { - CopyData(p_data, p_dataSize); - } - m_unk0x48 = FUN_10006d80(p_mediaSrcPath); - m_wfx = *p_pwfx; + m_wfx = p_pwfx; return SUCCESS; } @@ -104,11 +101,8 @@ void LegoCacheSound::CopyData(MxU8* p_data, MxU32 p_dataSize) // FUNCTION: LEGO1 0x10006920 void LegoCacheSound::Destroy() { - if (m_dsBuffer) { - m_dsBuffer->Stop(); - m_dsBuffer->Release(); - m_dsBuffer = NULL; - } + ma_sound_uninit(&m_cacheSound); + ma_audio_buffer_uninit(&m_buffer); delete[] m_data; Init(); @@ -120,7 +114,7 @@ LegoCacheSound* LegoCacheSound::Clone() { LegoCacheSound* pnew = new LegoCacheSound(); - if (pnew->Create(&m_wfx, m_unk0x48, m_volume, m_data, m_dataSize) == SUCCESS) { + if (pnew->Create(m_wfx, m_unk0x48, m_volume, m_data, m_dataSize) == SUCCESS) { return pnew; } @@ -137,35 +131,20 @@ MxResult LegoCacheSound::Play(const char* p_name, MxBool p_looping) } m_unk0x6a = FALSE; - m_sound.FUN_10011a60(m_dsBuffer, p_name); + m_sound.FUN_10011a60(&m_cacheSound, p_name); if (p_name != NULL) { m_unk0x74 = p_name; } - DWORD dwStatus; - m_dsBuffer->GetStatus(&dwStatus); - - if (dwStatus == DSBSTATUS_BUFFERLOST) { - m_dsBuffer->Restore(); - m_dsBuffer->GetStatus(&dwStatus); + if (ma_sound_seek_to_pcm_frame(&m_cacheSound, 0) != MA_SUCCESS) { + return FAILURE; } - if (dwStatus != DSBSTATUS_BUFFERLOST) { - LPVOID pvAudioPtr1, pvAudioPtr2; - DWORD dwAudioBytes1, dwAudioBytes2; + ma_sound_set_looping(&m_cacheSound, p_looping); - if (m_dsBuffer->Lock(0, m_dataSize, &pvAudioPtr1, &dwAudioBytes1, &pvAudioPtr2, &dwAudioBytes2, 0) == DS_OK) { - memcpy(pvAudioPtr1, m_data, dwAudioBytes1); - - if (dwAudioBytes2 != 0) { - memcpy(pvAudioPtr2, m_data + dwAudioBytes1, dwAudioBytes2); - } - - m_dsBuffer->Unlock(pvAudioPtr1, dwAudioBytes1, pvAudioPtr2, dwAudioBytes2); - m_dsBuffer->SetCurrentPosition(0); - m_dsBuffer->Play(0, 0, p_looping); - } + if (ma_sound_start(&m_cacheSound) != MA_SUCCESS) { + return FAILURE; } if (p_looping == FALSE) { @@ -183,12 +162,7 @@ MxResult LegoCacheSound::Play(const char* p_name, MxBool p_looping) // FUNCTION: LEGO1 0x10006b80 void LegoCacheSound::FUN_10006b80() { - DWORD dwStatus; - m_dsBuffer->GetStatus(&dwStatus); - - if (dwStatus) { - m_dsBuffer->Stop(); - } + ma_sound_stop(&m_cacheSound); m_unk0x58 = FALSE; m_unk0x6a = FALSE; @@ -203,19 +177,16 @@ void LegoCacheSound::FUN_10006b80() void LegoCacheSound::FUN_10006be0() { if (!m_looping) { - DWORD dwStatus; - m_dsBuffer->GetStatus(&dwStatus); - if (m_unk0x70) { - if (dwStatus == 0) { + if (!ma_sound_is_playing(&m_cacheSound)) { return; } m_unk0x70 = FALSE; } - if (dwStatus == 0) { - m_dsBuffer->Stop(); + if (!ma_sound_is_playing(&m_cacheSound)) { + ma_sound_stop(&m_cacheSound); m_sound.Reset(); if (m_unk0x74.GetLength() != 0) { m_unk0x74 = ""; @@ -227,16 +198,16 @@ void LegoCacheSound::FUN_10006be0() } if (m_unk0x74.GetLength() != 0 && !m_muted) { - if (!m_sound.UpdatePosition(m_dsBuffer)) { + if (!m_sound.UpdatePosition(&m_cacheSound)) { if (m_unk0x6a) { return; } - m_dsBuffer->Stop(); + ma_sound_stop(&m_cacheSound); m_unk0x6a = TRUE; } else if (m_unk0x6a) { - m_dsBuffer->Play(0, 0, m_looping); + ma_sound_start(&m_cacheSound); m_unk0x6a = FALSE; } } @@ -261,10 +232,10 @@ void LegoCacheSound::Mute(MxBool p_muted) m_muted = p_muted; if (m_muted) { - m_dsBuffer->Stop(); + ma_sound_stop(&m_cacheSound); } else { - m_dsBuffer->Play(0, 0, m_looping); + ma_sound_start(&m_cacheSound); } } } diff --git a/LEGO1/lego/legoomni/src/audio/legoloadcachesoundpresenter.cpp b/LEGO1/lego/legoomni/src/audio/legoloadcachesoundpresenter.cpp index a0a66e89..8adb79df 100644 --- a/LEGO1/lego/legoomni/src/audio/legoloadcachesoundpresenter.cpp +++ b/LEGO1/lego/legoomni/src/audio/legoloadcachesoundpresenter.cpp @@ -53,7 +53,7 @@ void LegoLoadCacheSoundPresenter::ReadyTickle() m_pData = data; m_cacheSound = new LegoCacheSound(); - m_pcmWaveFormat = header->m_pcmWaveFormat; + m_waveFormat = *header; m_subscriber->FreeDataChunk(chunk); ProgressTickleState(e_streaming); @@ -69,7 +69,7 @@ void LegoLoadCacheSoundPresenter::StreamingTickle() if (chunk) { if (chunk->GetChunkFlags() & DS_CHUNK_END_OF_STREAM) { m_cacheSound->Create( - &m_pcmWaveFormat, + m_waveFormat, ((MxDSSound*) m_action)->GetMediaSrcPath(), ((MxDSSound*) m_action)->GetVolume(), m_data + 2, diff --git a/LEGO1/lego/legoomni/src/audio/legosoundmanager.cpp b/LEGO1/lego/legoomni/src/audio/legosoundmanager.cpp index 2832d825..6da78ab1 100644 --- a/LEGO1/lego/legoomni/src/audio/legosoundmanager.cpp +++ b/LEGO1/lego/legoomni/src/audio/legosoundmanager.cpp @@ -22,7 +22,6 @@ LegoSoundManager::~LegoSoundManager() void LegoSoundManager::Init() { m_cacheSoundManager = NULL; - m_listener = NULL; } // FUNCTION: LEGO1 0x100299b0 @@ -45,27 +44,6 @@ MxResult LegoSoundManager::Create(MxU32 p_frequencyMS, MxBool p_createThread) if (MxSoundManager::Create(10, FALSE) == SUCCESS) { m_criticalSection.Enter(); locked = TRUE; - - if (MxOmni::IsSound3D()) { - if (m_dsBuffer->QueryInterface(IID_IDirectSound3DListener, (LPVOID*) &m_listener) != DS_OK) { - goto done; - } - - MxOmni* omni = MxOmni::GetInstance(); - LPDIRECTSOUND sound; - - if (omni && omni->GetSoundManager() && (sound = omni->GetSoundManager()->GetDirectSound())) { - DSCAPS caps; - memset(&caps, 0, sizeof(DSCAPS)); - caps.dwSize = sizeof(DSCAPS); - - if (sound->GetCaps(&caps) == S_OK && caps.dwMaxHw3DAllBuffers == 0) { - m_listener->SetDistanceFactor(0.026315790f, 0); - m_listener->SetRolloffFactor(10, 0); - } - } - } - m_cacheSoundManager = new LegoCacheSoundManager; result = SUCCESS; } @@ -106,29 +84,22 @@ void LegoSoundManager::UpdateListener( const float* p_velocity ) { - if (m_listener != NULL) { + if (MxOmni::IsSound3D()) { + // [library:audio] + // miniaudio expects the right-handed OpenGL coordinate system, while LEGO Island + // uses DirectX' left-handed system. The Z-axis needs to be inverted. + if (p_position != NULL) { - m_listener->SetPosition(p_position[0], p_position[1], p_position[2], DS3D_DEFERRED); + ma_engine_listener_set_position(&m_engine, 0, p_position[0], p_position[1], -p_position[2]); } if (p_direction != NULL && p_up != NULL) { - m_listener->SetOrientation( - p_direction[0], - p_direction[1], - p_direction[2], - p_up[0], - p_up[1], - p_up[2], - DS3D_DEFERRED - ); + ma_engine_listener_set_direction(&m_engine, 0, p_direction[0], p_direction[1], -p_direction[2]); + ma_engine_listener_set_world_up(&m_engine, 0, p_up[0], p_up[1], -p_up[2]); } if (p_velocity != NULL) { - m_listener->SetVelocity(p_velocity[0], p_velocity[1], p_velocity[2], DS3D_DEFERRED); - } - - if (p_position != NULL || (p_direction != NULL && p_up != NULL) || p_velocity != NULL) { - m_listener->CommitDeferredSettings(); + ma_engine_listener_set_velocity(&m_engine, 0, p_velocity[0], p_velocity[1], -p_velocity[2]); } } } diff --git a/LEGO1/omni/include/mxloopingmidipresenter.h b/LEGO1/omni/include/mxloopingmidipresenter.h deleted file mode 100644 index 0bdd191e..00000000 --- a/LEGO1/omni/include/mxloopingmidipresenter.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef MXLOOPINGMIDIPRESENTER_H -#define MXLOOPINGMIDIPRESENTER_H - -#include "mxmidipresenter.h" - -// VTABLE: LEGO1 0x100dc240 -// SIZE 0x58 -class MxLoopingMIDIPresenter : public MxMIDIPresenter { -public: - // FUNCTION: BETA10 0x1012f0b0 - static const char* HandlerClassName() - { - // STRING: LEGO1 0x10101de0 - return "MxLoopingMIDIPresenter"; - } - - // FUNCTION: LEGO1 0x100b1830 - // FUNCTION: BETA10 0x10143910 - inline const char* ClassName() const override // vtable+0x0c - { - return HandlerClassName(); - } - - // FUNCTION: LEGO1 0x100b1840 - inline MxBool IsA(const char* p_name) const override // vtable+0x10 - { - return !strcmp(p_name, MxLoopingMIDIPresenter::ClassName()) || MxMIDIPresenter::IsA(p_name); - } - - void StreamingTickle() override; // vtable+0x20 - void DoneTickle() override; // vtable+0x2c - MxResult PutData() override; // vtable+0x4c -}; - -// SYNTHETIC: LEGO1 0x100b19c0 -// MxLoopingMIDIPresenter::`scalar deleting destructor' - -#endif // MXLOOPINGMIDIPRESENTER_H diff --git a/LEGO1/omni/include/mxmidipresenter.h b/LEGO1/omni/include/mxmidipresenter.h deleted file mode 100644 index 35eefd4d..00000000 --- a/LEGO1/omni/include/mxmidipresenter.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef MXMIDIPRESENTER_H -#define MXMIDIPRESENTER_H - -#include "mxmusicpresenter.h" - -class MxStreamChunk; - -// VTABLE: LEGO1 0x100dca20 -// SIZE 0x58 -class MxMIDIPresenter : public MxMusicPresenter { -public: - MxMIDIPresenter(); - ~MxMIDIPresenter() override; - - // FUNCTION: BETA10 0x1012f090 - static const char* HandlerClassName() - { - // STRING: LEGO1 0x10101df8 - return "MxMIDIPresenter"; - } - - // FUNCTION: LEGO1 0x100c2650 - // FUNCTION: BETA10 0x10143a90 - inline const char* ClassName() const override // vtable+0x0c - { - return HandlerClassName(); - } - - // FUNCTION: LEGO1 0x100c2660 - inline MxBool IsA(const char* p_name) const override // vtable+0x10 - { - return !strcmp(p_name, MxMIDIPresenter::ClassName()) || MxMusicPresenter::IsA(p_name); - } - - void ReadyTickle() override; // vtable+0x18 - void StartingTickle() override; // vtable+0x1c - void StreamingTickle() override; // vtable+0x20 - void DoneTickle() override; // vtable+0x2c - void Destroy() override; // vtable+0x38 - void EndAction() override; // vtable+0x40 - MxResult PutData() override; // vtable+0x4c - void SetVolume(MxS32 p_volume) override; // vtable+0x60 - - // SYNTHETIC: LEGO1 0x100c27a0 - // MxMIDIPresenter::`scalar deleting destructor' - -private: - void Init(); - void Destroy(MxBool p_fromDestructor); - -protected: - MxStreamChunk* m_chunk; // 0x54 -}; - -#endif // MXMIDIPRESENTER_H diff --git a/LEGO1/omni/include/mxmisc.h b/LEGO1/omni/include/mxmisc.h index 0f149a02..09928ae2 100644 --- a/LEGO1/omni/include/mxmisc.h +++ b/LEGO1/omni/include/mxmisc.h @@ -6,7 +6,6 @@ class MxAtomSet; class MxDSAction; class MxEventManager; -class MxMusicManager; class MxNotificationManager; class MxObjectFactory; class MxSoundManager; @@ -21,7 +20,6 @@ MxTimer* Timer(); MxStreamer* Streamer(); MxSoundManager* MSoundManager(); MxVariableTable* VariableTable(); -MxMusicManager* MusicManager(); MxEventManager* EventManager(); MxResult Start(MxDSAction*); MxNotificationManager* NotificationManager(); diff --git a/LEGO1/omni/include/mxmusicmanager.h b/LEGO1/omni/include/mxmusicmanager.h deleted file mode 100644 index 1107e7e3..00000000 --- a/LEGO1/omni/include/mxmusicmanager.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef MXMUSICMANAGER_H -#define MXMUSICMANAGER_H - -#include "decomp.h" -#include "mxaudiomanager.h" - -#include - -// VTABLE: LEGO1 0x100dc930 -// SIZE 0x58 -class MxMusicManager : public MxAudioManager { -public: - MxMusicManager(); - ~MxMusicManager() override; - - void Destroy() override; // vtable+18 - void SetVolume(MxS32 p_volume) override; // vtable+2c - virtual MxResult Create(MxU32 p_frequencyMS, MxBool p_createThread); // vtable+30 - - inline MxBool GetMIDIInitialized() { return m_midiInitialized; } - inline void GetMIDIVolume(DWORD& p_volume) - { - if (midiOutGetVolume((HMIDIOUT) m_midiStreamH, &p_volume)) { - p_volume = CalculateVolume(100); - } - } - - MxResult ResetStream(); - void ResetBuffer(); - MxResult InitializeMIDI(MxU8* p_data, MxS32 p_loopCount); - void DeinitializeMIDI(); - void SetMultiplier(MxS32 p_multiplier); - -private: - void Destroy(MxBool p_fromDestructor); - - MxS32 CalculateVolume(MxS32 p_volume); - void SetMIDIVolume(); - - static void CALLBACK MidiCallbackProc(HDRVR p_hdrvr, UINT p_uMsg, DWORD p_dwUser, DWORD p_dw1, DWORD p_dw2); - - HMIDISTRM m_midiStreamH; // 0x30 - MxBool m_midiInitialized; // 0x34 - MxU32 m_bufferSize; // 0x38 - MxU32 m_bufferCurrentSize; // 0x3c - MxU8* m_bufferOffset; // 0x40 - MxU8* m_bufferCurrentOffset; // 0x44 - MxU32 m_loopCount; // 0x48 - MIDIHDR* m_midiHdrP; // 0x4c - MxS32 m_multiplier; // 0x50 - DWORD m_midiVolume; // 0x54 - - // SYNTHETIC: LEGO1 0x100c0610 - // MxMusicManager::`scalar deleting destructor' - -protected: - void Init(); - void InitData(); -}; - -#endif // MXMUSICMANAGER_H diff --git a/LEGO1/omni/include/mxmusicpresenter.h b/LEGO1/omni/include/mxmusicpresenter.h deleted file mode 100644 index ec9a37ce..00000000 --- a/LEGO1/omni/include/mxmusicpresenter.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef MXMUSICPRESENTER_H -#define MXMUSICPRESENTER_H - -#include "mxaudiopresenter.h" - -// VTABLE: LEGO1 0x100dc9b8 -// SIZE 0x54 -class MxMusicPresenter : public MxAudioPresenter { -public: - MxMusicPresenter(); - ~MxMusicPresenter() override; - - // FUNCTION: BETA10 0x10143a70 - static const char* HandlerClassName() - { - // STRING: LEGO1 0x10101e48 - return "MxMusicPresenter"; - } - - // FUNCTION: LEGO1 0x100c23a0 - // FUNCTION: BETA10 0x10143a50 - inline const char* ClassName() const override // vtable+0x0c - { - return HandlerClassName(); - } - - // FUNCTION: LEGO1 0x100c23b0 - inline MxBool IsA(const char* p_name) const override // vtable+0x10 - { - return !strcmp(p_name, MxMusicPresenter::ClassName()) || MxAudioPresenter::IsA(p_name); - } - - MxResult AddToManager() override; // vtable+0x34 - void Destroy() override; // vtable+0x38 - - // SYNTHETIC: LEGO1 0x100c24c0 - // MxMusicPresenter::`scalar deleting destructor' - -private: - void Init(); - void Destroy(MxBool p_fromDestructor); -}; - -#endif // MXMUSICPRESENTER_H diff --git a/LEGO1/omni/include/mxobjectfactory.h b/LEGO1/omni/include/mxobjectfactory.h index b52e5e32..afbec728 100644 --- a/LEGO1/omni/include/mxobjectfactory.h +++ b/LEGO1/omni/include/mxobjectfactory.h @@ -12,11 +12,9 @@ X(MxSmkPresenter) \ X(MxStillPresenter) \ X(MxWavePresenter) \ - X(MxMIDIPresenter) \ X(MxEventPresenter) \ X(MxLoopingFlcPresenter) \ - X(MxLoopingSmkPresenter) \ - X(MxLoopingMIDIPresenter) + X(MxLoopingSmkPresenter) // VTABLE: LEGO1 0x100dc220 class MxObjectFactory : public MxCore { diff --git a/LEGO1/omni/include/mxomni.h b/LEGO1/omni/include/mxomni.h index c3839c1e..4ac59916 100644 --- a/LEGO1/omni/include/mxomni.h +++ b/LEGO1/omni/include/mxomni.h @@ -11,7 +11,6 @@ class MxAtomSet; class MxDSAction; class MxEntity; class MxEventManager; -class MxMusicManager; class MxNotificationManager; class MxNotificationParam; class MxObjectFactory; @@ -86,9 +85,6 @@ public: // FUNCTION: BETA10 0x101251c0 MxVariableTable* GetVariableTable() const { return this->m_variableTable; } - // FUNCTION: BETA10 0x101251e0 - MxMusicManager* GetMusicManager() const { return this->m_musicManager; } - // FUNCTION: BETA10 0x10125200 MxEventManager* GetEventManager() const { return this->m_eventManager; } @@ -111,7 +107,6 @@ protected: MxNotificationManager* m_notificationManager; // 0x28 MxVideoManager* m_videoManager; // 0x2c MxSoundManager* m_soundManager; // 0x30 - MxMusicManager* m_musicManager; // 0x34 MxEventManager* m_eventManager; // 0x38 MxTimer* m_timer; // 0x3c MxStreamer* m_streamer; // 0x40 diff --git a/LEGO1/omni/include/mxsoundmanager.h b/LEGO1/omni/include/mxsoundmanager.h index 8d1d573d..4c36969b 100644 --- a/LEGO1/omni/include/mxsoundmanager.h +++ b/LEGO1/omni/include/mxsoundmanager.h @@ -5,7 +5,8 @@ #include "mxatom.h" #include "mxaudiomanager.h" -#include +#include +#include // VTABLE: LEGO1 0x100dc128 // SIZE 0x3c @@ -20,17 +21,29 @@ public: virtual void Pause(); // vtable+0x34 virtual void Resume(); // vtable+0x38 - inline LPDIRECTSOUND GetDirectSound() { return m_directSound; } + inline ma_engine* GetEngine() { return &m_engine; } - MxS32 GetAttenuation(MxU32 p_volume); + float GetAttenuation(MxU32 p_volume); protected: void Init(); void Destroy(MxBool p_fromDestructor); MxPresenter* FUN_100aebd0(const MxAtomId& p_atomId, MxU32 p_objectId); - LPDIRECTSOUND m_directSound; // 0x30 - LPDIRECTSOUNDBUFFER m_dsBuffer; // 0x34 + // [library:audio] + // Upscaling everything to 44.1KHz, since we have various sample rates throughout the game. + // Not sure how DirectSound handles this when different buffers have different rates. + static const MxU32 g_sampleRate = 44100; + + static void AudioStreamCallback( + void* p_userdata, + SDL_AudioStream* p_stream, + int p_additionalAmount, + int p_totalAmount + ); + + ma_engine m_engine; + SDL_AudioStream* m_stream; undefined m_unk0x38[4]; }; diff --git a/LEGO1/omni/include/mxwavepresenter.h b/LEGO1/omni/include/mxwavepresenter.h index 12ed817e..9caa3a6a 100644 --- a/LEGO1/omni/include/mxwavepresenter.h +++ b/LEGO1/omni/include/mxwavepresenter.h @@ -4,7 +4,7 @@ #include "decomp.h" #include "mxsoundpresenter.h" -#include +#include // VTABLE: LEGO1 0x100d49a8 // SIZE 0x6c @@ -56,12 +56,19 @@ public: // FUNCTION: LEGO1 0x1000d6b0 virtual MxBool IsPaused() { return m_paused; } // vtable+0x6c +#pragma pack(push, 1) // SIZE 0x18 struct WaveFormat { - PCMWAVEFORMAT m_pcmWaveFormat; // 0x00 - MxU32 m_dataSize; // 0x10 - MxU32 m_flags; // 0x14 + MxU16 m_formatTag; // 0x00 + MxU16 m_channels; // 0x02 + MxU32 m_samplesPerSec; // 0x04 + MxU32 m_avgBytesPerSec; // 0x08 + MxU16 m_blockAlign; // 0x0c + MxU16 m_bitsPerSample; // 0x0e + MxU32 m_dataSize; // 0x10 + MxU32 m_flags; // 0x14 }; +#pragma pack(pop) // SYNTHETIC: LEGO1 0x1000d810 // MxWavePresenter::`scalar deleting destructor' @@ -69,20 +76,25 @@ public: protected: void Init(); void Destroy(MxBool p_fromDestructor); + MxBool WriteToSoundBuffer(void* p_audioPtr, MxU32 p_length); - MxS8 GetPlayedChunks(); - MxBool FUN_100b1ba0(); - void WriteToSoundBuffer(void* p_audioPtr, MxU32 p_length); + // [library:audio] One chunk has up to 1 second worth of frames + static const MxU32 g_millisecondsPerChunk = 1000; - WaveFormat* m_waveFormat; // 0x54 - LPDIRECTSOUNDBUFFER m_dsBuffer; // 0x58 - MxU32 m_chunkLength; // 0x5c - MxU32 m_lockSize; // 0x60 - MxU8 m_writtenChunks; // 0x64 - MxBool m_started; // 0x65 - MxBool m_is3d; // 0x66 - MxS8 m_silenceData; // 0x67 - MxBool m_paused; // 0x68 + // [library:audio] Store up to 2 chunks worth of frames (same as in original game) + static const MxU32 g_rbSizeInMilliseconds = g_millisecondsPerChunk * 2; + + // [library:audio] WAVE_FORMAT_PCM (audio in .SI files only used this format) + static const MxU32 g_supportedFormatTag = 1; + + WaveFormat* m_waveFormat; // 0x54 + ma_pcm_rb m_rb; + ma_sound m_sound; + MxU32 m_chunkLength; // 0x5c + MxBool m_started; // 0x65 + MxBool m_is3d; // 0x66 + MxS8 m_silenceData; // 0x67 + MxBool m_paused; // 0x68 }; #endif // MXWAVEPRESENTER_H diff --git a/LEGO1/omni/src/audio/mxloopingmidipresenter.cpp b/LEGO1/omni/src/audio/mxloopingmidipresenter.cpp deleted file mode 100644 index 51c305fe..00000000 --- a/LEGO1/omni/src/audio/mxloopingmidipresenter.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "mxloopingmidipresenter.h" - -#include "decomp.h" -#include "mxdssound.h" -#include "mxmisc.h" -#include "mxmusicmanager.h" - -DECOMP_SIZE_ASSERT(MxLoopingMIDIPresenter, 0x58); - -// FUNCTION: LEGO1 0x100c2a80 -void MxLoopingMIDIPresenter::StreamingTickle() -{ - if (m_action->GetLoopCount()) { - MxMIDIPresenter::StreamingTickle(); - return; - } - - if (!m_chunk) { - m_chunk = NextChunk(); - return; - } - - if (m_chunk->GetTime() + m_action->GetDuration() <= m_action->GetElapsedTime()) { - ProgressTickleState(e_done); - } -} - -// FUNCTION: LEGO1 0x100c2ae0 -void MxLoopingMIDIPresenter::DoneTickle() -{ - if (m_action->GetLoopCount()) { - MxMIDIPresenter::DoneTickle(); - } - else { - EndAction(); - } -} - -// FUNCTION: LEGO1 0x100c2b00 -MxResult MxLoopingMIDIPresenter::PutData() -{ - m_criticalSection.Enter(); - - if (m_currentTickleState == e_streaming && m_chunk && !MusicManager()->GetMIDIInitialized()) { - SetVolume(((MxDSSound*) m_action)->GetVolume()); - MusicManager()->InitializeMIDI(m_chunk->GetData(), !m_action->GetLoopCount() ? -1 : m_action->GetLoopCount()); - } - - m_criticalSection.Leave(); - return SUCCESS; -} diff --git a/LEGO1/omni/src/audio/mxmidipresenter.cpp b/LEGO1/omni/src/audio/mxmidipresenter.cpp deleted file mode 100644 index d3c990c5..00000000 --- a/LEGO1/omni/src/audio/mxmidipresenter.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "mxmidipresenter.h" - -#include "decomp.h" -#include "mxautolock.h" -#include "mxdssound.h" -#include "mxdssubscriber.h" -#include "mxmisc.h" -#include "mxmusicmanager.h" - -DECOMP_SIZE_ASSERT(MxMIDIPresenter, 0x58); - -// FUNCTION: LEGO1 0x100c25e0 -MxMIDIPresenter::MxMIDIPresenter() -{ - Init(); -} - -// FUNCTION: LEGO1 0x100c27c0 -MxMIDIPresenter::~MxMIDIPresenter() -{ - Destroy(TRUE); -} - -// FUNCTION: LEGO1 0x100c2820 -void MxMIDIPresenter::Init() -{ - m_chunk = NULL; -} - -// FUNCTION: LEGO1 0x100c2830 -void MxMIDIPresenter::Destroy(MxBool p_fromDestructor) -{ - if (MusicManager()) { - MusicManager()->DeinitializeMIDI(); - } - - m_criticalSection.Enter(); - - if (m_subscriber && m_chunk) { - m_subscriber->FreeDataChunk(m_chunk); - } - Init(); - - m_criticalSection.Leave(); - - if (!p_fromDestructor) { - MxMusicPresenter::Destroy(); - } -} - -// FUNCTION: LEGO1 0x100c2890 -void MxMIDIPresenter::ReadyTickle() -{ - MxStreamChunk* chunk = NextChunk(); - - if (chunk) { - m_subscriber->FreeDataChunk(chunk); - ParseExtra(); - ProgressTickleState(e_starting); - } -} - -// FUNCTION: LEGO1 0x100c28d0 -void MxMIDIPresenter::StartingTickle() -{ - MxStreamChunk* chunk = CurrentChunk(); - - if (chunk && m_action->GetElapsedTime() >= chunk->GetTime()) { - ProgressTickleState(e_streaming); - } -} - -// FUNCTION: LEGO1 0x100c2910 -void MxMIDIPresenter::StreamingTickle() -{ - if (m_chunk) { - ProgressTickleState(e_done); - } - else { - m_chunk = NextChunk(); - } -} - -// FUNCTION: LEGO1 0x100c2940 -void MxMIDIPresenter::DoneTickle() -{ - if (!MusicManager()->GetMIDIInitialized()) { - EndAction(); - } -} - -// FUNCTION: LEGO1 0x100c2960 -void MxMIDIPresenter::Destroy() -{ - Destroy(FALSE); -} - -// FUNCTION: LEGO1 0x100c2970 -MxResult MxMIDIPresenter::PutData() -{ - m_criticalSection.Enter(); - - if (m_currentTickleState == e_streaming && m_chunk && !MusicManager()->GetMIDIInitialized()) { - SetVolume(((MxDSSound*) m_action)->GetVolume()); - - if (MusicManager()->InitializeMIDI(m_chunk->GetData(), 1) != SUCCESS) { - EndAction(); - } - } - - m_criticalSection.Leave(); - return SUCCESS; -} - -// FUNCTION: LEGO1 0x100c29e0 -void MxMIDIPresenter::EndAction() -{ - if (m_action) { - AUTOLOCK(m_criticalSection); - - MxMediaPresenter::EndAction(); - MusicManager()->DeinitializeMIDI(); - } -} - -// FUNCTION: LEGO1 0x100c2a60 -void MxMIDIPresenter::SetVolume(MxS32 p_volume) -{ - m_volume = p_volume; - MusicManager()->SetMultiplier(p_volume); -} diff --git a/LEGO1/omni/src/audio/mxmusicmanager.cpp b/LEGO1/omni/src/audio/mxmusicmanager.cpp deleted file mode 100644 index 762d3757..00000000 --- a/LEGO1/omni/src/audio/mxmusicmanager.cpp +++ /dev/null @@ -1,294 +0,0 @@ -#include "mxmusicmanager.h" - -#include "mxmisc.h" -#include "mxticklemanager.h" -#include "mxticklethread.h" - -#include - -DECOMP_SIZE_ASSERT(MxMusicManager, 0x58); - -// FUNCTION: LEGO1 0x100c05a0 -MxMusicManager::MxMusicManager() -{ - Init(); -} - -// FUNCTION: LEGO1 0x100c0630 -MxMusicManager::~MxMusicManager() -{ - Destroy(TRUE); -} - -// FUNCTION: LEGO1 0x100c0690 -void MxMusicManager::Init() -{ - m_multiplier = 100; - InitData(); -} - -// FUNCTION: LEGO1 0x100c06a0 -void MxMusicManager::InitData() -{ - m_midiStreamH = 0; - m_midiInitialized = FALSE; - m_bufferSize = 0; - m_bufferCurrentSize = 0; - m_bufferOffset = 0; - m_bufferCurrentOffset = 0; - m_loopCount = 0; - m_midiHdrP = NULL; -} - -// FUNCTION: LEGO1 0x100c06c0 -void MxMusicManager::Destroy(MxBool p_fromDestructor) -{ - if (m_thread) { - m_thread->Terminate(); - if (m_thread) { - delete m_thread; - } - } - else { - TickleManager()->UnregisterClient(this); - } - - m_criticalSection.Enter(); - DeinitializeMIDI(); - Init(); - m_criticalSection.Leave(); - - if (!p_fromDestructor) { - MxAudioManager::Destroy(); - } -} - -// FUNCTION: LEGO1 0x100c0720 -MxResult MxMusicManager::ResetStream() -{ - MxResult result = FAILURE; - - if (m_midiInitialized) { - if (m_bufferCurrentSize == 0) { - if (m_loopCount != -1) { - m_loopCount += -1; - - if (!m_loopCount) { - DeinitializeMIDI(); - goto done; - } - } - - ResetBuffer(); - } - - if (m_midiHdrP->dwFlags & MHDR_DONE || m_midiHdrP->dwFlags & MHDR_PREPARED) { - if (midiOutUnprepareHeader((HMIDIOUT) m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR)) != MMSYSERR_NOERROR) { - goto done; - } - - memset(m_midiHdrP, 0, sizeof(MIDIHDR)); - } - - m_bufferCurrentOffset += 4; - DWORD length = *((DWORD*) m_bufferCurrentOffset); - m_bufferCurrentOffset += sizeof(DWORD); - - m_midiHdrP->lpData = (LPSTR) m_bufferCurrentOffset; - m_midiHdrP->dwBufferLength = length; - m_midiHdrP->dwBytesRecorded = length; - - if (!midiOutPrepareHeader((HMIDIOUT) m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR))) { - if (!midiStreamOut(m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR))) { - result = SUCCESS; - m_bufferCurrentOffset += length; - m_bufferCurrentSize--; - } - } - } - -done: - return result; -} - -// FUNCTION: LEGO1 0x100c07e0 -void MxMusicManager::ResetBuffer() -{ - m_bufferCurrentOffset = m_bufferOffset; - m_bufferCurrentSize = m_bufferSize; -} - -// FUNCTION: LEGO1 0x100c07f0 -void MxMusicManager::SetMIDIVolume() -{ - MxS32 result = (m_volume * m_multiplier) / 0x64; - HMIDISTRM streamHandle = m_midiStreamH; - - if (streamHandle) { - MxS32 volume = CalculateVolume(result); - midiOutSetVolume((HMIDIOUT) streamHandle, volume); - } -} - -// FUNCTION: LEGO1 0x100c0820 -void CALLBACK MxMusicManager::MidiCallbackProc(HDRVR p_hdrvr, UINT p_uMsg, DWORD p_dwUser, DWORD p_dw1, DWORD p_dw2) -{ - if (p_uMsg == MOM_DONE) { - ((MxMusicManager*) p_dwUser)->ResetStream(); - } -} - -// FUNCTION: LEGO1 0x100c0840 -MxResult MxMusicManager::Create(MxU32 p_frequencyMS, MxBool p_createThread) -{ - MxResult status = FAILURE; - MxBool locked = FALSE; - - if (MxAudioManager::Create() == SUCCESS) { - if (p_createThread) { - m_criticalSection.Enter(); - locked = TRUE; - m_thread = new MxTickleThread(this, p_frequencyMS); - - if (!m_thread || m_thread->Start(0, 0) != SUCCESS) { - goto done; - } - } - else { - TickleManager()->RegisterClient(this, p_frequencyMS); - } - - status = SUCCESS; - } - -done: - if (status != SUCCESS) { - Destroy(); - } - - if (locked) { - m_criticalSection.Leave(); - } - - return status; -} - -// FUNCTION: LEGO1 0x100c0930 -void MxMusicManager::Destroy() -{ - Destroy(FALSE); -} - -// FUNCTION: LEGO1 0x100c0940 -void MxMusicManager::SetVolume(MxS32 p_volume) -{ - MxAudioManager::SetVolume(p_volume); - m_criticalSection.Enter(); - SetMIDIVolume(); - m_criticalSection.Leave(); -} - -// FUNCTION: LEGO1 0x100c0970 -void MxMusicManager::SetMultiplier(MxS32 p_multiplier) -{ - m_criticalSection.Enter(); - m_multiplier = p_multiplier; - SetMIDIVolume(); - m_criticalSection.Leave(); -} - -// FUNCTION: LEGO1 0x100c09a0 -MxS32 MxMusicManager::CalculateVolume(MxS32 p_volume) -{ - MxS32 result = (p_volume * 0xffff) / 100; - return (result << 0x10) | result; -} - -// FUNCTION: LEGO1 0x100c09c0 -MxResult MxMusicManager::InitializeMIDI(MxU8* p_data, MxS32 p_loopCount) -{ - MxResult result = FAILURE; - - m_criticalSection.Enter(); - - if (!m_midiInitialized) { - MxU32 total = midiOutGetNumDevs(); - MxU32 device = 0; - - for (; device < total; device++) { - MIDIOUTCAPSA caps; - midiOutGetDevCapsA(device, &caps, sizeof(MIDIOUTCAPSA)); - if (caps.wTechnology == MOD_FMSYNTH) { - break; - } - } - - if (device >= total) { - device = -1; - } - - if (midiStreamOpen(&m_midiStreamH, &device, 1, (DWORD) MidiCallbackProc, (DWORD) this, CALLBACK_FUNCTION) != - MMSYSERR_NOERROR) { - goto done; - } - - GetMIDIVolume(m_midiVolume); - - m_midiHdrP = new MIDIHDR(); - if (!m_midiHdrP) { - goto done; - } - - memset(m_midiHdrP, 0, sizeof(MIDIHDR)); - - MIDIPROPTIMEDIV timediv; - timediv.cbStruct = 8; - m_bufferOffset = p_data; - m_bufferOffset += 0x14; - timediv.dwTimeDiv = *((DWORD*) m_bufferOffset); - - if (midiStreamProperty(m_midiStreamH, (LPBYTE) &timediv, MIDIPROP_SET | MIDIPROP_TIMEDIV) != MMSYSERR_NOERROR) { - goto done; - } - - m_bufferOffset += 0x14; - m_bufferSize = *((MxU32*) m_bufferOffset); - m_bufferOffset += sizeof(MxU32); - m_loopCount = p_loopCount; - m_midiInitialized = TRUE; - - ResetBuffer(); - if (ResetStream() != SUCCESS) { - goto done; - } - - SetMIDIVolume(); - if (midiStreamRestart(m_midiStreamH) != MMSYSERR_NOERROR) { - goto done; - } - - result = SUCCESS; - } - -done: - m_criticalSection.Leave(); - return result; -} - -// FUNCTION: LEGO1 0x100c0b20 -void MxMusicManager::DeinitializeMIDI() -{ - m_criticalSection.Enter(); - - if (m_midiInitialized) { - m_midiInitialized = FALSE; - midiStreamStop(m_midiStreamH); - midiOutUnprepareHeader((HMIDIOUT) m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR)); - midiOutSetVolume((HMIDIOUT) m_midiStreamH, m_midiVolume); - midiStreamClose(m_midiStreamH); - delete m_midiHdrP; - InitData(); - } - - m_criticalSection.Leave(); -} diff --git a/LEGO1/omni/src/audio/mxmusicpresenter.cpp b/LEGO1/omni/src/audio/mxmusicpresenter.cpp deleted file mode 100644 index 9f327ae4..00000000 --- a/LEGO1/omni/src/audio/mxmusicpresenter.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "mxmusicpresenter.h" - -#include "decomp.h" -#include "mxmisc.h" -#include "mxmusicmanager.h" - -DECOMP_SIZE_ASSERT(MxMusicPresenter, 0x54); - -// FUNCTION: LEGO1 0x100c22c0 -MxMusicPresenter::MxMusicPresenter() -{ - Init(); -} - -// FUNCTION: LEGO1 0x100c24e0 -MxMusicPresenter::~MxMusicPresenter() -{ - Destroy(TRUE); -} - -// FUNCTION: LEGO1 0x100c2540 -void MxMusicPresenter::Init() -{ -} - -// FUNCTION: LEGO1 0x100c2550 -void MxMusicPresenter::Destroy(MxBool p_fromDestructor) -{ - if (MusicManager()) { - MusicManager()->UnregisterPresenter(*this); - } - - m_criticalSection.Enter(); - Init(); - m_criticalSection.Leave(); - - if (!p_fromDestructor) { - MxMediaPresenter::Destroy(FALSE); - } -} - -// FUNCTION: LEGO1 0x100c25a0 -MxResult MxMusicPresenter::AddToManager() -{ - MxResult result = FAILURE; - - if (MusicManager()) { - result = SUCCESS; - MusicManager()->RegisterPresenter(*this); - } - - return result; -} - -// FUNCTION: LEGO1 0x100c25d0 -void MxMusicPresenter::Destroy() -{ - Destroy(FALSE); -} diff --git a/LEGO1/omni/src/audio/mxsoundmanager.cpp b/LEGO1/omni/src/audio/mxsoundmanager.cpp index e97f7f29..4c9e8e63 100644 --- a/LEGO1/omni/src/audio/mxsoundmanager.cpp +++ b/LEGO1/omni/src/audio/mxsoundmanager.cpp @@ -37,8 +37,8 @@ MxSoundManager::~MxSoundManager() // FUNCTION: LEGO1 0x100ae830 void MxSoundManager::Init() { - m_directSound = NULL; - m_dsBuffer = NULL; + SDL_zero(m_engine); + m_stream = NULL; } // FUNCTION: LEGO1 0x100ae840 @@ -54,10 +54,12 @@ void MxSoundManager::Destroy(MxBool p_fromDestructor) m_criticalSection.Enter(); - if (m_dsBuffer) { - m_dsBuffer->Release(); + if (m_stream) { + SDL_DestroyAudioStream(m_stream); } + ma_engine_uninit(&m_engine); + Init(); m_criticalSection.Leave(); @@ -79,56 +81,27 @@ MxResult MxSoundManager::Create(MxU32 p_frequencyMS, MxBool p_createThread) m_criticalSection.Enter(); locked = TRUE; - if (DirectSoundCreate(NULL, &m_directSound, NULL) != DS_OK) { + ma_engine_config engineConfig = ma_engine_config_init(); + engineConfig.noDevice = MA_TRUE; + engineConfig.channels = MxOmni::IsSound3D() ? 2 : 1; + engineConfig.sampleRate = g_sampleRate; + + if (ma_engine_init(&engineConfig, &m_engine) != MA_SUCCESS) { goto done; } - if (m_directSound->SetCooperativeLevel(MxOmni::GetInstance()->GetWindowHandle(), DSSCL_PRIORITY) != DS_OK) { + SDL_AudioSpec spec; + SDL_zero(spec); + spec.freq = ma_engine_get_sample_rate(&m_engine); + spec.format = SDL_AUDIO_F32; + spec.channels = ma_engine_get_channels(&m_engine); + + if ((m_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, &AudioStreamCallback, this)) == + NULL) { goto done; } - DSBUFFERDESC desc; - memset(&desc, 0, sizeof(desc)); - desc.dwSize = sizeof(desc); - - if (MxOmni::IsSound3D()) { - desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D; - } - else { - desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; - } - - if (m_directSound->CreateSoundBuffer(&desc, &m_dsBuffer, NULL) != DS_OK) { - if (!MxOmni::IsSound3D()) { - goto done; - } - - MxOmni::SetSound3D(FALSE); - desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; - - if (m_directSound->CreateSoundBuffer(&desc, &m_dsBuffer, NULL) != DS_OK) { - goto done; - } - } - - WAVEFORMATEX format; - - format.wFormatTag = WAVE_FORMAT_PCM; - - if (MxOmni::IsSound3D()) { - format.nChannels = 2; - } - else { - format.nChannels = 1; - } - - format.nSamplesPerSec = 11025; // KHz - format.wBitsPerSample = 16; - format.nBlockAlign = format.nChannels * 2; - format.nAvgBytesPerSec = format.nBlockAlign * 11025; - format.cbSize = 0; - - status = m_dsBuffer->SetFormat(&format); + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(m_stream)); if (p_createThread) { m_thread = new MxTickleThread(this, p_frequencyMS); @@ -154,6 +127,25 @@ done: return status; } +void MxSoundManager::AudioStreamCallback( + void* p_userdata, + SDL_AudioStream* p_stream, + int p_additionalAmount, + int p_totalAmount +) +{ + static MxU8 g_buffer[4096]; + + MxSoundManager* manager = (MxSoundManager*) p_userdata; + ma_uint32 bytesPerFrame = ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&manager->m_engine)); + ma_uint32 bufferSizeInFrames = (ma_uint32) SDL_min(sizeof(g_buffer), p_additionalAmount) / bytesPerFrame; + ma_uint64 framesRead; + + if (ma_engine_read_pcm_frames(&manager->m_engine, g_buffer, bufferSizeInFrames, &framesRead) == MA_SUCCESS) { + SDL_PutAudioStreamData(manager->m_stream, g_buffer, framesRead * bytesPerFrame); + } +} + // FUNCTION: LEGO1 0x100aeab0 void MxSoundManager::Destroy() { @@ -196,15 +188,15 @@ MxPresenter* MxSoundManager::FUN_100aebd0(const MxAtomId& p_atomId, MxU32 p_obje } // FUNCTION: LEGO1 0x100aecf0 -MxS32 MxSoundManager::GetAttenuation(MxU32 p_volume) +float MxSoundManager::GetAttenuation(MxU32 p_volume) { - // The unit for p_volume is percent, rounded to integer. - // Convert to DSOUND attenuation units: -10000 (silent) to 0 (loudest). + // [library:audio] Convert DSOUND attenutation units to linear miniaudio volume + if (p_volume == 0) { - return DSBVOLUME_MIN; + return 0.0f; } - return g_volumeAttenuation[p_volume - 1]; + return ma_volume_db_to_linear((float) g_volumeAttenuation[p_volume - 1] / 100.0f); } // FUNCTION: LEGO1 0x100aed10 diff --git a/LEGO1/omni/src/audio/mxwavepresenter.cpp b/LEGO1/omni/src/audio/mxwavepresenter.cpp index caac77cc..db7f54e3 100644 --- a/LEGO1/omni/src/audio/mxwavepresenter.cpp +++ b/LEGO1/omni/src/audio/mxwavepresenter.cpp @@ -10,6 +10,8 @@ #include "mxsoundmanager.h" #include "mxutilities.h" +#include + DECOMP_SIZE_ASSERT(MxWavePresenter, 0x6c); DECOMP_SIZE_ASSERT(MxWavePresenter::WaveFormat, 0x18); @@ -17,10 +19,9 @@ DECOMP_SIZE_ASSERT(MxWavePresenter::WaveFormat, 0x18); void MxWavePresenter::Init() { m_waveFormat = NULL; - m_dsBuffer = NULL; + SDL_zero(m_rb); + SDL_zero(m_sound); m_chunkLength = 0; - m_lockSize = 0; - m_writtenChunks = 0; m_started = FALSE; m_is3d = FALSE; m_paused = FALSE; @@ -37,10 +38,8 @@ MxResult MxWavePresenter::AddToManager() // FUNCTION: LEGO1 0x100b1b10 void MxWavePresenter::Destroy(MxBool p_fromDestructor) { - if (m_dsBuffer) { - m_dsBuffer->Stop(); - m_dsBuffer->Release(); - } + ma_sound_uninit(&m_sound); + ma_pcm_rb_uninit(&m_rb); if (m_waveFormat) { delete[] ((MxU8*) m_waveFormat); @@ -53,64 +52,34 @@ void MxWavePresenter::Destroy(MxBool p_fromDestructor) } } -// FUNCTION: LEGO1 0x100b1b60 -MxS8 MxWavePresenter::GetPlayedChunks() -{ - DWORD dwCurrentPlayCursor, dwCurrentWriteCursor; - MxS8 playedChunks = -1; - - if (m_dsBuffer->GetCurrentPosition(&dwCurrentPlayCursor, &dwCurrentWriteCursor) == DS_OK) { - playedChunks = dwCurrentPlayCursor / m_chunkLength; - } - - return playedChunks; -} - -// FUNCTION: LEGO1 0x100b1ba0 -MxBool MxWavePresenter::FUN_100b1ba0() -{ - return !m_started || GetPlayedChunks() != m_writtenChunks; -} - // FUNCTION: LEGO1 0x100b1bd0 -void MxWavePresenter::WriteToSoundBuffer(void* p_audioPtr, MxU32 p_length) +MxBool MxWavePresenter::WriteToSoundBuffer(void* p_audioPtr, MxU32 p_length) { - DWORD dwStatus; - LPVOID pvAudioPtr1; - DWORD dwOffset; - LPVOID pvAudioPtr2; - DWORD dwAudioBytes1; - DWORD dwAudioBytes2; + ma_uint32 requestedFrames = + ma_calculate_buffer_size_in_frames_from_milliseconds(g_millisecondsPerChunk, m_waveFormat->m_samplesPerSec); + ma_uint32 acquiredFrames = requestedFrames; + void* bufferOut; - dwOffset = m_chunkLength * m_writtenChunks; - m_dsBuffer->GetStatus(&dwStatus); + ma_pcm_rb_acquire_write(&m_rb, &acquiredFrames, &bufferOut); - if (dwStatus == DSBSTATUS_BUFFERLOST) { - m_dsBuffer->Restore(); - m_dsBuffer->GetStatus(&dwStatus); + // [library:audio] If there isn't enough space in the buffer for a full chunk, try again later. + if (acquiredFrames != requestedFrames) { + ma_pcm_rb_commit_write(&m_rb, 0); + return FALSE; } - if (dwStatus != DSBSTATUS_BUFFERLOST) { - if (m_action->GetFlags() & MxDSAction::c_looping) { - m_writtenChunks++; - m_lockSize = p_length; - } - else { - m_writtenChunks = 1 - m_writtenChunks; - m_lockSize = m_chunkLength; - } + ma_uint32 acquiredBytes = acquiredFrames * ma_get_bytes_per_frame(m_rb.format, m_rb.channels); + assert(p_length <= acquiredBytes); - if (m_dsBuffer->Lock(dwOffset, m_lockSize, &pvAudioPtr1, &dwAudioBytes1, &pvAudioPtr2, &dwAudioBytes2, 0) == - DS_OK) { - memcpy(pvAudioPtr1, p_audioPtr, p_length); + memcpy(bufferOut, p_audioPtr, p_length); - if (m_lockSize > p_length && !(m_action->GetFlags() & MxDSAction::c_looping)) { - memset((MxU8*) pvAudioPtr1 + p_length, m_silenceData, m_lockSize - p_length); - } - - m_dsBuffer->Unlock(pvAudioPtr1, m_lockSize, pvAudioPtr2, 0); - } + // [library:audio] Pad with silence data if we don't have a full chunk. + if (p_length < acquiredBytes) { + memset((ma_uint8*) bufferOut + p_length, m_silenceData, acquiredBytes - p_length); } + + ma_pcm_rb_commit_write(&m_rb, acquiredFrames); + return TRUE; } // FUNCTION: LEGO1 0x100b1cf0 @@ -133,55 +102,68 @@ void MxWavePresenter::StartingTickle() MxStreamChunk* chunk = CurrentChunk(); if (chunk && m_action->GetElapsedTime() >= chunk->GetTime()) { - MxU32 length = chunk->GetLength(); - WAVEFORMATEX waveFormatEx; + MxBool success = FALSE; + m_chunkLength = chunk->GetLength(); - m_chunkLength = length; - memset(&waveFormatEx, 0, sizeof(waveFormatEx)); + assert(m_waveFormat->m_formatTag == g_supportedFormatTag); + assert(m_waveFormat->m_bitsPerSample == 8 || m_waveFormat->m_bitsPerSample == 16); - waveFormatEx.wFormatTag = m_waveFormat->m_pcmWaveFormat.wf.wFormatTag; - waveFormatEx.nChannels = m_waveFormat->m_pcmWaveFormat.wf.nChannels; - waveFormatEx.nSamplesPerSec = m_waveFormat->m_pcmWaveFormat.wf.nSamplesPerSec; - waveFormatEx.nAvgBytesPerSec = m_waveFormat->m_pcmWaveFormat.wf.nAvgBytesPerSec; - waveFormatEx.nBlockAlign = m_waveFormat->m_pcmWaveFormat.wf.nBlockAlign; - waveFormatEx.wBitsPerSample = m_waveFormat->m_pcmWaveFormat.wBitsPerSample; + // [library:audio] + // The original game supported a looping/repeating action mode which apparently + // went unused in the retail version of the game (at least for audio). + // It is only ever "used" on startup to load two dummy sounds which are + // initially disabled and never play: IsleScript::c_TransitionSound1 and IsleScript::c_TransitionSound2 + // If MxDSAction::c_looping was set, MxWavePresenter kept the entire sound track + // in a buffer, presumably to allow random seeking and looping. This functionality + // has most likely been superseded by the looping mechanism implemented in the streaming layer. + // Since this has gone unused and to reduce complexity, we don't allow this anymore; + // except for the two dummy sounds, which must be !IsEnabled() + assert(!(m_action->GetFlags() & MxDSAction::c_looping) || !IsEnabled()); - if (waveFormatEx.wBitsPerSample == 8) { + if (m_waveFormat->m_bitsPerSample == 8) { m_silenceData = 0x7F; } - if (waveFormatEx.wBitsPerSample == 16) { + if (m_waveFormat->m_bitsPerSample == 16) { m_silenceData = 0; } - DSBUFFERDESC desc; - memset(&desc, 0, sizeof(desc)); - desc.dwSize = sizeof(desc); - - if (m_is3d) { - desc.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME; - } - else { - desc.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME; + if (ma_pcm_rb_init( + m_waveFormat->m_bitsPerSample == 16 ? ma_format_s16 : ma_format_u8, + m_waveFormat->m_channels, + ma_calculate_buffer_size_in_frames_from_milliseconds( + g_rbSizeInMilliseconds, + m_waveFormat->m_samplesPerSec + ), + NULL, + NULL, + &m_rb + ) != MA_SUCCESS) { + goto done; } - if (m_action->GetFlags() & MxDSAction::c_looping) { - desc.dwBufferBytes = m_waveFormat->m_pcmWaveFormat.wf.nAvgBytesPerSec * - (m_action->GetDuration() / m_action->GetLoopCount()) / 1000; - } - else { - desc.dwBufferBytes = 2 * length; + ma_pcm_rb_set_sample_rate(&m_rb, m_waveFormat->m_samplesPerSec); + + if (ma_sound_init_from_data_source( + MSoundManager()->GetEngine(), + &m_rb, + m_is3d ? 0 : MA_SOUND_FLAG_NO_SPATIALIZATION, + NULL, + &m_sound + ) != MA_SUCCESS) { + goto done; } - desc.lpwfxFormat = &waveFormatEx; + ma_sound_set_looping(&m_sound, MA_TRUE); - if (MSoundManager()->GetDirectSound()->CreateSoundBuffer(&desc, &m_dsBuffer, NULL) != DS_OK) { + SetVolume(((MxDSSound*) m_action)->GetVolume()); + ProgressTickleState(e_streaming); + success = TRUE; + + done: + if (!success) { EndAction(); } - else { - SetVolume(((MxDSSound*) m_action)->GetVolume()); - ProgressTickleState(e_streaming); - } } } @@ -215,18 +197,8 @@ void MxWavePresenter::StreamingTickle() // FUNCTION: LEGO1 0x100b20c0 void MxWavePresenter::DoneTickle() { - if (m_dsBuffer) { - DWORD dwCurrentPlayCursor, dwCurrentWriteCursor; - m_dsBuffer->GetCurrentPosition(&dwCurrentPlayCursor, &dwCurrentWriteCursor); - - MxS8 playedChunks = dwCurrentPlayCursor / m_chunkLength; - if (m_action->GetFlags() & MxDSAction::c_bit7 || m_action->GetFlags() & MxDSAction::c_looping || - (!(m_action->GetFlags() & MxDSAction::c_looping) && - (m_writtenChunks != playedChunks || m_lockSize + (m_chunkLength * playedChunks) <= dwCurrentPlayCursor))) { - MxMediaPresenter::DoneTickle(); - } - } - else { + if (!ma_sound_get_engine(&m_sound) || m_action->GetFlags() & MxDSAction::c_bit7 || + ma_pcm_rb_pointer_distance(&m_rb) == 0) { MxMediaPresenter::DoneTickle(); } } @@ -248,16 +220,13 @@ MxResult MxWavePresenter::PutData() if (IsEnabled()) { switch (m_currentTickleState) { case e_streaming: - if (m_currentChunk && FUN_100b1ba0()) { - WriteToSoundBuffer(m_currentChunk->GetData(), m_currentChunk->GetLength()); + if (m_currentChunk && WriteToSoundBuffer(m_currentChunk->GetData(), m_currentChunk->GetLength())) { m_subscriber->FreeDataChunk(m_currentChunk); m_currentChunk = NULL; } if (!m_started) { - m_dsBuffer->SetCurrentPosition(0); - - if (m_dsBuffer->Play(0, 0, DSBPLAY_LOOPING) == DS_OK) { + if (ma_sound_start(&m_sound) == MA_SUCCESS) { m_started = TRUE; } } @@ -267,9 +236,7 @@ MxResult MxWavePresenter::PutData() break; } - m_dsBuffer->SetCurrentPosition(0); - - if (m_dsBuffer->Play(0, 0, m_action->GetLoopCount() > 1) == DS_OK) { + if (ma_sound_start(&m_sound) == MA_SUCCESS) { m_started = TRUE; } } @@ -285,8 +252,8 @@ void MxWavePresenter::EndAction() AUTOLOCK(m_criticalSection); MxMediaPresenter::EndAction(); - if (m_dsBuffer) { - m_dsBuffer->Stop(); + if (ma_sound_get_engine(&m_sound)) { + ma_sound_stop(&m_sound); } } } @@ -297,10 +264,10 @@ void MxWavePresenter::SetVolume(MxS32 p_volume) m_criticalSection.Enter(); m_volume = p_volume; - if (m_dsBuffer != NULL) { + if (ma_sound_get_engine(&m_sound)) { MxS32 volume = p_volume * MxOmni::GetInstance()->GetSoundManager()->GetVolume() / 100; - MxS32 attenuation = MxOmni::GetInstance()->GetSoundManager()->GetAttenuation(volume); - m_dsBuffer->SetVolume(attenuation); + float attenuation = MxOmni::GetInstance()->GetSoundManager()->GetAttenuation(volume); + ma_sound_set_volume(&m_sound, attenuation); } m_criticalSection.Leave(); @@ -313,11 +280,10 @@ void MxWavePresenter::Enable(MxBool p_enable) MxSoundPresenter::Enable(p_enable); if (p_enable) { - m_writtenChunks = 0; m_started = FALSE; } - else if (m_dsBuffer) { - m_dsBuffer->Stop(); + else if (ma_sound_get_engine(&m_sound)) { + ma_sound_stop(&m_sound); } } } @@ -349,8 +315,8 @@ void MxWavePresenter::ParseExtra() void MxWavePresenter::Pause() { if (!m_paused && m_started) { - if (m_dsBuffer) { - m_dsBuffer->Stop(); + if (ma_sound_get_engine(&m_sound)) { + ma_sound_stop(&m_sound); } m_paused = TRUE; } @@ -360,17 +326,8 @@ void MxWavePresenter::Pause() void MxWavePresenter::Resume() { if (m_paused) { - if (m_dsBuffer && m_started) { - switch (m_currentTickleState) { - case e_streaming: - m_dsBuffer->Play(0, 0, DSBPLAY_LOOPING); - break; - case e_repeating: - m_dsBuffer->Play(0, 0, m_action->GetLoopCount() > 1); - break; - case e_done: - m_dsBuffer->Play(0, 0, 0); - } + if (ma_sound_get_engine(&m_sound) && m_started) { + ma_sound_start(&m_sound); } m_paused = FALSE; diff --git a/LEGO1/omni/src/common/mxmisc.cpp b/LEGO1/omni/src/common/mxmisc.cpp index 8dbab329..98e21f89 100644 --- a/LEGO1/omni/src/common/mxmisc.cpp +++ b/LEGO1/omni/src/common/mxmisc.cpp @@ -76,14 +76,6 @@ MxVariableTable* VariableTable() return MxOmni::GetInstance()->GetVariableTable(); } -// FUNCTION: LEGO1 0x100acf30 -// FUNCTION: BETA10 0x10124faf -MxMusicManager* MusicManager() -{ - assert(MxOmni::GetInstance()); - return MxOmni::GetInstance()->GetMusicManager(); -} - // FUNCTION: LEGO1 0x100acf40 // FUNCTION: BETA10 0x10124ff6 MxEventManager* EventManager() diff --git a/LEGO1/omni/src/common/mxobjectfactory.cpp b/LEGO1/omni/src/common/mxobjectfactory.cpp index 3722bb0e..eab64110 100644 --- a/LEGO1/omni/src/common/mxobjectfactory.cpp +++ b/LEGO1/omni/src/common/mxobjectfactory.cpp @@ -5,9 +5,7 @@ #include "mxeventpresenter.h" #include "mxflcpresenter.h" #include "mxloopingflcpresenter.h" -#include "mxloopingmidipresenter.h" #include "mxloopingsmkpresenter.h" -#include "mxmidipresenter.h" #include "mxpresenter.h" #include "mxsmkpresenter.h" #include "mxstillpresenter.h" diff --git a/LEGO1/omni/src/common/mxpresenter.cpp b/LEGO1/omni/src/common/mxpresenter.cpp index e599f568..742a02ce 100644 --- a/LEGO1/omni/src/common/mxpresenter.cpp +++ b/LEGO1/omni/src/common/mxpresenter.cpp @@ -11,9 +11,7 @@ #include "mxeventpresenter.h" #include "mxflcpresenter.h" #include "mxloopingflcpresenter.h" -#include "mxloopingmidipresenter.h" #include "mxloopingsmkpresenter.h" -#include "mxmidipresenter.h" #include "mxmisc.h" #include "mxnotificationmanager.h" #include "mxobjectfactory.h" @@ -210,8 +208,8 @@ const char* PresenterNameDispatch(const MxDSAction& p_action) format = ((MxDSSound&) p_action).GetMediaFormat(); switch (format) { case FOURCC(' ', 'M', 'I', 'D'): - name = !p_action.IsLooping() ? MxMIDIPresenter::HandlerClassName() - : MxLoopingMIDIPresenter::HandlerClassName(); + // [library:audio] No MIDI files are used in the retail version of the game. + assert(FALSE); break; case FOURCC(' ', 'W', 'A', 'V'): name = MxWavePresenter::HandlerClassName(); diff --git a/LEGO1/omni/src/main/mxomni.cpp b/LEGO1/omni/src/main/mxomni.cpp index cdfe400d..16a12d48 100644 --- a/LEGO1/omni/src/main/mxomni.cpp +++ b/LEGO1/omni/src/main/mxomni.cpp @@ -6,7 +6,6 @@ #include "mxdsmultiaction.h" #include "mxeventmanager.h" #include "mxmisc.h" -#include "mxmusicmanager.h" #include "mxnotificationmanager.h" #include "mxobjectfactory.h" #include "mxomnicreateparam.h" @@ -63,7 +62,6 @@ void MxOmni::Init() m_notificationManager = NULL; m_videoManager = NULL; m_soundManager = NULL; - m_musicManager = NULL; m_eventManager = NULL; m_timer = NULL; m_streamer = NULL; @@ -149,15 +147,6 @@ MxResult MxOmni::Create(MxOmniCreateParam& p_param) } } - if (p_param.CreateFlags().CreateMusicManager()) { - if ((m_musicManager = new MxMusicManager())) { - if (m_musicManager->Create(50, 0) != SUCCESS) { - delete m_musicManager; - m_musicManager = NULL; - } - } - } - if (p_param.CreateFlags().CreateEventManager()) { if ((m_eventManager = new MxEventManager())) { if (m_eventManager->Create(50, 0) != SUCCESS) { @@ -198,7 +187,6 @@ void MxOmni::Destroy() delete m_eventManager; delete m_soundManager; - delete m_musicManager; delete m_videoManager; delete m_streamer; delete m_timer; diff --git a/README.md b/README.md index d3161cfc..3415390a 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ To achieve our goal of platform independence, we need to replace any Windows-onl | Filesystem | [SDL3](https://www.libsdl.org/) | ❌ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Afilesystem%5D%22&type=code) | | Threads, Mutexes (Synchronization) | [SDL3](https://www.libsdl.org/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Asynchronization%5D%22&type=code) | | Keyboard/Mouse, Joystick, DirectInput (Input) | [SDL3](https://www.libsdl.org/) | WIP | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Ainput%5D%22&type=code) | -| WinMM, DirectSound (Audio) | [SDL_mixer](https://github.com/libsdl-org/SDL_mixer) | ❌ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aaudio%5D%22&type=code) | +| WinMM, DirectSound (Audio) | [SDL3](https://www.libsdl.org/), [miniaudio](https://miniaud.io/) | WIP | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aaudio%5D%22&type=code) | | DirectDraw (2D video) | [SDL3](https://www.libsdl.org/) | ❌ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A2d%5D%22&type=code) | | [Smacker](https://github.com/isledecomp/isle/tree/master/3rdparty/smacker) | [libsmacker](https://github.com/foxtacles/libsmacker) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable%20%22%2F%2F%20%5Blibrary%3Alibsmacker%5D%22&type=code) | | Direct3D (3D video) | [SDL3](https://www.libsdl.org/), OpenGL ES (**TBD**) | ❌ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A3d%5D%22&type=code) |