Files
Racoon-MinecraftConsoles/Shims/AudioShim/AudioShim.cpp
veroxsity a2187762fd Add initial project structure and audio shims for LCE Xbox port
- Added Wide310x150Logo.png asset for application branding.
- Created LCE.Xbox project file with configurations for Debug and Release.
- Added Package.appxmanifest for application metadata and capabilities.
- Introduced SharedSurface.h for shared memory management between processes.
- Implemented AudioShim with XAudio2 integration for audio playback.
- Developed XSBParser and XWBParser for handling sound bank files.
- Created DXGIShim to intercept DXGI calls and manage swap chains.
- Defined project files for AudioShim and DXGIShim with necessary dependencies.
2026-04-19 01:38:48 +01:00

206 lines
6.0 KiB
C++

// AudioShim.cpp
// Implements a minimal XACT3-compatible interface backed by XAudio2.
// Built as xactengine3_7.dll placed alongside Minecraft.Client.exe.
// The game loads XACT dynamically via CoCreateInstance / LoadLibrary.
#include "AudioShim.h"
#include "XWBParser.h"
#include "XSBParser.h"
#include <xaudio2.h>
#include <wrl/client.h>
#include <unordered_map>
#include <string>
#include <vector>
#include <mutex>
using Microsoft::WRL::ComPtr;
// ---------------------------------------------------------------------------
// Global XAudio2 state
// ---------------------------------------------------------------------------
static ComPtr<IXAudio2> g_xaudio;
static IXAudio2MasteringVoice* g_masterVoice = nullptr;
static std::mutex g_mutex;
// Loaded banks: filename stem → parser
static std::unordered_map<std::string, XWBParser> g_waveBanks;
static std::unordered_map<std::string, XSBParser> g_soundBanks;
// Active source voices keyed by cue name (one voice per cue, simplification)
static std::unordered_map<std::string, IXAudio2SourceVoice*> g_activeVoices;
static bool g_initialised = false;
static bool EnsureXAudio2()
{
if (g_initialised) return true;
std::lock_guard<std::mutex> lock(g_mutex);
if (g_initialised) return true;
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) return false;
hr = XAudio2Create(g_xaudio.GetAddressOf(), 0, XAUDIO2_DEFAULT_PROCESSOR);
if (FAILED(hr)) return false;
hr = g_xaudio->CreateMasteringVoice(&g_masterVoice);
if (FAILED(hr)) return false;
g_initialised = true;
return true;
}
// ---------------------------------------------------------------------------
// Play a sound by cue name — called from the public API below
// ---------------------------------------------------------------------------
static void PlayCue(const std::string& bankName, const std::string& cueName, bool loop)
{
if (!EnsureXAudio2()) return;
auto sbIt = g_soundBanks.find(bankName);
if (sbIt == g_soundBanks.end()) return;
const XSBCue* cue = sbIt->second.FindCue(cueName);
if (!cue) return;
// Find the wave bank — LCE uses matching stem names (minecraft.xsb → resident.xwb)
const XWBSound* sound = nullptr;
for (auto& [wbName, wb] : g_waveBanks)
{
sound = wb.GetSound(cue->waveIndex);
if (sound) break;
}
if (!sound || sound->data.empty()) return;
// Stop existing voice for this cue if any
{
std::lock_guard<std::mutex> lock(g_mutex);
auto it = g_activeVoices.find(cueName);
if (it != g_activeVoices.end())
{
it->second->Stop();
it->second->DestroyVoice();
g_activeVoices.erase(it);
}
}
WAVEFORMATEX fmt = {};
fmt.wFormatTag = sound->isADPCM ? WAVE_FORMAT_ADPCM : WAVE_FORMAT_PCM;
fmt.nChannels = sound->channels;
fmt.nSamplesPerSec = sound->sampleRate;
fmt.wBitsPerSample = sound->bitsPerSample;
fmt.nBlockAlign = sound->blockAlign;
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
fmt.cbSize = 0;
IXAudio2SourceVoice* voice = nullptr;
if (FAILED(g_xaudio->CreateSourceVoice(&voice, &fmt))) return;
XAUDIO2_BUFFER buf = {};
buf.pAudioData = sound->data.data();
buf.AudioBytes = static_cast<UINT32>(sound->data.size());
buf.Flags = XAUDIO2_END_OF_STREAM;
buf.LoopCount = loop ? XAUDIO2_LOOP_INFINITE : 0;
if (FAILED(voice->SubmitSourceBuffer(&buf))) { voice->DestroyVoice(); return; }
voice->SetVolume(cue->volume);
voice->Start(0);
std::lock_guard<std::mutex> lock(g_mutex);
g_activeVoices[cueName] = voice;
}
static void StopCue(const std::string& cueName)
{
std::lock_guard<std::mutex> lock(g_mutex);
auto it = g_activeVoices.find(cueName);
if (it != g_activeVoices.end())
{
it->second->Stop();
it->second->DestroyVoice();
g_activeVoices.erase(it);
}
}
// ---------------------------------------------------------------------------
// Public C API — exported from xactengine3_7.dll
// The game calls these via the XACT3 COM interface; we expose a flat C API
// that the game can call after we've intercepted the XACT3 factory.
// ---------------------------------------------------------------------------
extern "C"
{
// Called by the game to initialise the audio engine
__declspec(dllexport) HRESULT LCEAudio_Init()
{
return EnsureXAudio2() ? S_OK : E_FAIL;
}
// Load a wave bank file (.xwb)
__declspec(dllexport) HRESULT LCEAudio_LoadWaveBank(const wchar_t* path, const char* name)
{
EnsureXAudio2();
XWBParser parser;
if (!parser.Load(path)) return E_FAIL;
g_waveBanks[name] = std::move(parser);
return S_OK;
}
// Load a sound bank file (.xsb)
__declspec(dllexport) HRESULT LCEAudio_LoadSoundBank(const wchar_t* path, const char* name)
{
EnsureXAudio2();
XSBParser parser;
if (!parser.Load(path)) return E_FAIL;
g_soundBanks[name] = std::move(parser);
return S_OK;
}
// Play a cue by name
__declspec(dllexport) void LCEAudio_PlayCue(const char* bankName, const char* cueName, BOOL loop)
{
PlayCue(bankName, cueName, loop != 0);
}
// Stop a cue
__declspec(dllexport) void LCEAudio_StopCue(const char* cueName)
{
StopCue(cueName);
}
// Set master volume (0.0 - 1.0)
__declspec(dllexport) void LCEAudio_SetMasterVolume(float vol)
{
if (g_masterVoice) g_masterVoice->SetVolume(vol);
}
// Shutdown
__declspec(dllexport) void LCEAudio_Shutdown()
{
std::lock_guard<std::mutex> lock(g_mutex);
for (auto& [name, voice] : g_activeVoices)
{
voice->Stop();
voice->DestroyVoice();
}
g_activeVoices.clear();
if (g_masterVoice) { g_masterVoice->DestroyVoice(); g_masterVoice = nullptr; }
g_xaudio.Reset();
g_initialised = false;
}
} // extern "C"
BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
EnsureXAudio2();
else if (reason == DLL_PROCESS_DETACH)
LCEAudio_Shutdown();
return TRUE;
}