From 11991d4d1f16c0b2a44ae840e3902b73cfb39ff6 Mon Sep 17 00:00:00 2001 From: shenlei Date: Wed, 30 Sep 2026 12:07:37 +0900 Subject: [PATCH] MilesLib: run the 40250 sound sources verbatim over an AIL software mixer Copy SoundBase/SoundData/SoundInstance*/SoundManager*/Type from 40250 and implement the AIL_* calls they make in MilesAil.cpp (2D pan, 3D distance and listener pan, threaded stream decode, loop counts). The SDL host drains the mixer. CSoundManager is created/updated/destroyed where 40250 does it and the old AudioCommands bridge is removed. Port-map MilesLib 142/142 PORTED. Co-Authored-By: Claude Opus 5.5 --- audit/history.jsonl | 1 + audit/port-map/MilesLib/SoundBase.cpp.json | 56 + audit/port-map/MilesLib/SoundData.cpp.json | 160 +++ .../MilesLib/SoundInstance2D.cpp.json | 128 ++ .../MilesLib/SoundInstance3D.cpp.json | 136 +++ .../MilesLib/SoundInstanceStream.cpp.json | 144 +++ audit/port-map/MilesLib/SoundManager.cpp.json | 273 ++++- .../port-map/MilesLib/SoundManager2D.cpp.json | 48 + .../port-map/MilesLib/SoundManager3D.cpp.json | 104 ++ .../MilesLib/SoundManagerStream.cpp.json | 64 + audit/port-map/MilesLib/Type.cpp.json | 48 + src/host/live_client.cpp | 1 - src/host/native_audio.cpp | 235 +--- src/host/native_audio.h | 50 +- src/host/sdl_window.cpp | 2 + src/platform/MilesLib/AudioCommands.cpp | 23 - src/platform/MilesLib/AudioCommands.h | 36 - src/platform/MilesLib/MilesAil.cpp | 1029 +++++++++++++++++ src/platform/MilesLib/MilesMixer.h | 19 + src/platform/MilesLib/SoundBase.cpp | 74 +- src/platform/MilesLib/SoundData.cpp | 301 +++-- src/platform/MilesLib/SoundInstance.cpp | 256 ---- src/platform/MilesLib/SoundInstance2D.cpp | 121 ++ src/platform/MilesLib/SoundInstance3D.cpp | 137 +++ src/platform/MilesLib/SoundInstanceStream.cpp | 114 ++ src/platform/MilesLib/SoundManager.cpp | 706 +++++++---- src/platform/MilesLib/SoundManager2D.cpp | 78 +- src/platform/MilesLib/SoundManager3D.cpp | 201 +++- src/platform/MilesLib/SoundManagerStream.cpp | 65 +- src/platform/MilesLib/Type.cpp | 136 ++- src/platform/ScriptLib/PythonBoot.cpp | 15 + src/platform/ScriptLib/PythonBoot.h | 2 + .../UserInterface/PythonApplicationCamera.cpp | 13 +- src/platform/UserInterface/UserInterface.cpp | 11 +- src/port/CMakeLists.txt | 5 + src/port/common/Win32Crt.cpp | 5 + src/port/common/Win32Crt.h | 3 +- src/port/common/shim/sdk/mss.h | 125 +- tests/port/port_miles_test.cpp | 360 ++++++ 39 files changed, 4232 insertions(+), 1053 deletions(-) create mode 100644 audit/port-map/MilesLib/SoundBase.cpp.json create mode 100644 audit/port-map/MilesLib/SoundData.cpp.json create mode 100644 audit/port-map/MilesLib/SoundInstance2D.cpp.json create mode 100644 audit/port-map/MilesLib/SoundInstance3D.cpp.json create mode 100644 audit/port-map/MilesLib/SoundInstanceStream.cpp.json create mode 100644 audit/port-map/MilesLib/SoundManager2D.cpp.json create mode 100644 audit/port-map/MilesLib/SoundManager3D.cpp.json create mode 100644 audit/port-map/MilesLib/SoundManagerStream.cpp.json create mode 100644 audit/port-map/MilesLib/Type.cpp.json delete mode 100644 src/platform/MilesLib/AudioCommands.cpp delete mode 100644 src/platform/MilesLib/AudioCommands.h create mode 100644 src/platform/MilesLib/MilesAil.cpp create mode 100644 src/platform/MilesLib/MilesMixer.h delete mode 100644 src/platform/MilesLib/SoundInstance.cpp create mode 100644 src/platform/MilesLib/SoundInstance2D.cpp create mode 100644 src/platform/MilesLib/SoundInstance3D.cpp create mode 100644 src/platform/MilesLib/SoundInstanceStream.cpp create mode 100644 tests/port/port_miles_test.cpp diff --git a/audit/history.jsonl b/audit/history.jsonl index 146670df..00569c29 100644 --- a/audit/history.jsonl +++ b/audit/history.jsonl @@ -570,3 +570,4 @@ {"date": "2026-09-28T05:22:26+00:00", "unit": "UserInterface/PythonApplication.cpp", "action": "unit J: Process gains 40250's render-time/face-count block and automatic sight range (SetViewDistanceSet(0)); SetForceSightRange/SkipRenderBuffering ported from stubs; invented SetCursorPosition in UpdateGame removed; boilerplate notes on RenderGame/UpdateGame/Process/Destroy replaced (Destroy marked DIVERGENT: never called, teardown via PythonBoot::Stop)", "evidence": "ctest -j3 35/35; fake-server spawn frame 900 mean abs diff 0.33/255 vs previous (fog range not in view); sight-range visuals NEEDS_LIVE"} {"date": "2026-09-28T05:29:23+00:00", "unit": "EterLib/GrpImageTexture.cpp", "action": "unit K: DXT DDS textures upload the file's own mip levels (40250 CreateDDSTexture: max(1,dwMipMapCount), dwLinearSize>>2i Copy) instead of a regenerated chain; non-DDS keep the D3DX_DEFAULT full chain; mem: textures one level; invented is_ui_tex no-mip rule (icon/ui/locale/<=128px) deleted; dxt_decode_test registered in ctest with mip-level cases", "evidence": "ctest -j3 36/36; fake-server spawn frame 900: near terrain diff 0.00, far terrain 2.7/255 (minified levels now from the file), rest is UI timing (area title/chat); far-terrain look NEEDS_LIVE"} {"date": "2026-09-28T05:33:21+00:00", "unit": "SpeedTreeLib/SpeedTreeWrapper.cpp", "action": "SpeedTree follow-up closed without code change: .spt 0x4E25 is one composite rect per file (87/87, independent of 1-3 leaf maps) and overlays on the atlases put it on the billboard tree image for most species, so it is not the leaf-map texcoords; the hand-picked leaf rects stay DIVERGENT, note updated", "evidence": "byte dump of b1_baobab_rt.spt tail (0x4E20/0x4E22 name/0x4E23 b/0x4E24 b/0x4E25 8f/0x4E21); count over 87 .spt files; atlas overlays of 9 species"} +{"date": "2026-09-30T03:07:37+00:00", "unit": "MilesLib/*", "action": "MilesLib platform port: 40250 SoundBase/SoundData/SoundInstance2D/3D/Stream/SoundManager*/Type copied verbatim (only includes + one unused QPC local differ) over an AIL_* software mixer (MilesAil.cpp: 2D balance pan, 3D inverse distance min 1/max 200 + listener pan, threaded stream decode, loop counts, decompress_ASI/ADPCM to float WAV); host NativeAudioEngine drains MtMilesMix; CSoundManager Create/Destroy in PythonBoot (IsNoSoundCard), listener+Update from PythonApplicationCamera, Save/RestoreVolume on window focus, CSoundData::SetPackMode in PackInitialize; old AudioCommands/SoundInstance bridge deleted; 142/142 PORTED", "evidence": "port_miles_test ok (2D/3D/stream fades/DataToInstance); ctest all pass except pre-existing flaky login_flow; desktop live client MT_AUDIO_TRACE peaks 1.17/0.70 at login and ~0.02 in game; Android device not attached, NEEDS device run"} diff --git a/audit/port-map/MilesLib/SoundBase.cpp.json b/audit/port-map/MilesLib/SoundBase.cpp.json new file mode 100644 index 00000000..32e131d1 --- /dev/null +++ b/audit/port-map/MilesLib/SoundBase.cpp.json @@ -0,0 +1,56 @@ +{ + "reference": "MilesLib/SoundBase.cpp", + "reference_sha256": "e44646f6093f7432f6dd56892e9d8883763b2270c09139c1d7ca528dd71929ee", + "priority": "P4", + "contracts": [], + "functions": { + "CSoundBase::CSoundBase": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundBase.cpp:CSoundBase::CSoundBase" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundBase::~CSoundBase": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundBase.cpp:CSoundBase::~CSoundBase" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundBase::Destroy": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundBase.cpp:CSoundBase::Destroy" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundBase::Initialize": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundBase.cpp:CSoundBase::Initialize" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundBase::GetFileCRC": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundBase.cpp:CSoundBase::GetFileCRC" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundBase::AddFile": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundBase.cpp:CSoundBase::AddFile" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + } + } +} diff --git a/audit/port-map/MilesLib/SoundData.cpp.json b/audit/port-map/MilesLib/SoundData.cpp.json new file mode 100644 index 00000000..155a9063 --- /dev/null +++ b/audit/port-map/MilesLib/SoundData.cpp.json @@ -0,0 +1,160 @@ +{ + "reference": "MilesLib/SoundData.cpp", + "reference_sha256": "f028e41436c108c8b24018fa0a7d6b11eecaca942086c71b6d82e1d340703fb5", + "priority": "P4", + "contracts": [], + "functions": { + "CSoundData::GetFileName": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::GetFileName" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::Assign": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::Assign" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::Get": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::Get" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::GetSize": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::GetSize" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::Release": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::Release" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::GetPlayTime": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::GetPlayTime" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::SetPlayTime": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::SetPlayTime" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::GetAccessTime": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::GetAccessTime" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::ReadFromDisk": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::ReadFromDisk" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp); the unused QueryPerformanceCounter local is dropped (no effect)" + }, + "CSoundData::isSlotIndex": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::isSlotIndex" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::GetEmptySlotIndex": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::GetEmptySlotIndex" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::open_callback": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::open_callback" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::close_callback": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::close_callback" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::seek_callback": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::seek_callback" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::read_callback": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::read_callback" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::SetPackMode": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::SetPackMode" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::Destroy": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::Destroy" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::CSoundData": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::CSoundData" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundData::~CSoundData": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundData.cpp:CSoundData::~CSoundData" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + } + } +} diff --git a/audit/port-map/MilesLib/SoundInstance2D.cpp.json b/audit/port-map/MilesLib/SoundInstance2D.cpp.json new file mode 100644 index 00000000..abaaa4f1 --- /dev/null +++ b/audit/port-map/MilesLib/SoundInstance2D.cpp.json @@ -0,0 +1,128 @@ +{ + "reference": "MilesLib/SoundInstance2D.cpp", + "reference_sha256": "deba312791d6628cd5c5c7113b54301f28afc44d84462c22a54feb8150e79060", + "priority": "P4", + "contracts": [], + "functions": { + "CSoundInstance2D::CSoundInstance2D": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::CSoundInstance2D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::~CSoundInstance2D": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::~CSoundInstance2D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::Destroy": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::Destroy" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::Initialize": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::Initialize" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::SetSound": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::SetSound" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::IsDone": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::IsDone" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::Play": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::Play" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::Pause": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::Pause" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::Resume": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::Resume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::Stop": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::Stop" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::GetVolume": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::GetVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::SetVolume": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::SetVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::SetPosition": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::SetPosition" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::SetOrientation": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::SetOrientation" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance2D::SetVelocity": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance2D.cpp:CSoundInstance2D::SetVelocity" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + } + } +} diff --git a/audit/port-map/MilesLib/SoundInstance3D.cpp.json b/audit/port-map/MilesLib/SoundInstance3D.cpp.json new file mode 100644 index 00000000..2828173f --- /dev/null +++ b/audit/port-map/MilesLib/SoundInstance3D.cpp.json @@ -0,0 +1,136 @@ +{ + "reference": "MilesLib/SoundInstance3D.cpp", + "reference_sha256": "1ed8d6f7049d75a0a29d005b4867ec7ed4637bb19f9621bf00a9f9ac1e706c42", + "priority": "P4", + "contracts": [], + "functions": { + "CSoundInstance3D::CSoundInstance3D": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::CSoundInstance3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::~CSoundInstance3D": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::~CSoundInstance3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::Destroy": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::Destroy" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::Initialize": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::Initialize" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::SetSound": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::SetSound" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::IsDone": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::IsDone" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::Play": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::Play" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::Pause": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::Pause" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::Resume": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::Resume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::Stop": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::Stop" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::GetVolume": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::GetVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::SetVolume": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::SetVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::SetPosition": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::SetPosition" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::SetOrientation": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::SetOrientation" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::SetVelocity": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::SetVelocity" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstance3D::UpdatePosition": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstance3D.cpp:CSoundInstance3D::UpdatePosition" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + } + } +} diff --git a/audit/port-map/MilesLib/SoundInstanceStream.cpp.json b/audit/port-map/MilesLib/SoundInstanceStream.cpp.json new file mode 100644 index 00000000..9f8c8999 --- /dev/null +++ b/audit/port-map/MilesLib/SoundInstanceStream.cpp.json @@ -0,0 +1,144 @@ +{ + "reference": "MilesLib/SoundInstanceStream.cpp", + "reference_sha256": "687c0e40ff73198f8ebc0e65f393bd4a58f6c4d14984bef1443ba17680b70cc6", + "priority": "P4", + "contracts": [], + "functions": { + "CSoundInstanceStream::CSoundInstanceStream": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::CSoundInstanceStream" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::~CSoundInstanceStream": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::~CSoundInstanceStream" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::Destroy": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::Destroy" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::Initialize": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::Initialize" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::SetStream": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::SetStream" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::IsDone": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::IsDone" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::IsData": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::IsData" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::Play": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::Play" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::Pause": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::Pause" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::Resume": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::Resume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::Stop": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::Stop" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::GetVolume": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::GetVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::SetVolume": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::SetVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::SetSound": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::SetSound" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::SetPosition": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::SetPosition" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::SetOrientation": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::SetOrientation" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundInstanceStream::SetVelocity": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundInstanceStream.cpp:CSoundInstanceStream::SetVelocity" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + } + } +} diff --git a/audit/port-map/MilesLib/SoundManager.cpp.json b/audit/port-map/MilesLib/SoundManager.cpp.json index 9ddaa839..8d6d7b81 100644 --- a/audit/port-map/MilesLib/SoundManager.cpp.json +++ b/audit/port-map/MilesLib/SoundManager.cpp.json @@ -5,159 +5,324 @@ "contracts": [], "functions": { "CSoundManager::CSoundManager": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::CSoundManager" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::~CSoundManager": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::~CSoundManager" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::Create": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::Create" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::Destroy": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::Destroy" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SetPosition": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SetPosition" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SetDirection": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SetDirection" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::Update": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::Update" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::UpdateSoundData": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::UpdateSoundData" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::UpdateSoundInstance": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::UpdateSoundInstance" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::GetSoundScale": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::GetSoundScale" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SetSoundScale": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SetSoundScale" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SetAmbienceSoundScale": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SetAmbienceSoundScale" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SetSoundVolume": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SetSoundVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::__SetMusicVolume": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::__SetMusicVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::__ConvertRatioVolumeToApplyVolume": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::__ConvertRatioVolumeToApplyVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::__ConvertGradeVolumeToApplyVolume": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::__ConvertGradeVolumeToApplyVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SetSoundVolumeGrade": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SetSoundVolumeGrade" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SetSoundVolumeRatio": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SetSoundVolumeRatio" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SetMusicVolume": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SetMusicVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::GetSoundVolume": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::GetSoundVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::GetMusicVolume": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::GetMusicVolume" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::GetSoundInstance2D": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::GetSoundInstance2D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::GetSoundInstance3D": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::GetSoundInstance3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::PlaySound2D": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::PlaySound2D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::PlaySound3D": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::PlaySound3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::PlayAmbienceSound3D": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::PlayAmbienceSound3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::PlayCharacterSound3D": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::PlayCharacterSound3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::StopSound3D": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::StopSound3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SetSoundVolume3D": { - "status": "ADAPTED", + "status": "PORTED", "impl": [ "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SetSoundVolume3D" ], - "test": "project/audio_driver_test.gd", - "note": "40250 indexed instance volume is forwarded to the active Godot 3D sound player." + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::StopAllSound3D": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::StopAllSound3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::PlayMusic": { - "status": "ADAPTED", + "status": "PORTED", "impl": [ "src/platform/MilesLib/SoundManager.cpp:CSoundManager::PlayMusic" ], - "test": "project/audio_driver_test.gd", - "note": "40250 direct PlayMusic uses stream slot 0; the Godot driver now replaces slot 0." + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::FadeInMusic": { - "status": "ADAPTED", + "status": "PORTED", "impl": [ "src/platform/MilesLib/SoundManager.cpp:CSoundManager::FadeInMusic" ], - "test": "project/audio_driver_test.gd", - "note": "40250 named stream fade-in advances by fVolumeSpeed per update; Godot uses three slots and the same per-frame volume step." + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::FadeLimitOutMusic": { - "status": "ADAPTED", + "status": "PORTED", "impl": [ "src/platform/MilesLib/SoundManager.cpp:CSoundManager::FadeLimitOutMusic" ], - "test": "project/audio_driver_test.gd", - "note": "40250 ratio limit is converted by the original volume formula and the named stream fades to that limit." + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::FadeOutMusic": { - "status": "ADAPTED", + "status": "PORTED", "impl": [ "src/platform/MilesLib/SoundManager.cpp:CSoundManager::FadeOutMusic" ], - "test": "project/audio_driver_test.gd", - "note": "40250 fade-out targets only the named active stream and stops it at volume zero." + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::FadeOutAllMusic": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::FadeOutAllMusic" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::SaveVolume": { - "status": "ADAPTED", + "status": "PORTED", "impl": [ "src/platform/MilesLib/SoundManager.cpp:CSoundManager::SaveVolume" ], - "test": "tests/port/port_login_flow_test.cpp", - "note": "40250 backup and mute sequence is preserved, including the repeated-save guard." + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::RestoreVolume": { - "status": "ADAPTED", + "status": "PORTED", "impl": [ "src/platform/MilesLib/SoundManager.cpp:CSoundManager::RestoreVolume" ], - "test": "tests/port/port_login_flow_test.cpp", - "note": "40250 clears the disabled flag before restoring backed-up music and sound volumes." + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::StopMusic": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::StopMusic" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::GetMusicIndex": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::GetMusicIndex" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" }, "CSoundManager::FadeAll": { - "status": "TODO" + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager.cpp:CSoundManager::FadeAll" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" } } } diff --git a/audit/port-map/MilesLib/SoundManager2D.cpp.json b/audit/port-map/MilesLib/SoundManager2D.cpp.json new file mode 100644 index 00000000..31839723 --- /dev/null +++ b/audit/port-map/MilesLib/SoundManager2D.cpp.json @@ -0,0 +1,48 @@ +{ + "reference": "MilesLib/SoundManager2D.cpp", + "reference_sha256": "c4d23d9c8571bfb1ac49d9bd64bd05517f0cef76dec104418d2db4d7f1389777", + "priority": "P4", + "contracts": [], + "functions": { + "CSoundManager2D::CSoundManager2D": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager2D.cpp:CSoundManager2D::CSoundManager2D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager2D::~CSoundManager2D": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager2D.cpp:CSoundManager2D::~CSoundManager2D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager2D::Initialize": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager2D.cpp:CSoundManager2D::Initialize" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager2D::Destroy": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager2D.cpp:CSoundManager2D::Destroy" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager2D::GetInstance": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager2D.cpp:CSoundManager2D::GetInstance" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + } + } +} diff --git a/audit/port-map/MilesLib/SoundManager3D.cpp.json b/audit/port-map/MilesLib/SoundManager3D.cpp.json new file mode 100644 index 00000000..7b726fc4 --- /dev/null +++ b/audit/port-map/MilesLib/SoundManager3D.cpp.json @@ -0,0 +1,104 @@ +{ + "reference": "MilesLib/SoundManager3D.cpp", + "reference_sha256": "3059a637ec84699f028ba69ae3fe8bdfa9c34e0b3f6a12f19d12067147a92078", + "priority": "P4", + "contracts": [], + "functions": { + "CSoundManager3D::CSoundManager3D": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::CSoundManager3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::~CSoundManager3D": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::~CSoundManager3D" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::Initialize": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::Initialize" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::Destroy": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::Destroy" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::SetListenerDirection": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::SetListenerDirection" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::SetListenerPosition": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::SetListenerPosition" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::SetListenerVelocity": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::SetListenerVelocity" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::SetInstance": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::SetInstance" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::GetInstance": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::GetInstance" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::IsValidInstanceIndex": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::IsValidInstanceIndex" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::Lock": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::Lock" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManager3D::Unlock": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManager3D.cpp:CSoundManager3D::Unlock" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + } + } +} diff --git a/audit/port-map/MilesLib/SoundManagerStream.cpp.json b/audit/port-map/MilesLib/SoundManagerStream.cpp.json new file mode 100644 index 00000000..b1a31c09 --- /dev/null +++ b/audit/port-map/MilesLib/SoundManagerStream.cpp.json @@ -0,0 +1,64 @@ +{ + "reference": "MilesLib/SoundManagerStream.cpp", + "reference_sha256": "230c097f9ead6f7783d2c5aaedffeae4cea30d58ee9d454ae63493315ac7431a", + "priority": "P4", + "contracts": [], + "functions": { + "CSoundManagerStream::CSoundManagerStream": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManagerStream.cpp:CSoundManagerStream::CSoundManagerStream" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManagerStream::~CSoundManagerStream": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManagerStream.cpp:CSoundManagerStream::~CSoundManagerStream" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManagerStream::Initialize": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManagerStream.cpp:CSoundManagerStream::Initialize" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManagerStream::Destroy": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManagerStream.cpp:CSoundManagerStream::Destroy" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManagerStream::SetInstance": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManagerStream.cpp:CSoundManagerStream::SetInstance" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManagerStream::GetInstance": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManagerStream.cpp:CSoundManagerStream::GetInstance" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "CSoundManagerStream::CheckInstanceIndex": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/SoundManagerStream.cpp:CSoundManagerStream::CheckInstanceIndex" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + } + } +} diff --git a/audit/port-map/MilesLib/Type.cpp.json b/audit/port-map/MilesLib/Type.cpp.json new file mode 100644 index 00000000..ce9826ed --- /dev/null +++ b/audit/port-map/MilesLib/Type.cpp.json @@ -0,0 +1,48 @@ +{ + "reference": "MilesLib/Type.cpp", + "reference_sha256": "645eebf6857f0a51c3a339afe202f86cefaf397731e4f53bd5c6803c920adbb8", + "priority": "P4", + "contracts": [], + "functions": { + "NSound::GetResultString": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/Type.cpp:NSound::GetResultString" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "NSound::SetResultString": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/Type.cpp:NSound::SetResultString" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "NSound::LoadSoundInformationPiece": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/Type.cpp:NSound::LoadSoundInformationPiece" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "NSound::SaveSoundInformationPiece": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/Type.cpp:NSound::SaveSoundInformationPiece" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + }, + "NSound::DataToInstance": { + "status": "PORTED", + "impl": [ + "src/platform/MilesLib/Type.cpp:NSound::DataToInstance" + ], + "test": "tests/port/port_miles_test.cpp", + "note": "verbatim 40250 source over the AIL_* emulation in src/platform/MilesLib/MilesAil.cpp (software mixer drained by src/host/native_audio.cpp)" + } + } +} diff --git a/src/host/live_client.cpp b/src/host/live_client.cpp index 3781cf62..0341c885 100644 --- a/src/host/live_client.cpp +++ b/src/host/live_client.cpp @@ -11,7 +11,6 @@ #include "ui_scale.h" #include "vulkan_window.h" -#include "platform/MilesLib/AudioCommands.h" #include "platform/PackBackend.h" #include "platform/ScriptLib/PythonBoot.h" #include "platform/UserInterface/ServerClock.h" diff --git a/src/host/native_audio.cpp b/src/host/native_audio.cpp index dee01109..8e2d9a0d 100644 --- a/src/host/native_audio.cpp +++ b/src/host/native_audio.cpp @@ -2,232 +2,75 @@ #ifdef MT_NATIVE_HAS_LIVE_CLIENT #include "audio_decode.h" -#include "texture_decode.h" -#include "platform/MilesLib/AudioCommands.h" +#include "platform/MilesLib/MilesMixer.h" #include -#include #include -#include +#include +#include namespace mt_host { +namespace { + +bool decode_miles_file(const void* data, std::size_t size, std::vector& out) { + if (!decode_audio(static_cast(data), size, out)) { + SDL_Log("audio: cannot decode a %zu-byte sound file", size); + return false; + } + return true; +} + +} // namespace + +static_assert(MT_MILES_MIX_RATE == kMixRate && kMixChannels == 2, "Miles mix format"); + NativeAudioEngine::NativeAudioEngine() { if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { SDL_AudioSpec spec{}; spec.format = SDL_AUDIO_F32; - spec.channels = 2; - spec.freq = 44100; + spec.channels = kMixChannels; + spec.freq = kMixRate; stream_ = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, nullptr, nullptr); if (stream_) SDL_ResumeAudioStreamDevice(stream_); + else + SDL_Log("audio: no playback device: %s", SDL_GetError()); } - // Always DrainAudioCommands() from cold boot so stale queued commands don't accumulate. - (void)DrainAudioCommands(); + // Without a device the Miles driver does not open, like 40250 with no sound card. + if (stream_) + MtMilesSetDecoder(decode_miles_file); + const char* trace = std::getenv("MT_AUDIO_TRACE"); + trace_ = trace && *trace == '1'; } NativeAudioEngine::~NativeAudioEngine() { + MtMilesSetDecoder(nullptr); if (stream_) SDL_DestroyAudioStream(stream_); SDL_QuitSubSystem(SDL_INIT_AUDIO); } void NativeAudioEngine::pump() { - const auto commands = DrainAudioCommands(); - for (const auto& cmd : commands) { - ++commands_drained_; - switch (cmd.type) { - case AudioCommand::PlaySound2D: { - const auto* clip = get_clip(cmd.filename); - if (clip && !clip->empty()) { - Voice v{}; - v.samples = clip; - v.volume = sound_volume_; - v.target_volume = sound_volume_; - v.filename = cmd.filename; - voices_.push_back(std::move(v)); - ++played_2d_; - } - break; - } - case AudioCommand::PlaySound3D: { - const auto* clip = get_clip(cmd.filename); - if (clip && !clip->empty()) { - Voice v{}; - v.samples = clip; - v.volume = sound_volume_ * std::clamp(cmd.volume, 0.0f, 1.0f); - v.target_volume = v.volume; - v.loop = cmd.play_count != 1; - v.is_3d = true; - v.handle = cmd.id; - v.filename = cmd.filename; - voices_.push_back(std::move(v)); - ++played_3d_; - } - break; - } - case AudioCommand::StopSound3D: - voices_.erase( - std::remove_if(voices_.begin(), voices_.end(), [&](const Voice& v) { - return v.is_3d && v.handle == cmd.id; - }), - voices_.end()); - break; - case AudioCommand::StopAllSound3D: - voices_.erase( - std::remove_if(voices_.begin(), voices_.end(), [](const Voice& v) { return v.is_3d; }), - voices_.end()); - break; - case AudioCommand::SetSoundVolume3D: - for (auto& v : voices_) { - if (v.is_3d && v.handle == cmd.id) { - v.volume = sound_volume_ * std::clamp(cmd.volume, 0.0f, 1.0f); - v.target_volume = v.volume; - } - } - break; - case AudioCommand::PlayMusic: - case AudioCommand::FadeInMusic: { - std::vector bytes; - if (cmd.filename.empty() || !read_live_pack_texture(cmd.filename, bytes)) break; - if (pending_music_.valid()) superseded_music_.push_back(std::move(pending_music_)); - // The pack is read here (CEterPackManager is not thread-safe); only the decode runs off-thread. - pending_music_ = std::async(std::launch::async, [bytes = std::move(bytes), name = cmd.filename]() -> Clip { - auto clip = std::make_shared>(); - if (!decode_audio(bytes.data(), bytes.size(), *clip)) - SDL_Log("audio: cannot decode %s (%zu bytes)", name.c_str(), bytes.size()); - return clip; - }); - pending_music_file_ = cmd.filename; - pending_music_fade_in_ = cmd.type == AudioCommand::FadeInMusic; - pending_music_volume_ = cmd.volume; - break; - } - case AudioCommand::FadeOutMusic: - case AudioCommand::FadeOutAllMusic: - pending_music_file_.clear(); // a track still decoding is dropped when it finishes - if (bgm_.samples) { - bgm_.target_volume = 0.0f; - bgm_.fade_step_per_frame = -std::max(bgm_.volume, 0.01f) / (44100.0f * 1.0f); - bgm_.stop_after_fade = true; - } - break; - case AudioCommand::FadeLimitOutMusic: - if (bgm_.samples) { - bgm_.target_volume = music_volume_ * std::clamp(cmd.volume, 0.0f, 1.0f); - bgm_.fade_step_per_frame = (bgm_.target_volume - bgm_.volume) / (44100.0f * 1.0f); - bgm_.stop_after_fade = false; - } - break; - case AudioCommand::SetMusicVolume: - music_volume_ = std::clamp(cmd.volume, 0.0f, 1.0f); - if (bgm_.samples && !bgm_.stop_after_fade) { - bgm_.volume = music_volume_; - bgm_.target_volume = music_volume_; - } - break; - case AudioCommand::SetSoundVolume: - sound_volume_ = std::clamp(cmd.volume, 0.0f, 1.0f); - break; - default: - break; - } - } - - if (pending_music_.valid() && - pending_music_.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - Clip clip = pending_music_.get(); - if (!pending_music_file_.empty() && clip && !clip->empty()) - start_music(std::move(clip), pending_music_file_, pending_music_fade_in_, pending_music_volume_); - pending_music_file_.clear(); - } - superseded_music_.erase( - std::remove_if(superseded_music_.begin(), superseded_music_.end(), [](const std::future& f) { - return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready; - }), - superseded_music_.end()); - if (!stream_) return; const int queued_bytes = SDL_GetAudioStreamAvailable(stream_); if (queued_bytes < 0) return; - const std::size_t queued_frames = static_cast(queued_bytes) / (2 * sizeof(float)); - constexpr std::size_t kTargetQueuedFrames = 4410; // ~100 ms stereo @ 44.1 kHz + const std::size_t queued_frames = static_cast(queued_bytes) / (kMixChannels * sizeof(float)); + constexpr std::size_t kTargetQueuedFrames = kMixRate / 10; // ~100 ms if (queued_frames >= kTargetQueuedFrames) return; const std::size_t frames_to_mix = kTargetQueuedFrames - queued_frames; - mix_buffer_.assign(frames_to_mix * 2, 0.0f); - - auto mix_voice = [&](Voice& v) -> bool { - if (!v.samples || v.samples->empty()) return false; - const std::size_t total_frames = v.samples->size() / 2; - if (total_frames == 0) return false; - const float* src = v.samples->data(); - for (std::size_t f = 0; f < frames_to_mix; ++f) { - if (v.frame_cursor >= total_frames) { - if (v.loop) v.frame_cursor = 0; - else return false; - } - if (v.fade_step_per_frame != 0.0f) { - v.volume += v.fade_step_per_frame; - if ((v.fade_step_per_frame > 0.0f && v.volume >= v.target_volume) || - (v.fade_step_per_frame < 0.0f && v.volume <= v.target_volume)) { - v.volume = v.target_volume; - v.fade_step_per_frame = 0.0f; - if (v.stop_after_fade && v.volume <= 0.0001f) { - v.samples = nullptr; - return false; - } - } - } - mix_buffer_[f * 2 + 0] += src[v.frame_cursor * 2 + 0] * v.volume; - mix_buffer_[f * 2 + 1] += src[v.frame_cursor * 2 + 1] * v.volume; - ++v.frame_cursor; - } - return v.loop || v.frame_cursor < total_frames; - }; - - if (bgm_.samples) { - if (!mix_voice(bgm_)) { - bgm_.samples = nullptr; - bgm_clip_.reset(); - } - } - for (auto it = voices_.begin(); it != voices_.end();) { - if (!mix_voice(*it)) it = voices_.erase(it); - else ++it; - } - for (float& sample : mix_buffer_) + mix_buffer_.assign(frames_to_mix * kMixChannels, 0.0f); + MtMilesMix(mix_buffer_.data(), frames_to_mix); + for (float& sample : mix_buffer_) { + if (trace_) trace_peak_ = std::max(trace_peak_, std::fabs(sample)); sample = std::clamp(sample, -1.0f, 1.0f); - SDL_PutAudioStreamData(stream_, mix_buffer_.data(), static_cast(mix_buffer_.size() * sizeof(float))); -} - -void NativeAudioEngine::start_music(Clip clip, const std::string& filename, bool fade_in, float volume) { - bgm_clip_ = std::move(clip); - bgm_.samples = bgm_clip_.get(); - bgm_.frame_cursor = 0; - bgm_.loop = true; - bgm_.filename = filename; - bgm_.stop_after_fade = false; - const float target = music_volume_ * std::clamp(volume, 0.0f, 1.0f); - bgm_.target_volume = target; - if (fade_in) { - bgm_.volume = 0.0f; - bgm_.fade_step_per_frame = target / (44100.0f * 1.5f); - } else { - bgm_.volume = target; - bgm_.fade_step_per_frame = 0.0f; } - ++played_music_; - SDL_Log("audio: music %s (%.0f s)", filename.c_str(), double(bgm_clip_->size() / 2) / kMixRate); -} - -const std::vector* NativeAudioEngine::get_clip(const std::string& vpath) { - if (vpath.empty()) return nullptr; - auto [it, inserted] = clips_.try_emplace(vpath); - if (!inserted) return &it->second; - std::vector bytes; - if (read_live_pack_texture(vpath, bytes) && !decode_audio(bytes.data(), bytes.size(), it->second)) - SDL_Log("audio: cannot decode %s (%zu bytes)", vpath.c_str(), bytes.size()); - return &it->second; + if (trace_ && (trace_frames_ += frames_to_mix) >= std::size_t(kMixRate) * 2) { + SDL_Log("audio: peak %.3f", trace_peak_); + trace_peak_ = 0.0f; + trace_frames_ = 0; + } + SDL_PutAudioStreamData(stream_, mix_buffer_.data(), static_cast(mix_buffer_.size() * sizeof(float))); } } // namespace mt_host diff --git a/src/host/native_audio.h b/src/host/native_audio.h index 40b3a51b..4a8f338f 100644 --- a/src/host/native_audio.h +++ b/src/host/native_audio.h @@ -3,63 +3,29 @@ #ifdef MT_NATIVE_HAS_LIVE_CLIENT #include -#include -#include -#include -#include -#include -#include #include namespace mt_host { -// Drains the MilesLib AudioCommand queue and mixes 2D/3D sounds and BGM into an SDL audio stream. +// The audio device behind the MilesLib port: installs the .wav/.mp3 decoder of the Miles emulation +// (platform/MilesLib/MilesMixer.h) and feeds its mix into an SDL audio stream. Constructed before +// CSoundManager::Create, which opens the Miles driver only when a decoder is installed. class NativeAudioEngine { - struct Voice { - const std::vector* samples = nullptr; - std::size_t frame_cursor = 0; - float volume = 1.0f; - float target_volume = 1.0f; - float fade_step_per_frame = 0.0f; - bool stop_after_fade = false; - bool loop = false; - bool is_3d = false; - int handle = 0; - std::string filename; - }; - public: NativeAudioEngine(); ~NativeAudioEngine(); + // Tops the device queue up to ~100 ms with MtMilesMix. void pump(); private: - using Clip = std::shared_ptr>; - - const std::vector* get_clip(const std::string& vpath); - void start_music(Clip clip, const std::string& filename, bool fade_in, float volume); - SDL_AudioStream* stream_ = nullptr; - std::unordered_map> clips_; - std::vector voices_; - Voice bgm_{}; - // BGM is not cached in clips_ (a 5-minute track is ~100 MB of float): only the playing track is kept, and - // a new one decodes on a worker thread while the old one keeps playing. - Clip bgm_clip_; - std::future pending_music_; - std::vector> superseded_music_; // a std::async future blocks in its destructor - std::string pending_music_file_; - bool pending_music_fade_in_ = false; - float pending_music_volume_ = 1.0f; std::vector mix_buffer_; - float sound_volume_ = 0.8f; - float music_volume_ = 0.5f; - std::size_t commands_drained_ = 0; - std::size_t played_2d_ = 0; - std::size_t played_3d_ = 0; - std::size_t played_music_ = 0; + // MT_AUDIO_TRACE=1: logs the peak level of the mix every 2 s. + bool trace_ = false; + float trace_peak_ = 0.0f; + std::size_t trace_frames_ = 0; }; } // namespace mt_host diff --git a/src/host/sdl_window.cpp b/src/host/sdl_window.cpp index f978bbaa..2c333380 100644 --- a/src/host/sdl_window.cpp +++ b/src/host/sdl_window.cpp @@ -252,6 +252,8 @@ bool VulkanWindow::poll(bool forward_to_live_client) { } #ifdef MT_NATIVE_HAS_LIVE_CLIENT if (forward_to_live_client) { + if (event.type == SDL_EVENT_WINDOW_FOCUS_GAINED || event.type == SDL_EVENT_WINDOW_FOCUS_LOST) + PythonBoot::UIActivate(event.type == SDL_EVENT_WINDOW_FOCUS_GAINED); if (!hardware_cursor_enabled_ && (event.type == SDL_EVENT_WINDOW_MOUSE_ENTER || event.type == SDL_EVENT_WINDOW_FOCUS_GAINED)) { std::printf(">>> SDL MOUSE ENTER/FOCUS GAINED\n"); diff --git a/src/platform/MilesLib/AudioCommands.cpp b/src/platform/MilesLib/AudioCommands.cpp deleted file mode 100644 index dc9bf4f5..00000000 --- a/src/platform/MilesLib/AudioCommands.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioCommands.h" - -#include - -namespace -{ -std::mutex g_audio_mutex; -std::vector g_audio_commands; -} - -void PushAudioCommand(const AudioCommand& cmd) -{ - std::lock_guard lock(g_audio_mutex); - g_audio_commands.push_back(cmd); -} - -std::vector DrainAudioCommands() -{ - std::lock_guard lock(g_audio_mutex); - std::vector out; - out.swap(g_audio_commands); - return out; -} diff --git a/src/platform/MilesLib/AudioCommands.h b/src/platform/MilesLib/AudioCommands.h deleted file mode 100644 index 764173d3..00000000 --- a/src/platform/MilesLib/AudioCommands.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include - -struct AudioCommand -{ - enum Type - { - PlaySound2D = 1, - PlaySound3D = 2, - StopSound3D = 3, - StopAllSound3D = 4, - PlayMusic = 5, - FadeOutMusic = 6, - FadeOutAllMusic = 7, - SetSoundVolume = 8, - SetMusicVolume = 9, - FadeInMusic = 10, - FadeLimitOutMusic = 11, - SetSoundVolume3D = 12, - }; - - Type type = PlaySound2D; - std::string filename; - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - float volume = 1.0f; - float speed = 0.0f; - int play_count = 1; - int id = 0; -}; - -void PushAudioCommand(const AudioCommand& cmd); -std::vector DrainAudioCommands(); diff --git a/src/platform/MilesLib/MilesAil.cpp b/src/platform/MilesLib/MilesAil.cpp new file mode 100644 index 00000000..e74aab6c --- /dev/null +++ b/src/platform/MilesLib/MilesAil.cpp @@ -0,0 +1,1029 @@ +// Platform implementation of the Miles Sound System calls 40250's MilesLib makes (shim ): a software +// mixer with the Miles handle model. Samples, 3D samples and streams are voices with Miles' status values +// (SMP_DONE/SMP_PLAYING/SMP_STOPPED), loop counts (0 = forever) and volume/pan; the host installs a decoder and +// pulls the mix (MilesMixer.h). What Miles did in its DLLs and this reproduces: +// - AIL_file_read goes through the callbacks AIL_set_file_callbacks installed (CSoundData::SetPackMode: +// the pack), else the disk; +// - AIL_decompress_ASI/ADPCM return a WAV image (32-bit float stereo, 44.1 kHz); +// - the one 3D provider is "Miles Fast 2D Positional Audio", the one 40250 opens: a left-handed space +// (MilesLib negates z), inverse-distance rolloff with the Miles default distances (min 1, max 200), a +// left/right balance from the source's direction relative to the listener's face/up vectors; +// - a stream decodes on a worker thread and is silent until then (Miles streamed from the file). +// Decoded sample data is cached per file image and dropped with it in AIL_mem_free_lock. +#include "MilesLib/Stdafx.h" +#include + +#include "MilesMixer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +using Clip = std::shared_ptr>; + +struct Voice +{ + Clip clip; + std::size_t cursor = 0; + S32 loop_count = 1; // 0 = forever + S32 loops_left = 1; + U32 status = SMP_DONE; +}; + +struct StreamDecode +{ + std::atomic done{false}; + Clip clip; +}; + +constexpr float kMin3DDistance = 1.0f; +constexpr float kMax3DDistance = 200.0f; +constexpr char kProviderName[] = "Miles Fast 2D Positional Audio"; +constexpr HPROVIDER kProvider = 1; +} + +struct _DIG_DRIVER +{ + U32 frequency = 0; +}; + +struct _SAMPLE +{ + Voice voice; + F32 volume = 1.0f; + F32 pan = 0.5f; +}; + +// H3DPOBJECT: a listener or a 3D sample. +struct MilesObject3D +{ + bool listener = false; + F32 position[3] = {0.0f, 0.0f, 0.0f}; + F32 face[3] = {0.0f, 0.0f, 1.0f}; + F32 up[3] = {0.0f, 1.0f, 0.0f}; + F32 velocity[3] = {0.0f, 0.0f, 0.0f}; + bool auto_update = false; + Voice voice; + F32 volume = 1.0f; +}; + +struct _STREAM +{ + Voice voice; + std::shared_ptr decode; + F32 left = 1.0f; + F32 right = 1.0f; +}; + +namespace +{ +std::mutex g_mutex; +MtMilesDecoder g_decoder = nullptr; +char g_last_error[256] = ""; + +AIL_file_open_callback g_open_cb = nullptr; +AIL_file_close_callback g_close_cb = nullptr; +AIL_file_seek_callback g_seek_cb = nullptr; +AIL_file_read_callback g_read_cb = nullptr; + +// Blocks handed out by AIL_file_read / AIL_decompress_*: start -> size. +std::map g_blocks; +// Decoded data of a file image (keyed by its first byte), inside one of g_blocks. +std::map g_decoded; + +std::vector<_SAMPLE*> g_samples; +std::vector g_objects; +std::vector<_STREAM*> g_streams; +MilesObject3D* g_listener = nullptr; +bool g_provider_open = false; + +void set_error(const char* message, const char* detail = nullptr) +{ + std::snprintf(g_last_error, sizeof(g_last_error), "%s%s%s", message, detail ? ": " : "", detail ? detail : ""); +} + +void start_voice(Voice& voice) +{ + voice.cursor = 0; + voice.loops_left = voice.loop_count; + voice.status = SMP_PLAYING; +} + +void mix_voice(Voice& voice, float* out, std::size_t frames, float left_gain, float right_gain) +{ + if (voice.status != SMP_PLAYING || !voice.clip) + return; + + const std::size_t total = voice.clip->size() / 2; + if (total == 0) + { + voice.status = SMP_DONE; + return; + } + + const float* src = voice.clip->data(); + for (std::size_t f = 0; f < frames; ++f) + { + if (voice.cursor >= total) + { + if (voice.loop_count != 0 && --voice.loops_left <= 0) + { + voice.status = SMP_DONE; + return; + } + voice.cursor = 0; + } + out[f * 2 + 0] += src[voice.cursor * 2 + 0] * left_gain; + out[f * 2 + 1] += src[voice.cursor * 2 + 1] * right_gain; + ++voice.cursor; + } + + if (voice.cursor >= total && voice.loop_count != 0 && voice.loops_left <= 1) + voice.status = SMP_DONE; +} + +U8* new_block(std::size_t size) +{ + U8* block = new U8[size ? size : 1]; + g_blocks[block] = size; + return block; +} + +// The size of the file image starting at `image`, when it lies in one of g_blocks. +std::size_t image_size(const void* image) +{ + const U8* p = static_cast(image); + auto it = g_blocks.upper_bound(p); + if (it == g_blocks.begin()) + return 0; + --it; + if (p < it->first || p >= it->first + it->second) + return 0; + return static_cast(it->first + it->second - p); +} + +Clip decode_image(const void* image, std::size_t size_hint) +{ + if (!image) + { + set_error("NULL file image"); + return nullptr; + } + + auto cached = g_decoded.find(image); + if (cached != g_decoded.end()) + return cached->second; + + const std::size_t block_size = image_size(image); + const std::size_t size = size_hint ? size_hint : block_size; + if (!g_decoder || size == 0) + { + set_error("Invalid file image"); + return nullptr; + } + + auto clip = std::make_shared>(); + if (!g_decoder(image, size, *clip) || clip->empty()) + { + set_error("Unsupported or damaged file image"); + return nullptr; + } + + // Only images inside our blocks are cached: AIL_mem_free_lock is what invalidates them. + if (block_size) + g_decoded[image] = clip; + return clip; +} + +bool read_file(const char* filename, std::vector& bytes) +{ + bytes.clear(); + if (!filename) + return false; + + if (g_open_cb) + { + U32 handle = 0; + if (!g_open_cb(filename, &handle)) + { + set_error("File not found", filename); + return false; + } + const S32 size = g_seek_cb ? g_seek_cb(handle, 0, AIL_FILE_SEEK_END) : 0; + if (g_seek_cb) + g_seek_cb(handle, 0, AIL_FILE_SEEK_BEGIN); + if (size > 0) + { + bytes.resize(static_cast(size)); + const U32 read = g_read_cb ? g_read_cb(handle, bytes.data(), static_cast(size)) : 0; + bytes.resize(read); + } + if (g_close_cb) + g_close_cb(handle); + } + else + { + FILE* file = std::fopen(filename, "rb"); + if (!file) + { + set_error("File not found", filename); + return false; + } + std::fseek(file, 0, SEEK_END); + const long size = std::ftell(file); + std::fseek(file, 0, SEEK_SET); + if (size > 0) + { + bytes.resize(static_cast(size)); + bytes.resize(std::fread(bytes.data(), 1, bytes.size(), file)); + } + std::fclose(file); + } + + if (bytes.empty()) + { + set_error("Empty file", filename); + return false; + } + return true; +} + +U32 read_u32(const U8* p) +{ + return U32(p[0]) | (U32(p[1]) << 8) | (U32(p[2]) << 16) | (U32(p[3]) << 24); +} + +U16 read_u16(const U8* p) +{ + return U16(p[0] | (p[1] << 8)); +} + +void write_u32(U8* p, U32 v) +{ + p[0] = U8(v); + p[1] = U8(v >> 8); + p[2] = U8(v >> 16); + p[3] = U8(v >> 24); +} + +void write_u16(U8* p, U16 v) +{ + p[0] = U8(v); + p[1] = U8(v >> 8); +} + +// Finds the "fmt " and "data" chunks of a RIFF/WAVE image. +bool parse_wav(const U8* data, std::size_t size, const U8** fmt, U32* fmt_size, const U8** pcm, U32* pcm_size) +{ + if (size < 12 || std::memcmp(data, "RIFF", 4) != 0 || std::memcmp(data + 8, "WAVE", 4) != 0) + return false; + + *fmt = nullptr; + *pcm = nullptr; + std::size_t pos = 12; + while (pos + 8 <= size) + { + const U32 chunk_size = read_u32(data + pos + 4); + const std::size_t body = pos + 8; + const std::size_t available = std::min(chunk_size, size - body); + if (std::memcmp(data + pos, "fmt ", 4) == 0 && available >= 16) + { + *fmt = data + body; + *fmt_size = U32(available); + } + else if (std::memcmp(data + pos, "data", 4) == 0) + { + *pcm = data + body; + *pcm_size = U32(available); + } + pos = body + chunk_size + (chunk_size & 1); + } + return *fmt != nullptr && *pcm != nullptr; +} + +bool is_mp3(const U8* data, std::size_t size) +{ + if (size >= 3 && std::memcmp(data, "ID3", 3) == 0) + return true; + // Frame sync, MPEG layer III. + return size >= 2 && data[0] == 0xFF && (data[1] & 0xE0) == 0xE0 && ((data[1] >> 1) & 3) == 1; +} + +// A 32-bit float stereo 44.1 kHz WAV image of `clip`, with the clip cached as its decoded data. +void* new_wav_image(const Clip& clip, U32* out_size) +{ + const U32 data_size = U32(clip->size() * sizeof(float)); + const U32 size = 44 + data_size; + U8* image = new_block(size); + std::memcpy(image, "RIFF", 4); + write_u32(image + 4, size - 8); + std::memcpy(image + 8, "WAVEfmt ", 8); + write_u32(image + 16, 16); + write_u16(image + 20, 3); // WAVE_FORMAT_IEEE_FLOAT + write_u16(image + 22, 2); + write_u32(image + 24, MT_MILES_MIX_RATE); + write_u32(image + 28, MT_MILES_MIX_RATE * 2 * sizeof(float)); + write_u16(image + 32, 2 * sizeof(float)); + write_u16(image + 34, 32); + std::memcpy(image + 36, "data", 4); + write_u32(image + 40, data_size); + std::memcpy(image + 44, clip->data(), data_size); + g_decoded[image] = clip; + *out_size = size; + return image; +} + +S32 decompress(const void* file, std::size_t size, void** outdata, U32* outsize) +{ + *outdata = nullptr; + *outsize = 0; + if (!g_decoder || !file || size == 0) + { + set_error("Cannot decompress"); + return 0; + } + auto clip = std::make_shared>(); + if (!g_decoder(file, size, *clip) || clip->empty()) + { + set_error("Cannot decompress: unsupported or damaged data"); + return 0; + } + *outdata = new_wav_image(clip, outsize); + return 1; +} + +MilesObject3D* object3d(H3DPOBJECT obj) +{ + return static_cast(obj); +} + +void normalize(F32 v[3]) +{ + const F32 length = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + if (length > 1e-6f) + { + v[0] /= length; + v[1] /= length; + v[2] /= length; + } +} + +// Left/right gains of a balance `pan` in [-1, 1] (0 = centre, both channels at full volume). +void balance(float volume, float pan, float* left, float* right) +{ + pan = std::clamp(pan, -1.0f, 1.0f); + *left = volume * std::min(1.0f, 1.0f - pan); + *right = volume * std::min(1.0f, 1.0f + pan); +} + +void gains_3d(const MilesObject3D& sample, float* left, float* right) +{ + static const MilesObject3D s_default_listener; + const MilesObject3D& listener = g_listener ? *g_listener : s_default_listener; + + F32 rel[3] = {sample.position[0] - listener.position[0], sample.position[1] - listener.position[1], + sample.position[2] - listener.position[2]}; + const F32 distance = std::sqrt(rel[0] * rel[0] + rel[1] * rel[1] + rel[2] * rel[2]); + + float gain = 1.0f; + if (distance >= kMax3DDistance) + gain = 0.0f; + else if (distance > kMin3DDistance) + gain = kMin3DDistance / distance; + + // Left-handed: right = up x face. + F32 right_dir[3] = {listener.up[1] * listener.face[2] - listener.up[2] * listener.face[1], + listener.up[2] * listener.face[0] - listener.up[0] * listener.face[2], + listener.up[0] * listener.face[1] - listener.up[1] * listener.face[0]}; + normalize(right_dir); + float pan = 0.0f; + if (distance > 1e-6f) + pan = (rel[0] * right_dir[0] + rel[1] * right_dir[1] + rel[2] * right_dir[2]) / distance; + + balance(std::clamp(sample.volume, 0.0f, 1.0f) * gain, pan, left, right); +} +} // namespace + +void MtMilesSetDecoder(MtMilesDecoder decoder) +{ + std::lock_guard lock(g_mutex); + g_decoder = decoder; +} + +void MtMilesMix(float* out, std::size_t frames) +{ + std::lock_guard lock(g_mutex); + + for (_SAMPLE* sample : g_samples) + { + float left, right; + balance(sample->volume, sample->pan * 2.0f - 1.0f, &left, &right); + mix_voice(sample->voice, out, frames, left, right); + } + + for (MilesObject3D* object : g_objects) + { + if (object->listener || object->voice.status != SMP_PLAYING) + continue; + float left, right; + gains_3d(*object, &left, &right); + mix_voice(object->voice, out, frames, left, right); + } + + for (_STREAM* stream : g_streams) + { + if (!stream->voice.clip && stream->decode && stream->decode->done.load(std::memory_order_acquire)) + { + stream->voice.clip = stream->decode->clip; + stream->decode.reset(); + if (!stream->voice.clip && stream->voice.status == SMP_PLAYING) + stream->voice.status = SMP_DONE; + } + mix_voice(stream->voice, out, frames, stream->left, stream->right); + } +} + +// System + +S32 AIL_startup() +{ + return 1; +} + +void AIL_shutdown() +{ +} + +char* AIL_set_redist_directory(char const*) +{ + return g_last_error; +} + +char* AIL_last_error() +{ + return g_last_error; +} + +HDIGDRIVER AIL_open_digital_driver(U32 frequency, S32, S32, U32) +{ + std::lock_guard lock(g_mutex); + // No decoder means the host has no audio device. + if (!g_decoder) + { + set_error("No audio device"); + return nullptr; + } + HDIGDRIVER driver = new _DIG_DRIVER; + driver->frequency = frequency; + return driver; +} + +void AIL_close_digital_driver(HDIGDRIVER dig) +{ + delete dig; +} + +// Files and memory + +void AIL_set_file_callbacks(AIL_file_open_callback opencb, AIL_file_close_callback closecb, + AIL_file_seek_callback seekcb, AIL_file_read_callback readcb) +{ + std::lock_guard lock(g_mutex); + g_open_cb = opencb; + g_close_cb = closecb; + g_seek_cb = seekcb; + g_read_cb = readcb; +} + +void* AIL_file_read(char const* filename, void* dest) +{ + std::lock_guard lock(g_mutex); + std::vector bytes; + if (!read_file(filename, bytes)) + return nullptr; + + if (dest == FILE_READ_WITH_SIZE) + { + U8* block = new_block(4 + bytes.size()); + write_u32(block, U32(bytes.size())); + std::memcpy(block + 4, bytes.data(), bytes.size()); + return block; + } + if (dest) + { + std::memcpy(dest, bytes.data(), bytes.size()); + return dest; + } + U8* block = new_block(bytes.size()); + std::memcpy(block, bytes.data(), bytes.size()); + return block; +} + +S32 AIL_file_type(void const* data, U32 size) +{ + const U8* bytes = static_cast(data); + if (!bytes) + return AILFILETYPE_UNKNOWN; + + const U8* fmt; + const U8* pcm; + U32 fmt_size, pcm_size; + if (parse_wav(bytes, size, &fmt, &fmt_size, &pcm, &pcm_size)) + { + switch (read_u16(fmt)) + { + case 0x0001: + return AILFILETYPE_PCM_WAV; + case 0x0002: // MS ADPCM + case 0x0011: // IMA ADPCM + return AILFILETYPE_ADPCM_WAV; + case 0x0055: // MPEG layer III in a WAV + return AILFILETYPE_MPEG_L3_AUDIO; + default: + return AILFILETYPE_OTHER_WAV; + } + } + if (is_mp3(bytes, size)) + return AILFILETYPE_MPEG_L3_AUDIO; + return AILFILETYPE_UNKNOWN; +} + +S32 AIL_WAV_info(void const* data, AILSOUNDINFO* info) +{ + std::lock_guard lock(g_mutex); + const U8* bytes = static_cast(data); + const std::size_t size = image_size(data); + const U8* fmt; + const U8* pcm; + U32 fmt_size, pcm_size; + if (!info || !bytes || !parse_wav(bytes, size, &fmt, &fmt_size, &pcm, &pcm_size)) + { + set_error("Not a WAV file"); + return 0; + } + info->format = read_u16(fmt); + info->channels = read_u16(fmt + 2); + info->rate = read_u32(fmt + 4); + info->block_size = read_u16(fmt + 12); + info->bits = read_u16(fmt + 14); + info->data_ptr = pcm; + info->data_len = pcm_size; + info->samples = info->block_size ? pcm_size / info->block_size : 0; + info->initial_ptr = data; + return 1; +} + +S32 AIL_decompress_ADPCM(AILSOUNDINFO const* info, void** outdata, U32* outsize) +{ + std::lock_guard lock(g_mutex); + const void* file = info ? info->initial_ptr : nullptr; + return decompress(file, file ? image_size(file) : 0, outdata, outsize); +} + +S32 AIL_decompress_ASI(void const* indata, U32 insize, char const*, void** outdata, U32* outsize, AILLENGTHYCB) +{ + std::lock_guard lock(g_mutex); + return decompress(indata, insize, outdata, outsize); +} + +void AIL_mem_free_lock(void* ptr) +{ + std::lock_guard lock(g_mutex); + auto it = g_blocks.find(static_cast(ptr)); + if (it == g_blocks.end()) + return; + const U8* begin = it->first; + const U8* end = begin + it->second; + for (auto d = g_decoded.lower_bound(begin); d != g_decoded.end() && std::less()(d->first, end);) + d = g_decoded.erase(d); + delete[] it->first; + g_blocks.erase(it); +} + +// 2D samples + +HSAMPLE AIL_allocate_sample_handle(HDIGDRIVER dig) +{ + if (!dig) + return nullptr; + std::lock_guard lock(g_mutex); + HSAMPLE sample = new _SAMPLE; + g_samples.push_back(sample); + return sample; +} + +void AIL_release_sample_handle(HSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + g_samples.erase(std::remove(g_samples.begin(), g_samples.end(), s), g_samples.end()); + delete s; +} + +void AIL_init_sample(HSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + s->voice = Voice{}; + s->volume = 1.0f; + s->pan = 0.5f; +} + +S32 AIL_set_sample_file(HSAMPLE s, void const* file_image, S32 block) +{ + if (!s) + return 0; + std::lock_guard lock(g_mutex); + Clip clip = decode_image(file_image, block > 0 ? std::size_t(block) : 0); + if (!clip) + return 0; + s->voice.clip = std::move(clip); + s->voice.cursor = 0; + s->voice.status = SMP_DONE; + return 1; +} + +// A NULL handle (no audio device, or after CSoundInstance2D::Stop) is never SMP_DONE, so never reused. +U32 AIL_sample_status(HSAMPLE s) +{ + if (!s) + return SMP_FREE; + std::lock_guard lock(g_mutex); + return s->voice.status; +} + +void AIL_set_sample_loop_count(HSAMPLE s, S32 loop_count) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + s->voice.loop_count = loop_count; +} + +void AIL_start_sample(HSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + if (s->voice.clip) + start_voice(s->voice); +} + +void AIL_stop_sample(HSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + if (s->voice.status == SMP_PLAYING) + s->voice.status = SMP_STOPPED; +} + +void AIL_resume_sample(HSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + if (s->voice.status == SMP_STOPPED) + s->voice.status = SMP_PLAYING; +} + +void AIL_end_sample(HSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + s->voice.status = SMP_DONE; +} + +void AIL_sample_volume_pan(HSAMPLE s, F32* volume, F32* pan) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + if (volume) + *volume = s->volume; + if (pan) + *pan = s->pan; +} + +void AIL_set_sample_volume_pan(HSAMPLE s, F32 volume, F32 pan) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + s->volume = std::clamp(volume, 0.0f, 1.0f); + s->pan = std::clamp(pan, 0.0f, 1.0f); +} + +// 3D + +S32 AIL_enumerate_3D_providers(HPROENUM* next, HPROVIDER* dest, char** name) +{ + if (!next || *next != HPROENUM_FIRST) + return 0; + *next = 1; + if (dest) + *dest = kProvider; + if (name) + *name = const_cast(kProviderName); + return 1; +} + +M3DRESULT AIL_open_3D_provider(HPROVIDER lib) +{ + std::lock_guard lock(g_mutex); + if (lib != kProvider || !g_decoder) + { + set_error("No audio device"); + return 1; + } + g_provider_open = true; + return M3D_NOERR; +} + +void AIL_close_3D_provider(HPROVIDER) +{ + std::lock_guard lock(g_mutex); + g_provider_open = false; +} + +H3DPOBJECT AIL_open_3D_listener(HPROVIDER lib) +{ + std::lock_guard lock(g_mutex); + if (lib != kProvider || !g_provider_open) + return nullptr; + MilesObject3D* listener = new MilesObject3D; + listener->listener = true; + g_objects.push_back(listener); + g_listener = listener; + return listener; +} + +void AIL_close_3D_listener(H3DPOBJECT listener) +{ + if (!listener) + return; + std::lock_guard lock(g_mutex); + g_objects.erase(std::remove(g_objects.begin(), g_objects.end(), object3d(listener)), g_objects.end()); + if (g_listener == listener) + g_listener = nullptr; + delete object3d(listener); +} + +H3DSAMPLE AIL_allocate_3D_sample_handle(HPROVIDER lib) +{ + std::lock_guard lock(g_mutex); + if (lib != kProvider || !g_provider_open) + return nullptr; + MilesObject3D* sample = new MilesObject3D; + g_objects.push_back(sample); + return sample; +} + +void AIL_release_3D_sample_handle(H3DSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + g_objects.erase(std::remove(g_objects.begin(), g_objects.end(), object3d(s)), g_objects.end()); + delete object3d(s); +} + +S32 AIL_set_3D_sample_file(H3DSAMPLE s, void const* file_image) +{ + if (!s) + return 0; + std::lock_guard lock(g_mutex); + Clip clip = decode_image(file_image, 0); + if (!clip) + return 0; + MilesObject3D* sample = object3d(s); + sample->voice.clip = std::move(clip); + sample->voice.cursor = 0; + sample->voice.status = SMP_DONE; + return 1; +} + +U32 AIL_3D_sample_status(H3DSAMPLE s) +{ + if (!s) + return SMP_FREE; + std::lock_guard lock(g_mutex); + return object3d(s)->voice.status; +} + +void AIL_set_3D_sample_loop_count(H3DSAMPLE s, U32 loops) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + object3d(s)->voice.loop_count = S32(loops); +} + +void AIL_start_3D_sample(H3DSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + if (object3d(s)->voice.clip) + start_voice(object3d(s)->voice); +} + +void AIL_stop_3D_sample(H3DSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + if (object3d(s)->voice.status == SMP_PLAYING) + object3d(s)->voice.status = SMP_STOPPED; +} + +void AIL_resume_3D_sample(H3DSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + if (object3d(s)->voice.status == SMP_STOPPED) + object3d(s)->voice.status = SMP_PLAYING; +} + +void AIL_end_3D_sample(H3DSAMPLE s) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + object3d(s)->voice.status = SMP_DONE; +} + +F32 AIL_3D_sample_volume(H3DSAMPLE s) +{ + if (!s) + return 0.0f; + std::lock_guard lock(g_mutex); + return object3d(s)->volume; +} + +void AIL_set_3D_sample_volume(H3DSAMPLE s, F32 volume) +{ + if (!s) + return; + std::lock_guard lock(g_mutex); + object3d(s)->volume = std::clamp(volume, 0.0f, 1.0f); +} + +void AIL_set_3D_position(H3DPOBJECT obj, F32 X, F32 Y, F32 Z) +{ + if (!obj) + return; + std::lock_guard lock(g_mutex); + F32* position = object3d(obj)->position; + position[0] = X; + position[1] = Y; + position[2] = Z; +} + +void AIL_set_3D_orientation(H3DPOBJECT obj, F32 X_face, F32 Y_face, F32 Z_face, F32 X_up, F32 Y_up, F32 Z_up) +{ + if (!obj) + return; + std::lock_guard lock(g_mutex); + MilesObject3D* object = object3d(obj); + object->face[0] = X_face; + object->face[1] = Y_face; + object->face[2] = Z_face; + object->up[0] = X_up; + object->up[1] = Y_up; + object->up[2] = Z_up; +} + +void AIL_set_3D_velocity(H3DPOBJECT obj, F32 dx_per_ms, F32 dy_per_ms, F32 dz_per_ms, F32 magnitude) +{ + if (!obj) + return; + std::lock_guard lock(g_mutex); + F32* velocity = object3d(obj)->velocity; + velocity[0] = dx_per_ms * magnitude; + velocity[1] = dy_per_ms * magnitude; + velocity[2] = dz_per_ms * magnitude; +} + +void AIL_auto_update_3D_position(H3DPOBJECT obj, S32 enable) +{ + if (!obj) + return; + std::lock_guard lock(g_mutex); + object3d(obj)->auto_update = enable != 0; +} + +void AIL_update_3D_position(H3DPOBJECT obj, F32 dt_milliseconds) +{ + if (!obj) + return; + std::lock_guard lock(g_mutex); + MilesObject3D* object = object3d(obj); + for (int i = 0; i < 3; ++i) + object->position[i] += object->velocity[i] * dt_milliseconds; +} + +// Streams + +HSTREAM AIL_open_stream(HDIGDRIVER dig, char const* filename, S32) +{ + if (!dig) + return nullptr; + std::lock_guard lock(g_mutex); + std::vector bytes; + if (!g_decoder || !read_file(filename, bytes)) + return nullptr; + + HSTREAM stream = new _STREAM; + stream->decode = std::make_shared(); + // The pack is read above, on the calling thread; only the decode runs on the worker. + std::thread([decode = stream->decode, bytes = std::move(bytes), decoder = g_decoder]() { + auto clip = std::make_shared>(); + if (decoder(bytes.data(), bytes.size(), *clip) && !clip->empty()) + decode->clip = std::move(clip); + decode->done.store(true, std::memory_order_release); + }).detach(); + g_streams.push_back(stream); + return stream; +} + +void AIL_close_stream(HSTREAM stream) +{ + if (!stream) + return; + std::lock_guard lock(g_mutex); + g_streams.erase(std::remove(g_streams.begin(), g_streams.end(), stream), g_streams.end()); + delete stream; +} + +S32 AIL_stream_status(HSTREAM stream) +{ + if (!stream) + return -1; + std::lock_guard lock(g_mutex); + return S32(stream->voice.status); +} + +void AIL_set_stream_loop_count(HSTREAM stream, S32 count) +{ + if (!stream) + return; + std::lock_guard lock(g_mutex); + stream->voice.loop_count = count; +} + +void AIL_start_stream(HSTREAM stream) +{ + if (!stream) + return; + std::lock_guard lock(g_mutex); + // Still decoding: plays from the start once the data is there. + stream->voice.cursor = 0; + stream->voice.loops_left = stream->voice.loop_count; + stream->voice.status = SMP_PLAYING; +} + +void AIL_pause_stream(HSTREAM stream, S32 onoff) +{ + if (!stream) + return; + std::lock_guard lock(g_mutex); + if (onoff && stream->voice.status == SMP_PLAYING) + stream->voice.status = SMP_STOPPED; + else if (!onoff && stream->voice.status == SMP_STOPPED) + stream->voice.status = SMP_PLAYING; +} + +void AIL_stream_volume_levels(HSTREAM stream, F32* left_level, F32* right_level) +{ + if (!stream) + return; + std::lock_guard lock(g_mutex); + if (left_level) + *left_level = stream->left; + if (right_level) + *right_level = stream->right; +} + +void AIL_set_stream_volume_levels(HSTREAM stream, F32 left_level, F32 right_level) +{ + if (!stream) + return; + std::lock_guard lock(g_mutex); + stream->left = std::clamp(left_level, 0.0f, 1.0f); + stream->right = std::clamp(right_level, 0.0f, 1.0f); +} diff --git a/src/platform/MilesLib/MilesMixer.h b/src/platform/MilesLib/MilesMixer.h new file mode 100644 index 00000000..6bcd610a --- /dev/null +++ b/src/platform/MilesLib/MilesMixer.h @@ -0,0 +1,19 @@ +#pragma once +// Host side of the Miles emulation (MilesAil.cpp). The AIL_* calls MilesLib makes keep sample, 3D sample +// and stream voices; the host decodes files and pulls the mixed output. +// +// Output format: interleaved stereo float at MT_MILES_MIX_RATE. + +#include +#include + +constexpr int MT_MILES_MIX_RATE = 44100; + +// Decodes a whole .wav/.mp3 file image into interleaved stereo float at MT_MILES_MIX_RATE. Called on the +// script thread for samples and on a worker thread for streams, so it must be thread-safe. +typedef bool (*MtMilesDecoder)(const void* data, std::size_t size, std::vector& out); +void MtMilesSetDecoder(MtMilesDecoder decoder); + +// Adds `frames` frames of every playing voice into `out` (2 * frames floats, not cleared or clamped) and +// advances them; a voice that runs out of loops becomes SMP_DONE. +void MtMilesMix(float* out, std::size_t frames); diff --git a/src/platform/MilesLib/SoundBase.cpp b/src/platform/MilesLib/SoundBase.cpp index e223f242..dbc8e64e 100644 --- a/src/platform/MilesLib/SoundBase.cpp +++ b/src/platform/MilesLib/SoundBase.cpp @@ -1,50 +1,70 @@ -// Platform skeleton for MilesLib/SoundBase.h (40250 MilesLib/SoundBase.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. #include "MilesLib/Stdafx.h" #include "MilesLib/SoundBase.h" -#include "../PlatformStub.h" +HDIGDRIVER CSoundBase::ms_DIGDriver = NULL; +TSoundDataMap CSoundBase::ms_dataMap; +TProvider * CSoundBase::ms_pProviderDefault = NULL; +std::vector CSoundBase::ms_ProviderVector; +bool CSoundBase::ms_bInitialized = false; +int CSoundBase::ms_iRefCount = 0; CSoundBase::CSoundBase() { - MT_PLATFORM_STUB(); } CSoundBase::~CSoundBase() { - MT_PLATFORM_STUB(); } -auto CSoundBase::Initialize() -> void +void CSoundBase::Destroy() { - MT_PLATFORM_STUB(); + if (ms_iRefCount > 1) + { + --ms_iRefCount; + return; + } + + ms_iRefCount = 0; + + if (!ms_dataMap.empty()) + { + TSoundDataMap::iterator i; + for (i = ms_dataMap.begin(); i != ms_dataMap.end(); ++i) + { + CSoundData * pSoundData = i->second; + delete pSoundData; + } + + ms_dataMap.clear(); + } + + AIL_shutdown(); } -auto CSoundBase::Destroy() -> void +void CSoundBase::Initialize() { - MT_PLATFORM_STUB(); + ++ms_iRefCount; + + if (ms_iRefCount > 1) + return; + + AIL_set_redist_directory("miles"); + AIL_startup(); + + ms_ProviderVector.clear(); + ms_dataMap.clear(); + } -auto CSoundBase::AddFile(DWORD, const char *) -> CSoundData * +DWORD CSoundBase::GetFileCRC(const char * filename) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + return GetCRC32(filename, strlen(filename)); } -auto CSoundBase::GetFileCRC(const char *) -> DWORD +CSoundData * CSoundBase::AddFile(DWORD dwFileCRC, const char* filename) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + CSoundData * pSoundData = new CSoundData; + pSoundData->Assign(filename); + ms_dataMap.insert(TSoundDataMap::value_type(dwFileCRC, pSoundData)); + return pSoundData; } - -decltype(CSoundBase::ms_iRefCount) CSoundBase::ms_iRefCount{}; - -decltype(CSoundBase::ms_DIGDriver) CSoundBase::ms_DIGDriver{}; - -decltype(CSoundBase::ms_pProviderDefault) CSoundBase::ms_pProviderDefault{}; - -decltype(CSoundBase::ms_ProviderVector) CSoundBase::ms_ProviderVector{}; - -decltype(CSoundBase::ms_dataMap) CSoundBase::ms_dataMap{}; - -decltype(CSoundBase::ms_bInitialized) CSoundBase::ms_bInitialized{}; diff --git a/src/platform/MilesLib/SoundData.cpp b/src/platform/MilesLib/SoundData.cpp index 3c852ca5..2ef08e22 100644 --- a/src/platform/MilesLib/SoundData.cpp +++ b/src/platform/MilesLib/SoundData.cpp @@ -1,116 +1,211 @@ -// Platform skeleton for MilesLib/SoundData.h (40250 MilesLib/SoundData.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. #include "MilesLib/Stdafx.h" #include "MilesLib/SoundData.h" -#include "../PlatformStub.h" +#include "EterPack/EterPackManager.h" +#include "EterBase/Timer.h" -auto CSoundData::SetPackMode() -> void +bool CSoundData::ms_isSoundFile[SOUND_FILE_MAX_NUM]; +CMappedFile CSoundData::ms_SoundFile[SOUND_FILE_MAX_NUM]; + +const char * CSoundData::GetFileName() { - MT_PLATFORM_STUB(); + return m_filename; } -CSoundData::CSoundData() +void CSoundData::Assign(const char* filename) +{ + assert(m_assigned == false); + + strncpy(m_filename, filename, sizeof(m_filename)-1); + m_assigned = true; +} + +LPVOID CSoundData::Get() +{ + ++m_iRefCount; + m_dwAccessTime = ELTimer_GetMSec(); + + if (!m_data) + ReadFromDisk(); + + if (m_flag & FLAG_DATA_SIZE) + return ((S32 *) m_data + 1); + else + return (m_data); +} + +ULONG CSoundData::GetSize() +{ + return m_size; +} + +void CSoundData::Release() +{ + assert(m_iRefCount != 0); + --m_iRefCount; + m_dwAccessTime = ELTimer_GetMSec(); +} + +DWORD CSoundData::GetPlayTime() +{ + return m_dwPlayTime; +} + +void CSoundData::SetPlayTime(DWORD dwPlayTime) +{ + m_dwPlayTime = dwPlayTime; +} + +DWORD CSoundData::GetAccessTime() +{ + return m_dwAccessTime; +} + +bool CSoundData::ReadFromDisk() +{ + assert(m_assigned == true); + + // PORT: 40250 read QueryPerformanceCounter into an unused local here; dropped. + + U32* s = (U32 *) AIL_file_read(m_filename, FILE_READ_WITH_SIZE); + + if (s == NULL) + return false; + + S32 type = AIL_file_type(s + 1, s[0]); + AILSOUNDINFO info; + + switch (type) + { + case AILFILETYPE_PCM_WAV: + { + m_data = s; + m_size = *((S32 *) s); + m_flag |= FLAG_DATA_SIZE; + } + break; + + case AILFILETYPE_ADPCM_WAV: // 3D 사운드는 decompress 해야 함. + { + AIL_WAV_info(s + 1, &info); + AIL_decompress_ADPCM(&info, &m_data, &m_size); + AIL_mem_free_lock(s); + } + break; + + case AILFILETYPE_MPEG_L3_AUDIO: + { + AIL_decompress_ASI(s + 1, *((S32 *) s), m_filename, &m_data, &m_size, 0); + AIL_mem_free_lock(s); + } + break; + + default: + assert(!"Unknown File Type"); + AIL_mem_free_lock(s); + return false; + } + + return true; +} + +bool CSoundData::isSlotIndex(DWORD dwIndex) +{ + if (dwIndex >= SOUND_FILE_MAX_NUM) + return false; + + if (!ms_isSoundFile[dwIndex]) + return false; + + return true; +} + +int CSoundData::GetEmptySlotIndex() +{ + for (int i = 0; i < SOUND_FILE_MAX_NUM; ++i) + { + if (!ms_isSoundFile[i]) + return i; + } + + return -1; +} + +U32 AILCALLBACK CSoundData::open_callback(char const * filename, U32 *file_handle) +{ + int iIndex = GetEmptySlotIndex(); + + if (-1 == iIndex) + return 0; + + LPCVOID pMap; + + if (!CEterPackManager::Instance().Get(ms_SoundFile[iIndex], filename, &pMap)) + return 0; + + ms_isSoundFile[iIndex] = true; + + *file_handle = iIndex; + return 1; +} + +void AILCALLBACK CSoundData::close_callback(U32 file_handle) +{ + if (!isSlotIndex(file_handle)) + return; + + ms_SoundFile[file_handle].Destroy(); + ms_isSoundFile[file_handle] = false; +} + +S32 AILCALLBACK CSoundData::seek_callback(U32 file_handle, S32 offset, U32 type) +{ + if (!isSlotIndex(file_handle)) + return 0; + + return ms_SoundFile[file_handle].Seek(offset, type); +} + +U32 AILCALLBACK CSoundData::read_callback(U32 file_handle, void * buffer, U32 bytes) +{ + if (!isSlotIndex(file_handle)) + return 0; + + DWORD dwRealSize = min(ms_SoundFile[file_handle].Size(), bytes); + ms_SoundFile[file_handle].Read(buffer, dwRealSize); + return dwRealSize; +} + +void CSoundData::SetPackMode() +{ + // if sound data reads from pack file, the callbacks of the MSS should be changed. + AIL_set_file_callbacks(open_callback, close_callback, seek_callback, read_callback); + + for (int i = 0; i < SOUND_FILE_MAX_NUM; ++i) + ms_isSoundFile[i] = false; +} + +void CSoundData::Destroy() +{ + if (m_data) + { + AIL_mem_free_lock(m_data); + m_data = NULL; + } +} + +CSoundData::CSoundData() : +m_assigned(false), +m_iRefCount(0), +m_dwPlayTime(0), +m_dwAccessTime(ELTimer_GetMSec()), +m_size(0), +m_data(NULL), +m_flag(0) { - MT_PLATFORM_STUB(); } CSoundData::~CSoundData() { - MT_PLATFORM_STUB(); + Destroy(); } - -auto CSoundData::Assign(const char *) -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundData::Get() -> LPVOID -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::GetSize() -> ULONG -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::Release() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundData::GetAccessTime() -> DWORD -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::GetFileName() -> const char * -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::SetPlayTime(DWORD) -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundData::GetPlayTime() -> DWORD -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::ReadFromDisk() -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::Destroy() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundData::open_callback(const char *, U32 *) -> U32 -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::close_callback(U32) -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundData::seek_callback(U32, S32, U32) -> S32 -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::read_callback(U32, void *, U32) -> U32 -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::isSlotIndex(DWORD) -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundData::GetEmptySlotIndex() -> int -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -decltype(CSoundData::ms_isSoundFile) CSoundData::ms_isSoundFile{}; - -decltype(CSoundData::ms_SoundFile) CSoundData::ms_SoundFile{}; diff --git a/src/platform/MilesLib/SoundInstance.cpp b/src/platform/MilesLib/SoundInstance.cpp deleted file mode 100644 index d276c2e1..00000000 --- a/src/platform/MilesLib/SoundInstance.cpp +++ /dev/null @@ -1,256 +0,0 @@ -// Platform skeleton for MilesLib/SoundInstance.h, generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. -#include "MilesLib/Stdafx.h" -#include "MilesLib/SoundInstance.h" - -#include "../PlatformStub.h" - -CSoundInstance2D::CSoundInstance2D() -{ - MT_PLATFORM_STUB(); -} - -CSoundInstance2D::~CSoundInstance2D() -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::Initialize() -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstance2D::Destroy() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::SetSound(CSoundData *) -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstance2D::Play(int, DWORD) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::Pause() const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::Resume() const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::Stop() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::GetVolume(float &) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::SetVolume(float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::IsDone() const -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstance2D::SetPosition(float, float, float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::SetOrientation(float, float, float, float, float, float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance2D::SetVelocity(float, float, float, float) const -> void -{ - MT_PLATFORM_STUB(); -} - -CSoundInstance3D::CSoundInstance3D() -{ - MT_PLATFORM_STUB(); -} - -CSoundInstance3D::~CSoundInstance3D() -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::Initialize() -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstance3D::Destroy() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::SetSound(CSoundData *) -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstance3D::Play(int, DWORD) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::Pause() const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::Resume() const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::Stop() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::GetVolume(float &) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::SetVolume(float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::IsDone() const -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstance3D::SetPosition(float, float, float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::SetOrientation(float, float, float, float, float, float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::SetVelocity(float, float, float, float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstance3D::UpdatePosition(float) -> void -{ - MT_PLATFORM_STUB(); -} - -CSoundInstanceStream::CSoundInstanceStream() -{ - MT_PLATFORM_STUB(); -} - -CSoundInstanceStream::~CSoundInstanceStream() -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::Initialize() -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstanceStream::Destroy() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::SetStream(HSTREAM) -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::SetSound(CSoundData *) -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstanceStream::Play(int, DWORD) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::Pause() const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::Resume() const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::Stop() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::GetVolume(float &) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::SetVolume(float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::IsDone() const -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstanceStream::IsData() const -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CSoundInstanceStream::SetPosition(float, float, float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::SetOrientation(float, float, float, float, float, float) const -> void -{ - MT_PLATFORM_STUB(); -} - -auto CSoundInstanceStream::SetVelocity(float, float, float, float) const -> void -{ - MT_PLATFORM_STUB(); -} diff --git a/src/platform/MilesLib/SoundInstance2D.cpp b/src/platform/MilesLib/SoundInstance2D.cpp new file mode 100644 index 00000000..23cf8397 --- /dev/null +++ b/src/platform/MilesLib/SoundInstance2D.cpp @@ -0,0 +1,121 @@ +#include "MilesLib/Stdafx.h" +#include "MilesLib/SoundManager2D.h" + +CSoundInstance2D::CSoundInstance2D() : m_sample(NULL), m_pSoundData(NULL) +{ +} + +CSoundInstance2D::~CSoundInstance2D() +{ + Destroy(); +} + +void CSoundInstance2D::Destroy() +{ + SAFE_RELEASE(m_pSoundData); + + if (m_sample) + { + AIL_release_sample_handle(m_sample); + m_sample = NULL; + } +} + +bool CSoundInstance2D::Initialize() +{ + if (m_sample) + return true; + + m_sample = AIL_allocate_sample_handle(ms_DIGDriver); + return m_sample ? true : false; +} + +bool CSoundInstance2D::SetSound(CSoundData * pSoundData) +{ + assert(m_sample != NULL && pSoundData != NULL); + + // 레퍼런스 카운트가 1이 될 때 로드를 해야 제대로 사이즈가 리턴 + // 되므로 반드시 Get을 호출 하고 진행해야 한다. + // 또, m_pSoundData가 pSoundData와 같고 m_pSoundData의 레퍼런스 + // 카운터가 1일 경우, 불필요하게 로드가 일어나므로 미리 레퍼런스 + // 카운터를 올려놔야 한다. + LPVOID lpData = pSoundData->Get(); + + AIL_init_sample(m_sample); + + if (AIL_set_sample_file(m_sample, lpData, pSoundData->GetSize()) == NULL) + { + if (m_pSoundData != NULL) + { + m_pSoundData->Release(); + m_pSoundData = NULL; + } + + pSoundData->Release(); + TraceError("%s: %s", AIL_last_error(), pSoundData->GetFileName()); + return false; + } + + if (m_pSoundData != NULL) + { + m_pSoundData->Release(); + m_pSoundData = NULL; + } + + m_pSoundData = pSoundData; + return true; +} + +bool CSoundInstance2D::IsDone() const +{ + return AIL_sample_status(m_sample) == SMP_DONE; +} + +void CSoundInstance2D::Play(int iLoopCount, DWORD dwPlayCycleTimeLimit) const +{ + AIL_set_sample_loop_count(m_sample, iLoopCount); + AIL_start_sample(m_sample); +} + +void CSoundInstance2D::Pause() const +{ + AIL_stop_sample(m_sample); +} + +void CSoundInstance2D::Resume() const +{ + AIL_resume_sample(m_sample); +} + +void CSoundInstance2D::Stop() +{ + AIL_end_sample(m_sample); + m_sample = NULL; +} + +void CSoundInstance2D::GetVolume(float& rfVolume) const +{ + AIL_sample_volume_pan(m_sample, &rfVolume, NULL); +} + +void CSoundInstance2D::SetVolume(float volume) const +{ + volume = max(0.0f, min(1.0f, volume)); + AIL_set_sample_volume_pan(m_sample, volume, 0.5f); +} + +void CSoundInstance2D::SetPosition(float x, float y, float z) const +{ + assert(!"must not call this method"); +} + +void CSoundInstance2D::SetOrientation(float x_face, float y_face, float z_face, + float x_normal, float y_normal, float z_normal) const +{ + assert(!"must not call this method"); +} + +void CSoundInstance2D::SetVelocity(float fx, float fy, float fz, float fMagnitude) const +{ + assert(!"must not call this method"); +} diff --git a/src/platform/MilesLib/SoundInstance3D.cpp b/src/platform/MilesLib/SoundInstance3D.cpp new file mode 100644 index 00000000..df1cdd93 --- /dev/null +++ b/src/platform/MilesLib/SoundInstance3D.cpp @@ -0,0 +1,137 @@ +#include "MilesLib/Stdafx.h" +#include "MilesLib/SoundManager3D.h" +#include "EterBase/Timer.h" + +CSoundInstance3D::CSoundInstance3D() : m_sample(NULL), m_pSoundData(NULL) +{ +} + +CSoundInstance3D::~CSoundInstance3D() +{ + Destroy(); +} + +void CSoundInstance3D::Destroy() +{ + SAFE_RELEASE(m_pSoundData); + + if (m_sample) + { + AIL_release_3D_sample_handle(m_sample); + m_sample = NULL; + } +} + +bool CSoundInstance3D::Initialize() +{ + if (m_sample) + return true; + + m_sample = AIL_allocate_3D_sample_handle(ms_pProviderDefault->hProvider); + return m_sample ? true : false; +} + +bool CSoundInstance3D::SetSound(CSoundData* pSoundData) +{ + assert(m_sample != NULL && pSoundData != NULL); + + // 레퍼런스 카운트가 1이 될 때 로드를 해야 제대로 사이즈가 리턴 + // 되므로 반드시 Get을 호출 하고 진행해야 한다. + // 또, m_pSoundData가 pSoundData와 같고 m_pSoundData의 레퍼런스 + // 카운터가 1일 경우, 불필요하게 로드가 일어나므로 미리 레퍼런스 + // 카운터를 올려놔야 한다. + LPVOID lpData = pSoundData->Get(); + + if (m_pSoundData != NULL) + { + m_pSoundData->Release(); + m_pSoundData = NULL; + } + + if (AIL_set_3D_sample_file(m_sample, lpData) == NULL) + { + TraceError("%s: %s", AIL_last_error(), pSoundData->GetFileName()); + pSoundData->Release(); + return false; + } + + m_pSoundData = pSoundData; + + AIL_set_3D_position(m_sample, 0.0F, 0.0F, 0.0F); + AIL_auto_update_3D_position(m_sample, 0); + return true; +} + +bool CSoundInstance3D::IsDone() const +{ + return AIL_3D_sample_status(m_sample) == SMP_DONE; +} + +void CSoundInstance3D::Play(int iLoopCount, DWORD dwPlayCycleTimeLimit) const +{ + if (!m_pSoundData) + return; + + DWORD dwCurTime = ELTimer_GetMSec(); + + if (dwCurTime - m_pSoundData->GetPlayTime() < dwPlayCycleTimeLimit) + return; + + m_pSoundData->SetPlayTime(dwCurTime); + + AIL_set_3D_sample_loop_count(m_sample, iLoopCount); + AIL_start_3D_sample(m_sample); +} + +void CSoundInstance3D::Pause() const +{ + AIL_stop_3D_sample(m_sample); +} + +void CSoundInstance3D::Resume() const +{ + AIL_resume_3D_sample(m_sample); +} + +void CSoundInstance3D::Stop() +{ + AIL_end_3D_sample(m_sample); +// m_sample = NULL; +// NOTE : IsDone을 체크하려면 m_sample이 살아있어야 합니다 - [levites] +} + +void CSoundInstance3D::GetVolume(float& rfVolume) const +{ + rfVolume = AIL_3D_sample_volume(m_sample); +} + +void CSoundInstance3D::SetVolume(float volume) const +{ + volume = max(0.0f, min(1.0f, volume)); + AIL_set_3D_sample_volume(m_sample, volume); +} + +void CSoundInstance3D::SetPosition(float x, float y, float z) const +{ + AIL_set_3D_position(m_sample, x, y, -z); +} + +void CSoundInstance3D::SetOrientation(float x_face, float y_face, float z_face, + float x_normal, float y_normal, float z_normal) const +{ + assert(!" CSoundInstance3D::SetOrientation - 사용 하지 않는 함수"); +// AIL_set_3D_orientation(m_sample, +// x_face, y_face, z_face, +// x_normal, y_normal, z_normal); +} + +void CSoundInstance3D::SetVelocity(float fDistanceX, float fDistanceY, float fDistanceZ, float fNagnitude) const +{ + AIL_set_3D_velocity(m_sample, fDistanceX, fDistanceY, fDistanceZ, fNagnitude); + AIL_auto_update_3D_position(m_sample, 1); +} + +void CSoundInstance3D::UpdatePosition(float fElapsedTime) +{ + AIL_update_3D_position(m_sample, fElapsedTime); +} \ No newline at end of file diff --git a/src/platform/MilesLib/SoundInstanceStream.cpp b/src/platform/MilesLib/SoundInstanceStream.cpp new file mode 100644 index 00000000..5c7fca4c --- /dev/null +++ b/src/platform/MilesLib/SoundInstanceStream.cpp @@ -0,0 +1,114 @@ +#include "MilesLib/Stdafx.h" +#include "MilesLib/SoundInstance.h" + +CSoundInstanceStream::CSoundInstanceStream() : m_stream(NULL) +{ +} + +CSoundInstanceStream::~CSoundInstanceStream() +{ + Destroy(); +} + +void CSoundInstanceStream::Destroy() +{ + if (m_stream != NULL) + { + AIL_close_stream(m_stream); + m_stream = NULL; + } +} + +bool CSoundInstanceStream::Initialize() +{ + return true; +} + +void CSoundInstanceStream::SetStream(HSTREAM stream) +{ + m_stream = stream; +} + +bool CSoundInstanceStream::IsDone() const +{ + return AIL_stream_status(m_stream) == -1; +} + +bool CSoundInstanceStream::IsData() const +{ + if (m_stream) + return true; + + return false; +} + +void CSoundInstanceStream::Play(int count, DWORD dwPlayCycleTimeLimit) const +{ + if (!IsData()) + return; + + AIL_set_stream_loop_count(m_stream, count); + AIL_start_stream(m_stream); +} + +void CSoundInstanceStream::Pause() const +{ + if (!IsData()) + return; + + AIL_pause_stream(m_stream, 1); +} + +void CSoundInstanceStream::Resume() const +{ + if (!IsData()) + return; + + AIL_pause_stream(m_stream, 0); +} + +void CSoundInstanceStream::Stop() +{ + if (!IsData()) + return; + + AIL_close_stream(m_stream); + m_stream = NULL; +} + +void CSoundInstanceStream::GetVolume(float& rfVolume) const +{ + float tmp; + + if (!IsData()) + return; + + AIL_stream_volume_levels(m_stream, &rfVolume, &tmp); +} + +void CSoundInstanceStream::SetVolume(float volume) const +{ + if (!IsData()) + return; + + volume = max(0.0f, min(1.0f, volume)); + AIL_set_stream_volume_levels(m_stream, volume, volume); +} + +bool CSoundInstanceStream::SetSound(CSoundData* pSound) +{ + return true; +} + +void CSoundInstanceStream::SetPosition(float x, float y, float z) const +{ +} + +void CSoundInstanceStream::SetOrientation(float x_face, float y_face, float z_face, + float x_normal, float y_normal, float z_normal) const +{ +} + +void CSoundInstanceStream::SetVelocity(float fx, float fy, float fz, float fMagnitude) const +{ +} diff --git a/src/platform/MilesLib/SoundManager.cpp b/src/platform/MilesLib/SoundManager.cpp index d61489f4..2429ad01 100644 --- a/src/platform/MilesLib/SoundManager.cpp +++ b/src/platform/MilesLib/SoundManager.cpp @@ -1,30 +1,29 @@ -// Platform implementation of MilesLib/SoundManager.h for port/headless execution. #include "MilesLib/Stdafx.h" -#include "MilesLib/SoundManager.h" -#include "AudioCommands.h" -#include -#include +#include -decltype(CSoundManager::ms_SoundManager2D) CSoundManager::ms_SoundManager2D{}; -decltype(CSoundManager::ms_SoundManager3D) CSoundManager::ms_SoundManager3D{}; -decltype(CSoundManager::ms_SoundManagerStream) CSoundManager::ms_SoundManagerStream{}; +#include "MilesLib/SoundManager.h" +#include "EterBase/Timer.h" + +CSoundManager2D CSoundManager::ms_SoundManager2D; +CSoundManager3D CSoundManager::ms_SoundManager3D; +CSoundManagerStream CSoundManager::ms_SoundManagerStream; CSoundManager::CSoundManager() { - m_bInitialized = FALSE; - m_isSoundDisable = FALSE; + m_bInitialized = FALSE; + m_isSoundDisable = FALSE; - m_fxPosition = 0.0f; - m_fyPosition = 0.0f; - m_fzPosition = 0.0f; + m_fxPosition = 0.0f; + m_fyPosition = 0.0f; + m_fzPosition = 0.0f; - m_fSoundScale = 200.0f; - m_fAmbienceSoundScale = 1000.0f; - m_fSoundVolume = 1.0f; - m_fMusicVolume = 1.0f; + m_fSoundScale = 200.0f; + m_fAmbienceSoundScale = 1000.0f; + m_fSoundVolume = 1.0f; + m_fMusicVolume = 1.0f; - m_fBackupMusicVolume = 0.0f; - m_fBackupSoundVolume = 0.0f; + m_fBackupMusicVolume = 0.0f; + m_fBackupSoundVolume = 0.0f; for (int i = 0; i < CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM; ++i) { @@ -32,7 +31,6 @@ CSoundManager::CSoundManager() rInstance.dwMusicFileNameCRC = 0; rInstance.MusicState = MUSIC_STATE_OFF; rInstance.fVolume = 0.0f; - rInstance.fLimitVolume = 0.0f; rInstance.fVolumeSpeed = 0.0f; } } @@ -43,14 +41,32 @@ CSoundManager::~CSoundManager() BOOL CSoundManager::Create() { - m_bInitialized = TRUE; + if (!ms_SoundManager2D.Initialize()) { + Tracen("CSoundManager::Create - Sound2D::Initialize - FAILURE"); + return FALSE; + } + + if (!ms_SoundManagerStream.Initialize()) + { + Tracen("CSoundManager::Create - SoundStream::Initialize - FAILURE"); + return FALSE; + } + + if (!ms_SoundManager3D.Initialize()) + { + Tracen("CSoundManager::Create - Sound3D::Initialize - FAILURE"); + return FALSE; + } + return TRUE; } void CSoundManager::Destroy() { - m_PlaySoundHistoryMap.clear(); - m_bInitialized = FALSE; + ms_SoundManagerStream.Destroy(); + ms_SoundManager3D.Destroy(); + ms_SoundManager2D.Destroy(); + m_PlaySoundHistoryMap.clear(); // Fix } void CSoundManager::SetPosition(float fx, float fy, float fz) @@ -60,12 +76,109 @@ void CSoundManager::SetPosition(float fx, float fy, float fz) m_fzPosition = fz; } -void CSoundManager::SetDirection(float, float, float, float, float, float) +void CSoundManager::SetDirection(float fxDir, float fyDir, float fzDir, float fxUp, float fyUp, float fzUp) { + ms_SoundManager3D.SetListenerDirection(fxDir, fyDir, fzDir, fxUp, fyUp, fzUp); } void CSoundManager::Update() { + // Update Information about 3D Sound + ms_SoundManager3D.SetListenerPosition(0.0f, 0.0f, 0.0f); + + for (int i = 0; i < CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM; ++i) + { + TMusicInstance & rMusicInstance = m_MusicInstances[i]; + if (MUSIC_STATE_OFF == rMusicInstance.MusicState) + continue; + + switch (rMusicInstance.MusicState) + { + case MUSIC_STATE_FADE_IN: + rMusicInstance.fVolume += rMusicInstance.fVolumeSpeed; + + if (rMusicInstance.fVolume >= GetMusicVolume()) + { + rMusicInstance.fVolume = GetMusicVolume(); + rMusicInstance.fVolumeSpeed = 0.0f; + rMusicInstance.MusicState = MUSIC_STATE_PLAY; + } + + { + CSoundInstanceStream * pInstance = ms_SoundManagerStream.GetInstance(i); + if (pInstance) + pInstance->SetVolume(rMusicInstance.fVolume); + } + break; + + case MUSIC_STATE_FADE_LIMIT_OUT: + rMusicInstance.fVolume -= rMusicInstance.fVolumeSpeed; + if (rMusicInstance.fVolume <= rMusicInstance.fLimitVolume) + { + rMusicInstance.fVolume = rMusicInstance.fLimitVolume; + rMusicInstance.fVolumeSpeed = 0.0f; + rMusicInstance.MusicState = MUSIC_STATE_PLAY; + } + { + CSoundInstanceStream * pInstance = ms_SoundManagerStream.GetInstance(i); + if (pInstance) + pInstance->SetVolume(rMusicInstance.fVolume); + } + break; + case MUSIC_STATE_FADE_OUT: + rMusicInstance.fVolume -= rMusicInstance.fVolumeSpeed; + if (rMusicInstance.fVolume <= 0.0f) + { + rMusicInstance.fVolume = 0.0f; + rMusicInstance.fVolumeSpeed = 0.0f; + rMusicInstance.MusicState = MUSIC_STATE_OFF; + + StopMusic(i); + } + { + CSoundInstanceStream * pInstance = ms_SoundManagerStream.GetInstance(i); + if (pInstance) + pInstance->SetVolume(rMusicInstance.fVolume); + } + break; + } + } +} + +void CSoundManager::UpdateSoundData(DWORD dwcurTime, const NSound::TSoundDataVector * c_pSoundDataVector) +{ + assert(!"CSoundManager::UpdateSoundData"); +} + +void CSoundManager::UpdateSoundData(float fx, float fy, float fz, DWORD dwcurTime, const NSound::TSoundDataVector * c_pSoundDataVector) +{ + assert(!"CSoundManager::UpdateSoundData"); +} + +void CSoundManager::UpdateSoundInstance(float fx, float fy, float fz, DWORD dwcurFrame, const NSound::TSoundInstanceVector * c_pSoundInstanceVector, BOOL bCheckFrequency) +{ + for (DWORD i = 0; i < c_pSoundInstanceVector->size(); ++i) + { + const NSound::TSoundInstance & c_rSoundInstance = c_pSoundInstanceVector->at(i); + if (c_rSoundInstance.dwFrame == dwcurFrame) + { + //Tracenf("PLAY SOUND %s", c_rSoundInstance.strSoundFileName.c_str()); + PlayCharacterSound3D(fx, fy, fz, c_rSoundInstance.strSoundFileName.c_str(), bCheckFrequency); + } + } +} + +void CSoundManager::UpdateSoundInstance(DWORD dwcurFrame, const NSound::TSoundInstanceVector * c_pSoundInstanceVector) +{ + for (DWORD i = 0; i < c_pSoundInstanceVector->size(); ++i) + { + const NSound::TSoundInstance & c_rSoundInstance = c_pSoundInstanceVector->at(i); + + if (c_rSoundInstance.dwFrame == dwcurFrame) + { + PlaySound2D(c_rSoundInstance.strSoundFileName.c_str()); + } + } } float CSoundManager::GetSoundScale() @@ -83,19 +196,6 @@ void CSoundManager::SetAmbienceSoundScale(float fScale) m_fAmbienceSoundScale = fScale; } -float CSoundManager::__ConvertRatioVolumeToApplyVolume(float fRatioVolume) -{ - if (0.1f > fRatioVolume) - return fRatioVolume; - - return (float)pow(10.0f, (-1.0f + fRatioVolume)); -} - -float CSoundManager::__ConvertGradeVolumeToApplyVolume(int nGradeVolume) -{ - return __ConvertRatioVolumeToApplyVolume(nGradeVolume / 5.0f); -} - void CSoundManager::SetSoundVolume(float fVolume) { if (m_isSoundDisable) @@ -104,30 +204,14 @@ void CSoundManager::SetSoundVolume(float fVolume) return; } - if (fVolume < 0.0f) fVolume = 0.0f; - if (fVolume > 1.0f) fVolume = 1.0f; + fVolume = fMAX(fVolume, 0.0f); + fVolume = fMIN(fVolume, 1.0f); m_fSoundVolume = fVolume; if (!m_isSoundDisable) { m_fBackupSoundVolume = fVolume; } - AudioCommand cmd; - cmd.type = AudioCommand::SetSoundVolume; - cmd.volume = m_fSoundVolume; - PushAudioCommand(cmd); -} - -void CSoundManager::SetSoundVolumeRatio(float fRatio) -{ - float fVolume = __ConvertRatioVolumeToApplyVolume(fRatio); - SetSoundVolume(fVolume); -} - -void CSoundManager::SetSoundVolumeGrade(int iGrade) -{ - float fVolume = __ConvertGradeVolumeToApplyVolume(iGrade); - SetSoundVolume(fVolume); } void CSoundManager::__SetMusicVolume(float fVolume) @@ -138,57 +222,69 @@ void CSoundManager::__SetMusicVolume(float fVolume) return; } - if (fVolume < 0.0f) fVolume = 0.0f; - if (fVolume > 1.0f) fVolume = 1.0f; + fVolume = fMAX(fVolume, 0.0f); + fVolume = fMIN(fVolume, 1.0f); m_fMusicVolume = fVolume; if (!m_isSoundDisable) { m_fBackupMusicVolume = fVolume; } - AudioCommand cmd; - cmd.type = AudioCommand::SetMusicVolume; - cmd.volume = m_fMusicVolume; - PushAudioCommand(cmd); + + for (int i = 0; i < CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM; ++i) + { + TMusicInstance & rMusicInstance = m_MusicInstances[i]; + if (MUSIC_STATE_OFF == rMusicInstance.MusicState) + continue; + if (MUSIC_STATE_FADE_OUT == rMusicInstance.MusicState) + continue; + + rMusicInstance.fVolume = fVolume; + + CSoundInstanceStream * pInstance = ms_SoundManagerStream.GetInstance(i); + if (pInstance) + pInstance->SetVolume(fVolume); + } +} + +float CSoundManager::__ConvertRatioVolumeToApplyVolume(float fRatioVolume) +{ + if (0.1f>fRatioVolume) + return fRatioVolume; + + return (float)pow(10.0f, (-1.0f + fRatioVolume)); +} + +float CSoundManager::__ConvertGradeVolumeToApplyVolume(int nGradeVolume) +{ + return __ConvertRatioVolumeToApplyVolume(nGradeVolume/5.0f); +} + + +void CSoundManager::SetSoundVolumeGrade(int iGrade) +{ + float fVolume=__ConvertGradeVolumeToApplyVolume(iGrade); + SetSoundVolume(fVolume); +} + +//void CSoundManager::SetMusicVolumeGrade(int iGrade) +//{ +// float fVolume=__ConvertGradeVolumeToApplyVolume(iGrade); +// __SetMusicVolume(fVolume); +//} + +void CSoundManager::SetSoundVolumeRatio(float fRatio) +{ + float fVolume = __ConvertRatioVolumeToApplyVolume(fRatio); + SetSoundVolume(fVolume); } void CSoundManager::SetMusicVolume(float fVolume) -{ +{ + //float fVolume = __ConvertRatioVolumeToApplyVolume(fRatio); __SetMusicVolume(fVolume); } -void CSoundManager::SetMusicVolumeRatio(float fRatio) -{ - float fVolume = __ConvertRatioVolumeToApplyVolume(fRatio); - SetMusicVolume(fVolume); -} - -void CSoundManager::SetMusicVolumeGrade(int iGrade) -{ - float fVolume = __ConvertGradeVolumeToApplyVolume(iGrade); - SetMusicVolume(fVolume); -} - -void CSoundManager::SaveVolume() -{ - if (m_isSoundDisable) - return; - const float musicVolume = m_fMusicVolume; - const float soundVolume = m_fSoundVolume; - SetMusicVolume(0.0f); - SetSoundVolume(0.0f); - m_fBackupMusicVolume = musicVolume; - m_fBackupSoundVolume = soundVolume; - m_isSoundDisable = TRUE; -} - -void CSoundManager::RestoreVolume() -{ - m_isSoundDisable = FALSE; - SetMusicVolume(m_fBackupMusicVolume); - SetSoundVolume(m_fBackupSoundVolume); -} - float CSoundManager::GetSoundVolume() { return m_fSoundVolume; @@ -199,170 +295,352 @@ float CSoundManager::GetMusicVolume() return m_fMusicVolume; } +BOOL CSoundManager::GetSoundInstance2D(const char * c_szSoundFileName, ISoundInstance ** ppInstance) +{ + *ppInstance = ms_SoundManager2D.GetInstance(c_szSoundFileName); + + if (!*ppInstance) + return FALSE; + + return TRUE; +} + +BOOL CSoundManager::GetSoundInstance3D(const char * c_szFileName, ISoundInstance ** ppInstance) +{ + int iIndex = ms_SoundManager3D.SetInstance(c_szFileName); + + if (-1 == iIndex) + return FALSE; + + *ppInstance = ms_SoundManager3D.GetInstance(iIndex); + + if (!*ppInstance) + return FALSE; + + return TRUE; +} + void CSoundManager::PlaySound2D(const char * c_szFileName) { - if (m_isSoundDisable || !c_szFileName || !c_szFileName[0]) + if (0.0f == GetSoundVolume()) return; - AudioCommand cmd; - cmd.type = AudioCommand::PlaySound2D; - cmd.filename = c_szFileName; - cmd.volume = m_fSoundVolume; - PushAudioCommand(cmd); + + ISoundInstance * pInstance; + if (!GetSoundInstance2D(c_szFileName, &pInstance)) + return; + + pInstance->SetVolume(GetSoundVolume()); + pInstance->Play(1); } void CSoundManager::PlaySound3D(float fx, float fy, float fz, const char * c_szFileName, int iPlayCount) { - if (m_isSoundDisable || !c_szFileName || !c_szFileName[0]) + if (0.0f == GetSoundVolume()) return; - AudioCommand cmd; - cmd.type = AudioCommand::PlaySound3D; - cmd.filename = c_szFileName; - cmd.x = fx; - cmd.y = fy; - cmd.z = fz; - cmd.play_count = iPlayCount; - cmd.volume = m_fSoundVolume; - PushAudioCommand(cmd); -} -void CSoundManager::StopSound3D(int iIndex) -{ - AudioCommand cmd; - cmd.type = AudioCommand::StopSound3D; - cmd.id = iIndex; - PushAudioCommand(cmd); + int iIndex = ms_SoundManager3D.SetInstance(c_szFileName); + if (-1 == iIndex) + return; + + ISoundInstance * pInstance = ms_SoundManager3D.GetInstance(iIndex); + if (!pInstance) + return; + + pInstance->SetPosition((fx - m_fxPosition) / m_fSoundScale, + (fy - m_fyPosition) / m_fSoundScale, + (fz - m_fzPosition) / m_fSoundScale); + + pInstance->SetVolume(GetSoundVolume()); + pInstance->Play(iPlayCount); } int CSoundManager::PlayAmbienceSound3D(float fx, float fy, float fz, const char * c_szFileName, int iPlayCount) { - if (m_isSoundDisable || !c_szFileName || !c_szFileName[0]) + if (0.0f == GetSoundVolume()) return -1; - static int s_next_id = 1; - int id = s_next_id++; - AudioCommand cmd; - cmd.type = AudioCommand::PlaySound3D; - cmd.filename = c_szFileName; - cmd.x = fx; - cmd.y = fy; - cmd.z = fz; - cmd.play_count = iPlayCount; - cmd.volume = m_fSoundVolume; - cmd.id = id; - PushAudioCommand(cmd); - return id; + + int iIndex = ms_SoundManager3D.SetInstance(c_szFileName); + if (-1 == iIndex) + return -1; + + ISoundInstance * pInstance = ms_SoundManager3D.GetInstance(iIndex); + if (!pInstance) + return -1; + + pInstance->SetPosition((fx - m_fxPosition) / m_fAmbienceSoundScale, + (fy - m_fyPosition) / m_fAmbienceSoundScale, + (fz - m_fzPosition) / m_fAmbienceSoundScale); + + pInstance->SetVolume(GetSoundVolume()); + pInstance->Play(iPlayCount); + + return iIndex; } -void CSoundManager::PlayCharacterSound3D(float fx, float fy, float fz, const char * c_szFileName, BOOL) +void CSoundManager::PlayCharacterSound3D(float fx, float fy, float fz, const char * c_szFileName, BOOL bCheckFrequency) { - PlaySound3D(fx, fy, fz, c_szFileName, 1); + if (0.0f == GetSoundVolume()) + return; + + // 어느 정도의 최적화가 필요할 수도 있다 - [levites] + if (bCheckFrequency) + { + static float s_fLimitDistance = 5000*5000; + float fdx = (fx - m_fxPosition) * (fx - m_fxPosition); + float fdy = (fy - m_fyPosition) * (fy - m_fyPosition); + + if (fdx+fdy > s_fLimitDistance) + return; + + std::map::iterator itor = m_PlaySoundHistoryMap.find(c_szFileName); + if (m_PlaySoundHistoryMap.end() != itor) + { + float fTime = itor->second; + if (CTimer::Instance().GetCurrentSecond() - fTime < 0.3f) + { + //Tracef("똑같은 소리가 0.3초 내에 다시 플레이 %s\n", c_szFileName); + return; + } + } + + m_PlaySoundHistoryMap.erase(c_szFileName); + m_PlaySoundHistoryMap.insert(std::map::value_type(c_szFileName, CTimer::Instance().GetCurrentSecond())); + } + + ISoundInstance * pInstance; + + if (!GetSoundInstance3D(c_szFileName, &pInstance)) + return; + + pInstance->SetPosition((fx - m_fxPosition) / m_fSoundScale, + (fy - m_fyPosition) / m_fSoundScale, + (fz - m_fzPosition) / m_fSoundScale); + + pInstance->SetVolume(GetSoundVolume()); + pInstance->Play(1); } -void CSoundManager::SetSoundVolume3D(int index, float volume) +void CSoundManager::StopSound3D(int iIndex) { - AudioCommand cmd; - cmd.type = AudioCommand::SetSoundVolume3D; - cmd.id = index; - cmd.volume = volume; - PushAudioCommand(cmd); + ISoundInstance * pInstance = ms_SoundManager3D.GetInstance(iIndex); + + if (!pInstance) + return; + + pInstance->Stop(); + //bool bisDone = pInstance->IsDone(); +} + +void CSoundManager::SetSoundVolume3D(int iIndex, float fVolume) +{ + ISoundInstance * pInstance = ms_SoundManager3D.GetInstance(iIndex); + + if (!pInstance) + return; + + pInstance->SetVolume(fVolume); } void CSoundManager::StopAllSound3D() { - m_PlaySoundHistoryMap.clear(); - AudioCommand cmd; - cmd.type = AudioCommand::StopAllSound3D; - PushAudioCommand(cmd); + for (int i = 0; i < CSoundManager3D::INSTANCE_MAX_COUNT; ++i) + { + StopSound3D(i); + } + + m_PlaySoundHistoryMap.clear(); // Fix } void CSoundManager::PlayMusic(const char * c_szFileName) { - if (!c_szFileName || !c_szFileName[0]) + PlayMusic(0, c_szFileName, GetMusicVolume(), 0.0f); +} + +void CSoundManager::FadeInMusic(const char * c_szFileName, float fVolumeSpeed) +{ + DWORD dwIndex; + if (GetMusicIndex(c_szFileName, &dwIndex)) + { + m_MusicInstances[dwIndex].MusicState = MUSIC_STATE_FADE_IN; + m_MusicInstances[dwIndex].fVolumeSpeed = fVolumeSpeed; return; - AudioCommand cmd; - cmd.type = AudioCommand::PlayMusic; - cmd.filename = c_szFileName; - cmd.volume = m_fMusicVolume; - PushAudioCommand(cmd); -} + } -void CSoundManager::FadeInMusic(const char * c_szFileName, float speed) -{ - if (!c_szFileName || !c_szFileName[0]) + FadeOutAllMusic(); + + //Tracenf("FadeInMusic: %s", c_szFileName); + + for (int i = 0; i < CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM; ++i) + { + TMusicInstance & rMusicInstance = m_MusicInstances[i]; + if (MUSIC_STATE_OFF != rMusicInstance.MusicState) + continue; + + PlayMusic(i, c_szFileName, 0.0f, fVolumeSpeed); return; - AudioCommand cmd; - cmd.type = AudioCommand::FadeInMusic; - cmd.filename = c_szFileName; - cmd.volume = m_fMusicVolume; - cmd.speed = speed; - PushAudioCommand(cmd); + } + + return; + + // If there is no empty music slot, then play music on slot 0. + /* + StopMusic(0); + PlayMusic(0, c_szFileName, 0.0f, fVolumeSpeed); + */ } -void CSoundManager::FadeOutMusic(const char * c_szFileName, float fSpeed) +void CSoundManager::FadeLimitOutMusic(const char * c_szFileName, float fLimitVolume, float fVolumeSpeed) { - AudioCommand cmd; - cmd.type = AudioCommand::FadeOutMusic; - cmd.filename = c_szFileName ? c_szFileName : ""; - cmd.speed = fSpeed; - PushAudioCommand(cmd); + //Tracenf("FadeLimitOutMusic: %s", c_szFileName); + + DWORD dwIndex; + if (!GetMusicIndex(c_szFileName, &dwIndex)) + { + Tracenf("FadeOutMusic: %s - ERROR NOT EXIST", c_szFileName); + return; + } + + if (dwIndex >= CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM) + { + Tracenf("FadeOutMusic: %s - ERROR OUT OF RANGE", c_szFileName); + return; + } + + SMusicInstance& rkMusicInst=m_MusicInstances[dwIndex]; + rkMusicInst.MusicState = MUSIC_STATE_FADE_LIMIT_OUT; + rkMusicInst.fVolumeSpeed = fVolumeSpeed; + rkMusicInst.fLimitVolume = __ConvertRatioVolumeToApplyVolume(fLimitVolume); + + //Tracenf("LimitVolume %f(%f)", fLimitVolume, rkMusicInst.fLimitVolume); } -void CSoundManager::FadeLimitOutMusic(const char * c_szFileName, float limit, float speed) +void CSoundManager::FadeOutMusic(const char * c_szFileName, float fVolumeSpeed) { - AudioCommand cmd; - cmd.type = AudioCommand::FadeLimitOutMusic; - cmd.filename = c_szFileName ? c_szFileName : ""; - cmd.volume = __ConvertRatioVolumeToApplyVolume(limit); - cmd.speed = speed; - PushAudioCommand(cmd); + //Tracenf("FadeOutMusic: %s", c_szFileName); + + DWORD dwIndex; + if (!GetMusicIndex(c_szFileName, &dwIndex)) + { + Tracenf("FadeOutMusic: %s - ERROR NOT EXIST", c_szFileName); + return; + } + + if (dwIndex >= CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM) + { + Tracenf("FadeOutMusic: %s - ERROR OUT OF RANGE", c_szFileName); + return; + } + + m_MusicInstances[dwIndex].MusicState = MUSIC_STATE_FADE_OUT; + m_MusicInstances[dwIndex].fVolumeSpeed = fVolumeSpeed; } void CSoundManager::FadeOutAllMusic() { - AudioCommand cmd; - cmd.type = AudioCommand::FadeOutAllMusic; - PushAudioCommand(cmd); + //Tracenf("FadeOutAllMusic"); + + for (int i = 0; i < CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM; ++i) + { + if (MUSIC_STATE_OFF == m_MusicInstances[i].MusicState) + continue; + + m_MusicInstances[i].MusicState = MUSIC_STATE_FADE_OUT; + m_MusicInstances[i].fVolumeSpeed = 0.01f; + } +} + +void CSoundManager::SaveVolume() +{ + // NOTE : 두번 이상 Save를 시도할때는 그냥 Return + if (m_isSoundDisable) + return; + + float fBackupMusicVolume = m_fMusicVolume; + float fBackupSoundVolume = m_fSoundVolume; + __SetMusicVolume(0.0f); + SetSoundVolume(0.0f); + m_fBackupMusicVolume = fBackupMusicVolume; + m_fBackupSoundVolume = fBackupSoundVolume; + m_isSoundDisable = TRUE; +} + +void CSoundManager::RestoreVolume() +{ + m_isSoundDisable = FALSE; + __SetMusicVolume(m_fBackupMusicVolume); + SetSoundVolume(m_fBackupSoundVolume); +} + +void CSoundManager::PlayMusic(DWORD dwIndex, const char * c_szFileName, float fVolume, float fVolumeSpeed) +{ + if (dwIndex >= CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM) + return; + + if (!ms_SoundManagerStream.SetInstance(dwIndex, c_szFileName)) + { + TraceError("CSoundManager::PlayMusic - Failed to load stream sound : %s", c_szFileName); + return; + } + + CSoundInstanceStream * pInstance = ms_SoundManagerStream.GetInstance(dwIndex); + if (!pInstance) + { + TraceError("CSoundManager::PlayMusic - There is no stream sound instance : %s", c_szFileName); + return; + } + + pInstance->SetVolume(fVolume); + pInstance->Play(0); + + TMusicInstance & rMusicInstance = m_MusicInstances[dwIndex]; + rMusicInstance.fVolume = fVolume; + rMusicInstance.fVolumeSpeed = fVolumeSpeed; + rMusicInstance.MusicState = MUSIC_STATE_FADE_IN; + + std::string strFileName; + StringPath(c_szFileName, strFileName); + rMusicInstance.dwMusicFileNameCRC = GetCaseCRC32(strFileName.c_str(), strFileName.length()); +} + +void CSoundManager::StopMusic(DWORD dwIndex) +{ + if (dwIndex >= CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM) + return; + + CSoundInstanceStream * pInstance = ms_SoundManagerStream.GetInstance(dwIndex); + if (!pInstance) + return; + + pInstance->Stop(); + + TMusicInstance & rMusicInstance = m_MusicInstances[dwIndex]; + rMusicInstance.fVolume = 0.0f; + rMusicInstance.fVolumeSpeed = 0.0f; + rMusicInstance.MusicState = MUSIC_STATE_OFF; + rMusicInstance.dwMusicFileNameCRC = 0; +} + +BOOL CSoundManager::GetMusicIndex(const char * c_szFileName, DWORD * pdwIndex) +{ + std::string strFileName; + StringPath(c_szFileName, strFileName); + DWORD dwCRC = GetCaseCRC32(strFileName.c_str(), strFileName.length()); + + for (int i = 0; i < CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM; ++i) + { + const TMusicInstance & c_rMusicInstance = m_MusicInstances[i]; + if (MUSIC_STATE_OFF != c_rMusicInstance.MusicState) + if (c_rMusicInstance.dwMusicFileNameCRC == dwCRC) + { + *pdwIndex = i; + return TRUE; + } + } + + return FALSE; } void CSoundManager::FadeAll() { FadeOutAllMusic(); } - -void CSoundManager::UpdateSoundData(DWORD, const NSound::TSoundDataVector *) -{ -} - -void CSoundManager::UpdateSoundData(float, float, float, DWORD, const NSound::TSoundDataVector *) -{ -} - -void CSoundManager::UpdateSoundInstance(float, float, float, DWORD, const NSound::TSoundInstanceVector *, BOOL) -{ -} - -void CSoundManager::UpdateSoundInstance(DWORD, const NSound::TSoundInstanceVector *) -{ -} - -void CSoundManager::PlayMusic(DWORD, const char *, float, float) -{ -} - -void CSoundManager::StopMusic(DWORD) -{ -} - -BOOL CSoundManager::GetMusicIndex(const char *, DWORD *) -{ - return FALSE; -} - -BOOL CSoundManager::GetSoundInstance2D(const char *, ISoundInstance **) -{ - return FALSE; -} - -BOOL CSoundManager::GetSoundInstance3D(const char *, ISoundInstance **) -{ - return FALSE; -} diff --git a/src/platform/MilesLib/SoundManager2D.cpp b/src/platform/MilesLib/SoundManager2D.cpp index 22d09992..762e7976 100644 --- a/src/platform/MilesLib/SoundManager2D.cpp +++ b/src/platform/MilesLib/SoundManager2D.cpp @@ -1,33 +1,79 @@ -// Platform skeleton for MilesLib/SoundManager2D.h (40250 MilesLib/SoundManager2D.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. #include "MilesLib/Stdafx.h" #include "MilesLib/SoundManager2D.h" -#include "../PlatformStub.h" - CSoundManager2D::CSoundManager2D() { - MT_PLATFORM_STUB(); } CSoundManager2D::~CSoundManager2D() -{ - MT_PLATFORM_STUB(); +{ } -auto CSoundManager2D::Initialize() -> bool +bool CSoundManager2D::Initialize() { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + CSoundBase::Initialize(); + + if (ms_DIGDriver) + return true; + + ms_DIGDriver = AIL_open_digital_driver(44100, 16, 2, 0); + + for (int i = 0; i < INSTANCE_MAX_COUNT; ++i) + ms_Instances[i].Initialize(); +/* ms_DIGDriver = AIL_open_digital_driver(44100, + (DIG_F_STEREO_16 & DIG_F_16BITS_MASK) ? 16 : 8, + (DIG_F_STEREO_16 & DIG_F_16BITS_MASK) ? 2 : 1, + 0); +*/ + return true; } -auto CSoundManager2D::Destroy() -> void -{ - MT_PLATFORM_STUB(); +void CSoundManager2D::Destroy() +{ + for (int i = 0; i < INSTANCE_MAX_COUNT; ++i) + ms_Instances[i].Destroy(); + + if (ms_DIGDriver != NULL) + { + AIL_close_digital_driver(ms_DIGDriver); + ms_DIGDriver = NULL; + } + + CSoundBase::Destroy(); } -auto CSoundManager2D::GetInstance(const char *) -> ISoundInstance * +ISoundInstance * CSoundManager2D::GetInstance(const char * c_pszFileName) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + DWORD dwFileCRC = GetFileCRC(c_pszFileName); + TSoundDataMap::iterator itor = ms_dataMap.find(dwFileCRC); + + CSoundData * pkSoundData; + + if (itor == ms_dataMap.end()) + pkSoundData = AddFile(dwFileCRC, c_pszFileName); // CSoundBase::AddFile + else + pkSoundData = itor->second; + + assert(pkSoundData != NULL); + + static DWORD k = 0; + + DWORD start = k++; + DWORD end = start + INSTANCE_MAX_COUNT; + + while (start < end) + { + CSoundInstance2D * pkInst = &ms_Instances[start % INSTANCE_MAX_COUNT]; + + if (pkInst->IsDone()) + { + if (!pkInst->SetSound(pkSoundData)) + TraceError("CSoundManager2D::GetInstance (filename: %s)", c_pszFileName); + return (pkInst); + } + + ++start; + } + + return NULL; } diff --git a/src/platform/MilesLib/SoundManager3D.cpp b/src/platform/MilesLib/SoundManager3D.cpp index 707fd43d..21b23738 100644 --- a/src/platform/MilesLib/SoundManager3D.cpp +++ b/src/platform/MilesLib/SoundManager3D.cpp @@ -1,76 +1,205 @@ -// Platform skeleton for MilesLib/SoundManager3D.h (40250 MilesLib/SoundManager3D.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. #include "MilesLib/Stdafx.h" #include "MilesLib/SoundManager3D.h" -#include "../PlatformStub.h" - CSoundManager3D::CSoundManager3D() { - MT_PLATFORM_STUB(); + m_bInit = false; } CSoundManager3D::~CSoundManager3D() { - MT_PLATFORM_STUB(); } -auto CSoundManager3D::Initialize() -> bool +bool CSoundManager3D::Initialize() { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + CSoundBase::Initialize(); + + if (ms_pProviderDefault) + return true; + + ms_ProviderVector.resize(MAX_PROVIDERS); + + HPROENUM enum3D = HPROENUM_FIRST; + int i = 0; + + while (AIL_enumerate_3D_providers(&enum3D, + &ms_ProviderVector[i].hProvider, + &ms_ProviderVector[i].name) && (i < MAX_PROVIDERS)) + { + TProvider * provider = &ms_ProviderVector[i]; + + //if (strcmp(provider->name, "DirectSound3D Software Emulation") == 0) + //if (strcmp(provider->name, "DirectSound3D Hardware Support") == 0) + //if (strcmp(provider->name, "DirectSound3D 7+ Software - Pan and Volume") == 0) + //if (strcmp(provider->name, "DirectSound3D 7+ Software - Light HRTF") == 0) + //if (strcmp(provider->name, "DirectSound3D 7+ Software - Full HRTF") == 0) + //if (strcmp(provider->name, "RAD Game Tools RSX 3D Audio") == 0) + //if (strcmp(provider->name, "Dolby Surround") == 0) + if (strcmp(provider->name, "Miles Fast 2D Positional Audio") == 0) + ms_pProviderDefault = provider; + + ++i; + } + + if (!ms_pProviderDefault) + { + CSoundBase::Destroy(); + return false; + } + + assert(ms_pProviderDefault != NULL); + + if (M3D_NOERR != AIL_open_3D_provider(ms_pProviderDefault->hProvider)) + { +// assert(!"AIL_open_3D_provider error"); +// char buf[64]; +// sprintf(buf, "Error AIL_open_3D_provider: %s\n", AIL_last_error()); +// OutputDebugString(buf); + + CSoundBase::Destroy(); + return false; + } + + m_pListener = AIL_open_3D_listener(ms_pProviderDefault->hProvider); + + SetListenerPosition(0.0f, 0.0f, 0.0f); + + for (i = 0; i < INSTANCE_MAX_COUNT; ++i) + { + m_Instances[i].Initialize(); + m_bLockingFlag[i] = false; + } + + m_bInit = true; + return true; } -auto CSoundManager3D::Destroy() -> void -{ - MT_PLATFORM_STUB(); + +void CSoundManager3D::Destroy() +{ + if (!m_bInit) + return; + + for (int i = 0; i < INSTANCE_MAX_COUNT; ++i) + m_Instances[i].Destroy(); + + if (m_pListener) + { + AIL_close_3D_listener(m_pListener); + m_pListener = NULL; + } + + if (ms_pProviderDefault) + { + AIL_close_3D_provider(ms_pProviderDefault->hProvider); + ms_pProviderDefault = NULL; + } + + CSoundBase::Destroy(); + m_bInit = false; } -auto CSoundManager3D::GetEmptyInstanceIndex() -> int +void CSoundManager3D::SetListenerDirection(float fxDir, float fyDir, float fzDir, float fxUp, float fyUp, float fzUp) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + if (NULL == m_pListener) + return; + AIL_set_3D_orientation(m_pListener, fxDir, fyDir, -fzDir, fxUp, fyUp, -fzUp); } -auto CSoundManager3D::SetInstance(const char *) -> int +void CSoundManager3D::SetListenerPosition(float fX, float fY, float fZ) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); +// assert(m_pListener != NULL); + if (NULL == m_pListener) + return; + AIL_set_3D_position(m_pListener, fX, fY, -fZ); } -auto CSoundManager3D::GetInstance(DWORD) -> ISoundInstance * +void CSoundManager3D::SetListenerVelocity(float fDistanceX, float fDistanceY, float fDistanceZ, float fNagnitude) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); +// assert(m_pListener != NULL); + if (NULL == m_pListener) + return; + AIL_set_3D_velocity(m_pListener, fDistanceX, fDistanceY, -fDistanceZ, fNagnitude); } -auto CSoundManager3D::SetListenerDirection(float, float, float, float, float, float) -> void +int CSoundManager3D::SetInstance(const char * c_pszFileName) { - MT_PLATFORM_STUB(); + DWORD dwFileCRC = GetFileCRC(c_pszFileName); + TSoundDataMap::iterator itor = ms_dataMap.find(dwFileCRC); + + CSoundData * pkSoundData; + + if (itor == ms_dataMap.end()) + pkSoundData = AddFile(dwFileCRC, c_pszFileName); // CSoundBase::AddFile + else + pkSoundData = itor->second; + + assert(pkSoundData != NULL); + + static DWORD k = 0; + + DWORD start = k++; + DWORD end = start + INSTANCE_MAX_COUNT; + + while (start < end) + { + CSoundInstance3D * pkInst = &m_Instances[start % INSTANCE_MAX_COUNT]; + + if (pkInst->IsDone()) + { + if (!pkInst->SetSound(pkSoundData)) + { + TraceError("CSoundManager3D::GetInstance (filename: %s)", c_pszFileName); + // NOTE : 사운드가 없을 경우 Failed to set. return NULL. - [levites] + return -1; + } + + return (start % INSTANCE_MAX_COUNT); + } + + ++start; + + // 설마 DWORD 한계값을 넘어갈리야 없겠지만.. 그래도.. 혹시나.. - [levites] + if (start > 50000) + { + start = 0; + return -1; + } + } + + return -1; } -auto CSoundManager3D::SetListenerPosition(float, float, float) -> void +ISoundInstance * CSoundManager3D::GetInstance(DWORD dwIndex) { - MT_PLATFORM_STUB(); + if (dwIndex >= INSTANCE_MAX_COUNT) + { + assert(dwIndex < INSTANCE_MAX_COUNT); + return NULL; + } + return &m_Instances[dwIndex]; } -auto CSoundManager3D::SetListenerVelocity(float, float, float, float) -> void +__forceinline bool CSoundManager3D::IsValidInstanceIndex(int iIndex) { - MT_PLATFORM_STUB(); + if (iIndex >= 0 && iIndex < INSTANCE_MAX_COUNT) + return true; + + return false; } -auto CSoundManager3D::Lock(int) -> void +void CSoundManager3D::Lock(int iIndex) { - MT_PLATFORM_STUB(); + if (!IsValidInstanceIndex(iIndex)) + return; + + m_bLockingFlag[iIndex] = true; } -auto CSoundManager3D::Unlock(int) -> void +void CSoundManager3D::Unlock(int iIndex) { - MT_PLATFORM_STUB(); -} + if (!IsValidInstanceIndex(iIndex)) + return; -auto CSoundManager3D::IsValidInstanceIndex(int) -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + m_bLockingFlag[iIndex] = false; } diff --git a/src/platform/MilesLib/SoundManagerStream.cpp b/src/platform/MilesLib/SoundManagerStream.cpp index e422292d..4cbfd476 100644 --- a/src/platform/MilesLib/SoundManagerStream.cpp +++ b/src/platform/MilesLib/SoundManagerStream.cpp @@ -1,45 +1,64 @@ -// Platform skeleton for MilesLib/SoundManagerStream.h (40250 MilesLib/SoundManagerStream.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. #include "MilesLib/Stdafx.h" #include "MilesLib/SoundManagerStream.h" -#include "../PlatformStub.h" - CSoundManagerStream::CSoundManagerStream() { - MT_PLATFORM_STUB(); } CSoundManagerStream::~CSoundManagerStream() -{ - MT_PLATFORM_STUB(); +{ } -auto CSoundManagerStream::Initialize() -> bool +bool CSoundManagerStream::Initialize() { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + CSoundBase::Initialize(); + + if (ms_DIGDriver) + return true; + + ms_DIGDriver = AIL_open_digital_driver(44100, 16, 2, 0); + + for (int i = 0; i < MUSIC_INSTANCE_MAX_NUM; ++i) + m_Instances[i].Initialize(); + + return true; } -auto CSoundManagerStream::Destroy() -> void -{ - MT_PLATFORM_STUB(); +void CSoundManagerStream::Destroy() +{ + for (int i=0; i bool +bool CSoundManagerStream::SetInstance(DWORD dwIndex, const char* filename) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + if (!CheckInstanceIndex(dwIndex)) + return false; + + HSTREAM hStream = AIL_open_stream(ms_DIGDriver, filename, 0); + + if (NULL == hStream) + return false; + + m_Instances[dwIndex].SetStream(hStream); + + return true; } -auto CSoundManagerStream::GetInstance(DWORD) -> CSoundInstanceStream * +CSoundInstanceStream * CSoundManagerStream::GetInstance(DWORD dwIndex) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + if (!CheckInstanceIndex(dwIndex)) + return NULL; + + return &m_Instances[dwIndex]; } -auto CSoundManagerStream::CheckInstanceIndex(DWORD) -> bool +bool CSoundManagerStream::CheckInstanceIndex(DWORD dwIndex) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} + if (dwIndex >= DWORD(MUSIC_INSTANCE_MAX_NUM)) + return false; + + return true; +} \ No newline at end of file diff --git a/src/platform/MilesLib/Type.cpp b/src/platform/MilesLib/Type.cpp index 1fced467..e58ba1ed 100644 --- a/src/platform/MilesLib/Type.cpp +++ b/src/platform/MilesLib/Type.cpp @@ -1,34 +1,136 @@ -// Platform skeleton for MilesLib/Type.h (40250 MilesLib/Type.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. #include "MilesLib/Stdafx.h" #include "MilesLib/Type.h" +#include "EterLib/TextFileLoader.h" -#include "../PlatformStub.h" +std::string NSound::strResult; -auto NSound::LoadSoundInformationPiece(const char *, TSoundDataVector &, const char *) -> bool +const char * NSound::GetResultString() { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + return strResult.c_str(); } -auto NSound::SaveSoundInformationPiece(const char *, TSoundDataVector &) -> bool +void NSound::SetResultString(const char * c_pszStr) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + strResult.assign(c_pszStr); } -auto NSound::DataToInstance(const TSoundDataVector &, TSoundInstanceVector *) -> void +bool NSound::LoadSoundInformationPiece(const char * c_szFileName, NSound::TSoundDataVector & rSoundDataVector, const char * c_szPathHeader) { - MT_PLATFORM_STUB(); + std::string strResult; + strResult = c_szFileName; + + CTextFileLoader* pkTextFileLoader=CTextFileLoader::Cache(c_szFileName); + if (!pkTextFileLoader) + return false; + + CTextFileLoader& rkTextFileLoader=*pkTextFileLoader; + if (rkTextFileLoader.IsEmpty()) + { + SetResultString((strResult + " 읽기용 파일을 열 수 없음").c_str()); + return false; + } + + rkTextFileLoader.SetTop(); + + int iCount; + if (!rkTextFileLoader.GetTokenInteger("sounddatacount", &iCount)) + { + SetResultString((strResult + " 파일 포멧 에러, SoundDataCount를 찾을 수 없음").c_str()); + return false; + } + + rSoundDataVector.clear(); + rSoundDataVector.resize(iCount); + + char szSoundDataHeader[32+1]; + for (DWORD i = 0; i < rSoundDataVector.size(); ++i) + { + _snprintf(szSoundDataHeader, sizeof(szSoundDataHeader), "sounddata%02d", i); + CTokenVector * pTokenVector; + if (!rkTextFileLoader.GetTokenVector(szSoundDataHeader, &pTokenVector)) + { + SetResultString((strResult + " 파일 포멧 에러: " + szSoundDataHeader + " 를 찾을 수 없음").c_str()); + return false; + } + + if (2 != pTokenVector->size()) + { + SetResultString((strResult + " 파일 포멧 에러: 벡터 크기가 2가 아님").c_str()); + return false; + } + + rSoundDataVector[i].fTime = (float) atof(pTokenVector->at(0).c_str()); + if (c_szPathHeader) + { + rSoundDataVector[i].strSoundFileName = c_szPathHeader; + rSoundDataVector[i].strSoundFileName += pTokenVector->at(1).c_str(); + } + else + { + rSoundDataVector[i].strSoundFileName = pTokenVector->at(1).c_str(); + } + } + + SetResultString((strResult + " 불러옴").c_str()); + return true; } -auto NSound::GetResultString() -> const char * +bool NSound::SaveSoundInformationPiece(const char * c_szFileName, NSound::TSoundDataVector & rSoundDataVector) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + if (rSoundDataVector.empty()) // 데이터가 없으면 성공으로 간주 + { + if (IsFile(c_szFileName)) // 데이터는 비어있는데 파일이 있다면 + { + _unlink(c_szFileName); // 지운다. + } + return true; + } + + std::string strResult; + strResult = c_szFileName; + + FILE * File = fopen(c_szFileName, "wt"); + + if (!File) + { + char szErrorText[256+1]; + _snprintf(szErrorText, sizeof(szErrorText), "Failed to save file (%s).\nPlease check if it is read-only or you have no space on the disk.\n", c_szFileName); + LogBox(szErrorText, "에러"); + SetResultString((strResult + " 쓰기용 파일을 열 수 없음").c_str()); + return false; + } + + fprintf(File, "ScriptType CharacterSoundInformation\n"); + fprintf(File, "\n"); + + fprintf(File, "SoundDataCount %d\n", rSoundDataVector.size()); + + for (DWORD i = 0; i < rSoundDataVector.size(); ++i) + { + NSound::TSoundData & rSoundData = rSoundDataVector[i]; + fprintf(File, "SoundData%02d %f \"%s\"\n", i, rSoundData.fTime, rSoundData.strSoundFileName.c_str()); + } + + fclose(File); + return true; } -auto NSound::SetResultString(const char *) -> void +void NSound::DataToInstance(const TSoundDataVector & c_rSoundDataVector, TSoundInstanceVector * pSoundInstanceVector) { - MT_PLATFORM_STUB(); -} + if (c_rSoundDataVector.empty()) + return; + + DWORD dwFPS = 60; + const float c_fFrameTime = 1.0f / float(dwFPS); + + pSoundInstanceVector->clear(); + pSoundInstanceVector->resize(c_rSoundDataVector.size()); + for (DWORD i = 0; i < c_rSoundDataVector.size(); ++i) + { + const TSoundData & c_rSoundData = c_rSoundDataVector[i]; + TSoundInstance & rSoundInstance = pSoundInstanceVector->at(i); + + rSoundInstance.dwFrame = (DWORD) (c_rSoundData.fTime / c_fFrameTime); + rSoundInstance.strSoundFileName = c_rSoundData.strSoundFileName; + } +} \ No newline at end of file diff --git a/src/platform/ScriptLib/PythonBoot.cpp b/src/platform/ScriptLib/PythonBoot.cpp index 9bd8576a..7dcde07c 100644 --- a/src/platform/ScriptLib/PythonBoot.cpp +++ b/src/platform/ScriptLib/PythonBoot.cpp @@ -401,6 +401,17 @@ void UIMouseWheel(int nLen) pkCmrCur->Wheel(nLen); } +// 40250 CPythonApplication::WindowProcedure WM_ACTIVATEAPP (PythonApplicationProcedure.cpp:58): the sound +// is muted while the window is inactive. +void UIActivate(bool active) +{ + if (!g_sound_manager) return; + if (active) + g_sound_manager->RestoreVolume(); + else + g_sound_manager->SaveVolume(); +} + // 40250 CPythonApplication::OnKeyDown/OnKeyUp (PythonApplicationEvent.cpp:130-146): ESC first goes // to RunPressEscapeKey (the OnPressEscapeKey chain every dialog closes on), then to RunKeyDown. void UIKey(int key, bool pressed) @@ -757,6 +768,9 @@ bool Start(const char* stdlib_path, std::string* error) g_game_singletons->pySystem.GetConfig()->width = ui_w; g_game_singletons->pySystem.GetConfig()->height = ui_h; } + // 40250 CPythonApplication::Create (PythonApplication.cpp:1186): no warning box when it fails. + if (!g_game_singletons->pySystem.IsNoSoundCard()) + g_sound_manager->Create(); g_game_singletons->pySystem.GetDisplaySettings(); // 40250 CPythonApplication::Create (PythonApplication.cpp:1233) g_flying_manager->SetMapManagerPtr(&g_game_singletons->pyBackground); // 40250 CPythonApplication ctor (PythonApplication.cpp:82) return true; @@ -1049,6 +1063,7 @@ void Stop() g_race_manager.reset(); g_server_state_checker.reset(); g_flying_manager.reset(); + g_sound_manager->Destroy(); // 40250 CPythonApplication::Destroy (PythonApplication.cpp:1442) g_sound_manager.reset(); g_player.reset(); g_account_connector.reset(); diff --git a/src/platform/ScriptLib/PythonBoot.h b/src/platform/ScriptLib/PythonBoot.h index 231c2517..90e6097b 100644 --- a/src/platform/ScriptLib/PythonBoot.h +++ b/src/platform/ScriptLib/PythonBoot.h @@ -70,6 +70,8 @@ void UIMouseMove(int x, int y); void UIMouseButton(int button, bool pressed, int x, int y); void UIMouseWheel(int nLen); void UIKey(int key, bool pressed); +// WM_ACTIVATEAPP: the window gained (true) or lost the focus. +void UIActivate(bool active); // Mobile touch & joystick controls void SetMoveDirection(float angleDeg, bool moving); void SetAttackKey(bool pressed); diff --git a/src/platform/UserInterface/PythonApplicationCamera.cpp b/src/platform/UserInterface/PythonApplicationCamera.cpp index 3c879461..1ab23e89 100644 --- a/src/platform/UserInterface/PythonApplicationCamera.cpp +++ b/src/platform/UserInterface/PythonApplicationCamera.cpp @@ -132,8 +132,17 @@ void CPythonApplication::__UpdateCamera() if (0.0f != s_app.m_kCmrPos.m_fUpDir) pMainCamera->MoveVertical(s_app.m_kCmrPos.m_fUpDir); - // PORT: SkipRenderBuffering while dragging and the listener update of m_SoundManager follow here in - // 40250; there is no render buffering and sound is not ported (2V4). + // PORT: SkipRenderBuffering while dragging follows here in 40250; there is no render buffering. + + ////////////////////// + // Sound Setting + // PORT: m_SoundManager is the CSoundManager singleton (PythonBoot's g_sound_manager). + const D3DXVECTOR3 & c_rv3CameraDirection = pMainCamera->GetView(); + const D3DXVECTOR3 & c_rv3CameraUp = pMainCamera->GetUp(); + CSoundManager::Instance().SetPosition(s_app.m_v3CenterPosition.x, s_app.m_v3CenterPosition.y, s_app.m_v3CenterPosition.z); // Listener - character position + CSoundManager::Instance().SetDirection(c_rv3CameraDirection.x, c_rv3CameraDirection.y, c_rv3CameraDirection.z, c_rv3CameraUp.x, c_rv3CameraUp.y, c_rv3CameraUp.z); + CSoundManager::Instance().Update(); + ////////////////////// } void CPythonApplication::SetViewDirCameraSpeed(float fSpeed) diff --git a/src/platform/UserInterface/UserInterface.cpp b/src/platform/UserInterface/UserInterface.cpp index a67423ed..3749c9b9 100644 --- a/src/platform/UserInterface/UserInterface.cpp +++ b/src/platform/UserInterface/UserInterface.cpp @@ -1,10 +1,8 @@ // Platform implementation of the pack bootstrap from 40250 UserInterface/UserInterface.cpp. PackInitialize is the 40250 -// function with three platform differences: +// function with two platform differences: // - pack names in Index are matched to the on-disk .eix/.epk case-insensitively ("Etc" is ETC.eix on NTFS, and // CEterPack::CreateIndexFile fopens the name as given), so the registered dbname carries the on-disk spelling; -// - no LogBoxf (a Win32 message box), only the TraceError next to it; -// - CTextFileLoader::SetCacheMode and CSoundData::SetPackMode are not called: EterLib/TextFileLoader and MilesLib -// are not ported yet, and neither affects which files the pack manager registers or returns. +// - no LogBoxf (a Win32 message box), only the TraceError next to it. // Built as in the Distribute configuration (_DISTRIBUTE): pack first. #include "EterBase/StdAfx.h" #include "EterBase/FileLoader.h" @@ -12,6 +10,8 @@ #include "EterBase/Debug.h" #include "EterBase/lzo.h" #include "EterPack/EterPackManager.h" +#include "EterLib/TextFileLoader.h" +#include "MilesLib/SoundData.h" #include "UserInterface.h" #include @@ -147,9 +147,12 @@ bool PackInitialize(const char * c_pszFolder) return false; } + CTextFileLoader::SetCacheMode(); CEterPackManager::Instance().SetCacheMode(); CEterPackManager::Instance().SetSearchMode(bPackFirst); + CSoundData::SetPackMode(); // Miles file callbacks + const std::map names = list_folder(stFolder); std::string strPackName, strTexCachePackName; diff --git a/src/port/CMakeLists.txt b/src/port/CMakeLists.txt index a4a35f2a..68de3532 100644 --- a/src/port/CMakeLists.txt +++ b/src/port/CMakeLists.txt @@ -195,6 +195,11 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME) add_test(NAME port.text COMMAND $) set_tests_properties(port.text PROPERTIES SKIP_RETURN_CODE 77) + # MilesLib: the 40250 sound managers over the Miles emulation (platform/MilesLib/MilesAil.cpp). + add_executable(port_miles_test ${PROJECT_SOURCE_DIR}/tests/port/port_miles_test.cpp) + target_link_libraries(port_miles_test PRIVATE port_platform) + add_test(NAME port.miles COMMAND $) + # 批次 2V1: Packet.h struct sizes against the classic wire layouts. add_executable(port_packet_test ${PROJECT_SOURCE_DIR}/tests/port/port_packet_test.cpp ${PROJECT_SOURCE_DIR}/tests/port/port_packet_test_wire.cpp) diff --git a/src/port/common/Win32Crt.cpp b/src/port/common/Win32Crt.cpp index 1ae2e4d7..09bc4f35 100644 --- a/src/port/common/Win32Crt.cpp +++ b/src/port/common/Win32Crt.cpp @@ -52,6 +52,11 @@ int _access(const char* path, int mode) return access(path, m); } +int _unlink(const char* path) +{ + return unlink(path); +} + namespace { struct FindState diff --git a/src/port/common/Win32Crt.h b/src/port/common/Win32Crt.h index 4974b9ed..de8222e7 100644 --- a/src/port/common/Win32Crt.h +++ b/src/port/common/Win32Crt.h @@ -58,11 +58,12 @@ int _snprintf(char* buf, size_t count, const char* fmt, ...) ; // kernel32 file-system calls (ANSI). CreateDirectory ignores the security attributes; _access takes -// the MSVC modes 0 (exists), 2 (write), 4 (read), 6 (read/write). +// the MSVC modes 0 (exists), 2 (write), 4 (read), 6 (read/write). _unlink is the one. BOOL CreateDirectory(LPCSTR path, void* security_attributes); BOOL DeleteFile(LPCSTR path); BOOL CopyFile(LPCSTR existing_path, LPCSTR new_path, BOOL fail_if_exists); int _access(const char* path, int mode); +int _unlink(const char* path); #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define FILE_ATTRIBUTE_NORMAL 0x00000080 struct WIN32_FIND_DATA { DWORD dwFileAttributes; char cFileName[MAX_PATH]; }; diff --git a/src/port/common/shim/sdk/mss.h b/src/port/common/shim/sdk/mss.h index 264976c1..df7b1e08 100644 --- a/src/port/common/shim/sdk/mss.h +++ b/src/port/common/shim/sdk/mss.h @@ -1,6 +1,7 @@ #pragma once -// Shim for Miles Sound System : the scalar types and opaque handles 40250's MilesLib -// headers name. Playback goes through platform/, never through these declarations. +// Shim for Miles Sound System : the scalar types, opaque handles, constants and the AIL_* calls +// 40250's MilesLib makes. The calls are implemented by platform/MilesLib/MilesAil.cpp, a small software +// mixer the host drains into its SDL audio stream (platform/MilesLib/MilesMixer.h). #include "../../Win32Types.h" typedef int32_t S32; @@ -24,3 +25,123 @@ typedef _DIG_DRIVER* HDIGDRIVER; typedef void* H3DPOBJECT; typedef H3DPOBJECT H3DSAMPLE; typedef U32 HPROVIDER; +typedef U32 HPROENUM; +typedef S32 M3DRESULT; + +#define HPROENUM_FIRST 0 +#define M3D_NOERR 0 + +// Sample/stream status (AIL_sample_status, AIL_3D_sample_status, AIL_stream_status). +#define SMP_FREE 0x0001 +#define SMP_DONE 0x0002 +#define SMP_PLAYING 0x0004 +#define SMP_STOPPED 0x0008 +#define SMP_PLAYINGBUTRELEASED 0x0010 + +// AIL_file_type results. +#define AILFILETYPE_UNKNOWN 0 +#define AILFILETYPE_PCM_WAV 1 +#define AILFILETYPE_ADPCM_WAV 2 +#define AILFILETYPE_OTHER_WAV 3 +#define AILFILETYPE_VOC 4 +#define AILFILETYPE_MIDI 5 +#define AILFILETYPE_XMIDI 6 +#define AILFILETYPE_XMIDI_DLS 7 +#define AILFILETYPE_XMIDI_MLS 8 +#define AILFILETYPE_DLS 9 +#define AILFILETYPE_MLS 10 +#define AILFILETYPE_MPEG_L1_AUDIO 11 +#define AILFILETYPE_MPEG_L2_AUDIO 12 +#define AILFILETYPE_MPEG_L3_AUDIO 13 + +// AIL_file_read(filename, FILE_READ_WITH_SIZE): the returned block starts with the file size as a U32. +#define FILE_READ_WITH_SIZE ((void*)(intptr_t)-1) + +#define AIL_FILE_SEEK_BEGIN 0 +#define AIL_FILE_SEEK_CURRENT 1 +#define AIL_FILE_SEEK_END 2 + +typedef struct _AILSOUNDINFO +{ + S32 format; + void const* data_ptr; + U32 data_len; + U32 rate; + S32 bits; + S32 channels; + U32 samples; + U32 block_size; + void const* initial_ptr; +} AILSOUNDINFO; + +typedef U32 (AILCALLBACK* AIL_file_open_callback)(char const* filename, U32* file_handle); +typedef void (AILCALLBACK* AIL_file_close_callback)(U32 file_handle); +typedef S32 (AILCALLBACK* AIL_file_seek_callback)(U32 file_handle, S32 offset, U32 type); +typedef U32 (AILCALLBACK* AIL_file_read_callback)(U32 file_handle, void* buffer, U32 bytes); +typedef S32 (AILCALLBACK* AILLENGTHYCB)(U32 state, uintptr_t user); + +// System +S32 AIL_startup(); +void AIL_shutdown(); +char* AIL_set_redist_directory(char const* dir); +char* AIL_last_error(); +HDIGDRIVER AIL_open_digital_driver(U32 frequency, S32 bits, S32 channel, U32 flags); +void AIL_close_digital_driver(HDIGDRIVER dig); + +// Files and memory +void AIL_set_file_callbacks(AIL_file_open_callback opencb, AIL_file_close_callback closecb, + AIL_file_seek_callback seekcb, AIL_file_read_callback readcb); +void* AIL_file_read(char const* filename, void* dest); +S32 AIL_file_type(void const* data, U32 size); +S32 AIL_WAV_info(void const* data, AILSOUNDINFO* info); +S32 AIL_decompress_ADPCM(AILSOUNDINFO const* info, void** outdata, U32* outsize); +S32 AIL_decompress_ASI(void const* indata, U32 insize, char const* filename_ext, void** outdata, U32* outsize, + AILLENGTHYCB callback); +void AIL_mem_free_lock(void* ptr); + +// 2D samples +HSAMPLE AIL_allocate_sample_handle(HDIGDRIVER dig); +void AIL_release_sample_handle(HSAMPLE s); +void AIL_init_sample(HSAMPLE s); +S32 AIL_set_sample_file(HSAMPLE s, void const* file_image, S32 block); +U32 AIL_sample_status(HSAMPLE s); +void AIL_set_sample_loop_count(HSAMPLE s, S32 loop_count); +void AIL_start_sample(HSAMPLE s); +void AIL_stop_sample(HSAMPLE s); +void AIL_resume_sample(HSAMPLE s); +void AIL_end_sample(HSAMPLE s); +void AIL_sample_volume_pan(HSAMPLE s, F32* volume, F32* pan); +void AIL_set_sample_volume_pan(HSAMPLE s, F32 volume, F32 pan); + +// 3D providers, listener and samples +S32 AIL_enumerate_3D_providers(HPROENUM* next, HPROVIDER* dest, char** name); +M3DRESULT AIL_open_3D_provider(HPROVIDER lib); +void AIL_close_3D_provider(HPROVIDER lib); +H3DPOBJECT AIL_open_3D_listener(HPROVIDER lib); +void AIL_close_3D_listener(H3DPOBJECT listener); +H3DSAMPLE AIL_allocate_3D_sample_handle(HPROVIDER lib); +void AIL_release_3D_sample_handle(H3DSAMPLE s); +S32 AIL_set_3D_sample_file(H3DSAMPLE s, void const* file_image); +U32 AIL_3D_sample_status(H3DSAMPLE s); +void AIL_set_3D_sample_loop_count(H3DSAMPLE s, U32 loops); +void AIL_start_3D_sample(H3DSAMPLE s); +void AIL_stop_3D_sample(H3DSAMPLE s); +void AIL_resume_3D_sample(H3DSAMPLE s); +void AIL_end_3D_sample(H3DSAMPLE s); +F32 AIL_3D_sample_volume(H3DSAMPLE s); +void AIL_set_3D_sample_volume(H3DSAMPLE s, F32 volume); +void AIL_set_3D_position(H3DPOBJECT obj, F32 X, F32 Y, F32 Z); +void AIL_set_3D_orientation(H3DPOBJECT obj, F32 X_face, F32 Y_face, F32 Z_face, F32 X_up, F32 Y_up, F32 Z_up); +void AIL_set_3D_velocity(H3DPOBJECT obj, F32 dx_per_ms, F32 dy_per_ms, F32 dz_per_ms, F32 magnitude); +void AIL_auto_update_3D_position(H3DPOBJECT obj, S32 enable); +void AIL_update_3D_position(H3DPOBJECT obj, F32 dt_milliseconds); + +// Streams +HSTREAM AIL_open_stream(HDIGDRIVER dig, char const* filename, S32 stream_mem); +void AIL_close_stream(HSTREAM stream); +S32 AIL_stream_status(HSTREAM stream); +void AIL_set_stream_loop_count(HSTREAM stream, S32 count); +void AIL_start_stream(HSTREAM stream); +void AIL_pause_stream(HSTREAM stream, S32 onoff); +void AIL_stream_volume_levels(HSTREAM stream, F32* left_level, F32* right_level); +void AIL_set_stream_volume_levels(HSTREAM stream, F32 left_level, F32 right_level); diff --git a/tests/port/port_miles_test.cpp b/tests/port/port_miles_test.cpp new file mode 100644 index 00000000..8ddc83ff --- /dev/null +++ b/tests/port/port_miles_test.cpp @@ -0,0 +1,360 @@ +// MilesLib port: the 40250 CSoundManager/CSoundManager2D/3D/Stream code over the Miles emulation in +// platform/MilesLib/MilesAil.cpp, observed through the mix. Sound files are written to a temporary +// directory and read by path (no pack callbacks); a fake decoder turns them into constant clips. +#include "MilesLib/Stdafx.h" +#include "MilesLib/SoundManager.h" +#include "MilesLib/Type.h" +#include "EterBase/Timer.h" +#include "../../src/platform/MilesLib/MilesMixer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int g_failures = 0; +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + std::fprintf(stderr, "%s:%d: CHECK(%s)\n", __FILE__, __LINE__, #cond); \ + ++g_failures; \ + } \ + } while (0) +#define CHECK_NEAR(a, b) CHECK(std::fabs(double(a) - double(b)) < 1e-3) + +namespace +{ +// A file is "": RIFF..WAVE with a PCM fmt chunk, or ID3 for an mp3. The +// fake decoder returns `frames` stereo frames of `value`. +constexpr float kWavValue = 0.125f; +constexpr float kMp3Value = 0.25f; + +void put_u32(std::vector& out, uint32_t v) +{ + for (int i = 0; i < 4; ++i) + out.push_back((unsigned char)(v >> (8 * i))); +} + +void put_u16(std::vector& out, uint16_t v) +{ + out.push_back((unsigned char)v); + out.push_back((unsigned char)(v >> 8)); +} + +std::string write_file(const std::string& dir, const char* name, const std::vector& bytes) +{ + const std::string path = dir + "/" + name; + FILE* file = std::fopen(path.c_str(), "wb"); + std::fwrite(bytes.data(), 1, bytes.size(), file); + std::fclose(file); + return path; +} + +std::string write_wav(const std::string& dir, const char* name, uint32_t frames) +{ + std::vector data; + put_u32(data, frames); + uint32_t value_bits; + std::memcpy(&value_bits, &kWavValue, 4); + put_u32(data, value_bits); + + std::vector out = {'R', 'I', 'F', 'F'}; + put_u32(out, uint32_t(4 + 8 + 16 + 8 + data.size())); + out.insert(out.end(), {'W', 'A', 'V', 'E', 'f', 'm', 't', ' '}); + put_u32(out, 16); + put_u16(out, 1); // PCM + put_u16(out, 2); + put_u32(out, 44100); + put_u32(out, 44100 * 4); + put_u16(out, 4); + put_u16(out, 16); + out.insert(out.end(), {'d', 'a', 't', 'a'}); + put_u32(out, uint32_t(data.size())); + out.insert(out.end(), data.begin(), data.end()); + return write_file(dir, name, out); +} + +std::string write_mp3(const std::string& dir, const char* name, uint32_t frames) +{ + std::vector out = {'I', 'D', '3'}; + put_u32(out, frames); + return write_file(dir, name, out); +} + +bool fake_decoder(const void* data, std::size_t size, std::vector& out) +{ + const unsigned char* p = static_cast(data); + uint32_t frames; + float value; + if (size >= 7 && std::memcmp(p, "ID3", 3) == 0) + { + std::memcpy(&frames, p + 3, 4); + value = kMp3Value; + } + else if (size >= 52 && std::memcmp(p, "RIFF", 4) == 0) + { + std::memcpy(&frames, p + 44, 4); + std::memcpy(&value, p + 48, 4); + } + else + return false; + out.assign(std::size_t(frames) * 2, value); + return true; +} + +struct Mix +{ + float left = 0.0f; + float right = 0.0f; +}; + +// The first frame of a `frames`-frame mix. +Mix mix(std::size_t frames = 1) +{ + std::vector out(frames * 2, 0.0f); + MtMilesMix(out.data(), frames); + return {out[0], out[1]}; +} + +void volume_conversion(CSoundManager& manager) +{ + manager.SetSoundVolumeRatio(0.05f); // below 0.1: as is + CHECK_NEAR(manager.GetSoundVolume(), 0.05f); + manager.SetSoundVolumeRatio(0.5f); // 10^(ratio - 1) + CHECK_NEAR(manager.GetSoundVolume(), std::pow(10.0f, -0.5f)); + manager.SetSoundVolumeGrade(5); + CHECK_NEAR(manager.GetSoundVolume(), 1.0f); + manager.SetSoundVolume(2.0f); + CHECK_NEAR(manager.GetSoundVolume(), 1.0f); +} + +void sound_2d(CSoundManager& manager, const std::string& wav) +{ + manager.SetSoundVolume(1.0f); + // Four instances: a fifth sound while all play is dropped. + for (int i = 0; i < 5; ++i) + manager.PlaySound2D(wav.c_str()); + Mix m = mix(); + CHECK_NEAR(m.left, 4 * kWavValue); + CHECK_NEAR(m.right, 4 * kWavValue); + + // One play of 100 frames: the rest of the first 150 is silent and the instances are done. + std::vector out(150 * 2, 0.0f); + MtMilesMix(out.data(), 150); + CHECK_NEAR(out[98 * 2], 4 * kWavValue); + CHECK_NEAR(out[99 * 2], 0.0f); + CHECK_NEAR(mix().left, 0.0f); + + manager.PlaySound2D(wav.c_str()); + CHECK_NEAR(mix().left, kWavValue); + mix(200); + + // Volume 0: nothing starts. + manager.SetSoundVolume(0.0f); + manager.PlaySound2D(wav.c_str()); + CHECK_NEAR(mix().left, 0.0f); + manager.SetSoundVolume(1.0f); +} + +void sound_3d(CSoundManager& manager, const std::string& wav, const std::string& mp3) +{ + manager.SetPosition(1000.0f, 2000.0f, 0.0f); + // A right-handed camera looking down -z with +y up has +x on its right. + manager.SetDirection(0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f); + manager.Update(); + + // At the listener: full volume on both sides. + manager.PlaySound3D(1000.0f, 2000.0f, 0.0f, wav.c_str(), 1); + Mix m = mix(); + CHECK_NEAR(m.left, kWavValue); + CHECK_NEAR(m.right, kWavValue); + manager.StopAllSound3D(); + CHECK_NEAR(mix().left, 0.0f); + + // 10 Miles units (x 200 sound scale) to the right: 1/10, right channel only. + manager.PlaySound3D(1000.0f + 2000.0f, 2000.0f, 0.0f, wav.c_str(), 1); + m = mix(); + CHECK_NEAR(m.left, 0.0f); + CHECK_NEAR(m.right, 0.1f * kWavValue); + manager.StopAllSound3D(); + + // Beyond the maximum distance: silent. + manager.PlaySound3D(1000.0f - 200.0f * 250.0f, 2000.0f, 0.0f, wav.c_str(), 1); + m = mix(); + CHECK_NEAR(m.left, 0.0f); + CHECK_NEAR(m.right, 0.0f); + manager.StopAllSound3D(); + + // An mp3 decompresses to a WAV image; loop count 0 plays forever. + manager.PlaySound3D(1000.0f, 2000.0f, 0.0f, mp3.c_str(), 0); + mix(1000); + CHECK_NEAR(mix().left, kMp3Value); + manager.StopAllSound3D(); + CHECK_NEAR(mix().left, 0.0f); + + // Loop count 2: two plays, then done. + manager.PlaySound3D(1000.0f, 2000.0f, 0.0f, wav.c_str(), 2); + std::vector out(250 * 2, 0.0f); + MtMilesMix(out.data(), 250); + CHECK_NEAR(out[150 * 2], kWavValue); + CHECK_NEAR(out[199 * 2], kWavValue); + CHECK_NEAR(out[200 * 2], 0.0f); + manager.StopAllSound3D(); + + // Character sounds: one per file within 0.3 s, none beyond 5000 units. + CTimer::Instance().Advance(); + manager.PlayCharacterSound3D(1000.0f, 2000.0f, 0.0f, wav.c_str(), TRUE); + manager.PlayCharacterSound3D(1000.0f, 2000.0f, 0.0f, wav.c_str(), TRUE); + CHECK_NEAR(mix().left, kWavValue); + manager.StopAllSound3D(); // also clears the history + manager.PlayCharacterSound3D(1000.0f, 2000.0f, 0.0f, wav.c_str(), FALSE); + manager.PlayCharacterSound3D(1000.0f, 2000.0f, 0.0f, wav.c_str(), FALSE); + CHECK_NEAR(mix().left, 2 * kWavValue); + manager.StopAllSound3D(); + manager.PlayCharacterSound3D(1000.0f + 5001.0f, 2000.0f, 0.0f, wav.c_str(), TRUE); + CHECK_NEAR(mix().right, 0.0f); + CHECK(manager.PlayAmbienceSound3D(1000.0f, 2000.0f, 0.0f, wav.c_str(), 1) >= 0); + CHECK_NEAR(mix().left, kWavValue); + manager.StopAllSound3D(); + + // UpdateSoundInstance plays the entries of the current frame. + NSound::TSoundInstanceVector instances(2); + instances[0].dwFrame = 3; + instances[0].strSoundFileName = wav; + instances[1].dwFrame = 4; + instances[1].strSoundFileName = wav; + manager.UpdateSoundInstance(1000.0f, 2000.0f, 0.0f, 3, &instances, FALSE); + CHECK_NEAR(mix().left, kWavValue); + manager.StopAllSound3D(); +} + +// The stream decodes on a worker thread: mix until the level is `expected` (or give up after 1 s). +float wait_for_stream(float expected) +{ + float level = 0.0f; + for (int i = 0; i < 200; ++i) + { + level = mix().left; + if (std::fabs(level - expected) < 1e-3f) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + return level; +} + +void music(CSoundManager& manager, const std::string& song, const std::string& other) +{ + manager.SetMusicVolume(1.0f); + manager.FadeInMusic(song.c_str(), 0.25f); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + CHECK_NEAR(mix().left, 0.0f); // starts at volume 0 + manager.Update(); + CHECK_NEAR(wait_for_stream(0.25f * kMp3Value), 0.25f * kMp3Value); + for (int i = 0; i < 5; ++i) + manager.Update(); + CHECK_NEAR(mix().left, kMp3Value); // clamped at the music volume + + manager.SetMusicVolume(0.5f); + CHECK_NEAR(mix().left, 0.5f * kMp3Value); + + // Down to a limit (a ratio, converted), then playing on. + manager.FadeLimitOutMusic(song.c_str(), 0.0f, 0.2f); + manager.Update(); + CHECK_NEAR(mix().left, 0.3f * kMp3Value); + manager.Update(); + manager.Update(); + CHECK_NEAR(mix().left, 0.0f); + + // A second song fades the first one out (0.01 per update) and fades in in the next slot. + manager.SetMusicVolume(1.0f); + CHECK_NEAR(mix().left, kMp3Value); + manager.FadeInMusic(other.c_str(), 0.5f); + manager.Update(); + CHECK_NEAR(wait_for_stream((0.99f + 0.5f) * kMp3Value), (0.99f + 0.5f) * kMp3Value); + manager.Update(); + CHECK_NEAR(mix().left, (0.98f + 1.0f) * kMp3Value); + + // Fading out stops the stream at 0. + manager.FadeOutMusic(other.c_str(), 1.0f); + manager.Update(); + CHECK_NEAR(mix().left, 0.97f * kMp3Value); + for (int i = 0; i < 100; ++i) + manager.Update(); + CHECK_NEAR(mix().left, 0.0f); + + // Save/RestoreVolume (window deactivated/activated). + manager.PlayMusic(song.c_str()); + CHECK_NEAR(wait_for_stream(kMp3Value), kMp3Value); + manager.SaveVolume(); + CHECK_NEAR(mix().left, 0.0f); + CHECK_NEAR(manager.GetSoundVolume(), 0.0f); + manager.SetSoundVolume(0.7f); // stored for the restore + manager.RestoreVolume(); + CHECK_NEAR(mix().left, kMp3Value); + CHECK_NEAR(manager.GetSoundVolume(), 0.7f); + manager.FadeOutAllMusic(); + for (int i = 0; i < 100; ++i) + manager.Update(); +} + +void sound_information() +{ + NSound::TSoundDataVector data(2); + data[0].fTime = 0.5f; + data[0].strSoundFileName = "a.wav"; + data[1].fTime = 0.1f; + data[1].strSoundFileName = "b.wav"; + NSound::TSoundInstanceVector instances; + NSound::DataToInstance(data, &instances); + CHECK(instances.size() == 2); + // fTime / (1/60), truncated: 0.5 s is frame 29 in float. + CHECK(instances[0].dwFrame == 29 && instances[0].strSoundFileName == "a.wav"); + CHECK(instances[1].dwFrame == 6 && instances[1].strSoundFileName == "b.wav"); +} +} // namespace + +int main() +{ + char dir_template[] = "/tmp/port_miles_XXXXXX"; + const char* dir = mkdtemp(dir_template); + if (!dir) + return 1; + const std::string wav = write_wav(dir, "hit.wav", 100); + const std::string mp3 = write_mp3(dir, "step.mp3", 400); + const std::string song = write_mp3(dir, "song.mp3", 44100); + const std::string other = write_mp3(dir, "other.mp3", 44100); + + CTimer timer; + CSoundManager manager; + + // No decoder means no audio device: the Miles driver does not open (40250 with no sound card). + CHECK(AIL_open_digital_driver(44100, 16, 2, 0) == NULL); + + MtMilesSetDecoder(fake_decoder); + CHECK(manager.Create()); + volume_conversion(manager); + sound_2d(manager, wav); + sound_3d(manager, wav, mp3); + music(manager, song, other); + sound_information(); + manager.Destroy(); + MtMilesSetDecoder(nullptr); + + for (const char* name : {"hit.wav", "step.mp3", "song.mp3", "other.mp3"}) + unlink((std::string(dir) + "/" + name).c_str()); + rmdir(dir); + + if (g_failures) + { + std::fprintf(stderr, "port_miles_test: %d failure(s)\n", g_failures); + return 1; + } + std::printf("port_miles_test: ok\n"); + return 0; +}