Embedded player implementation.

This commit is contained in:
Dario 2024-12-08 19:39:23 -03:00
parent e19e18b14d
commit e714c69f1d
7 changed files with 194 additions and 24 deletions

View file

@ -95,6 +95,7 @@ set(SWA_GPU_CXX_SOURCES
set(SWA_APU_CXX_SOURCES
"apu/audio.cpp"
"apu/embedded_player.cpp"
)
if (SWA_XAUDIO2)
@ -399,3 +400,5 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction.dds" ARRAY_NAME "g_xbox_color_correction" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_NAME "g_game_icon")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_cursor.wav" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_cursor.wav" ARRAY_NAME "g_sys_worldmap_cursor")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_finaldecide.wav" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_finaldecide.wav" ARRAY_NAME "g_sys_worldmap_finaldecide")

View file

@ -1,2 +1,3 @@
#pragma once
#include <apu/audio.h>

View file

@ -0,0 +1,142 @@
#include <miniaudio.h>
#include <apu/embedded_player.h>
#include <user/config.h>
#include <res/sounds/sys_worldmap_cursor.wav.h>
#include <res/sounds/sys_worldmap_finaldecide.wav.h>
enum EmbeddedSound
{
EmbeddedSoundSysWorldMapCursor,
EmbeddedSoundSysWorldMapFinalDecide,
EmbeddedSoundCount,
};
static ma_engine g_audioEngine = {};
static bool g_audioEngineInitialized = false;
static std::array<ma_sound, EmbeddedSoundCount> g_embeddedSoundCache = {};
static std::array<ma_decoder, EmbeddedSoundCount> g_embeddedDecoderCache = {};
static const std::unordered_map<std::string, EmbeddedSound> g_embeddedSoundMap =
{
{ "sys_worldmap_cursor", EmbeddedSoundSysWorldMapCursor },
{ "sys_worldmap_finaldecide", EmbeddedSoundSysWorldMapFinalDecide },
};
void InitEmbeddedSound(EmbeddedSound s)
{
ma_sound &sound = g_embeddedSoundCache[s];
ma_decoder &decoder = g_embeddedDecoderCache[s];
const void *soundData = nullptr;
size_t soundDataSize = 0;
float volume = Config::EffectsVolume;
switch (s)
{
case EmbeddedSoundSysWorldMapCursor:
soundData = g_sys_worldmap_cursor;
soundDataSize = sizeof(g_sys_worldmap_cursor);
volume *= 0.259f;
break;
case EmbeddedSoundSysWorldMapFinalDecide:
soundData = g_sys_worldmap_finaldecide;
soundDataSize = sizeof(g_sys_worldmap_finaldecide);
volume *= 0.61f;
break;
default:
assert(false && "Unknown embedded sound.");
return;
}
if (soundData != nullptr)
{
ma_result res;
res = ma_decoder_init_memory(soundData, soundDataSize, nullptr, &decoder);
if (res != MA_SUCCESS)
{
fprintf(stderr, "ma_decoder_init_memory failed with error code %d on embedded sound %d.\n", res, s);
return;
}
res = ma_sound_init_from_data_source(&g_audioEngine, &decoder, MA_SOUND_FLAG_DECODE, nullptr, &sound);
if (res != MA_SUCCESS)
{
fprintf(stderr, "ma_sound_init_from_data_source failed with error code %d on embedded sound %d.\n", res, s);
return;
}
ma_sound_set_volume(&sound, volume);
}
}
void EmbeddedPlayer::Init()
{
s_isActive = true;
}
void EmbeddedPlayer::Play(const char *name)
{
assert(s_isActive && "Playback shouldn't be requested if the Embedded Player isn't active.");
auto it = g_embeddedSoundMap.find(name);
if (it == g_embeddedSoundMap.end())
{
return;
}
if (!g_audioEngineInitialized)
{
ma_engine_config engineConfig = ma_engine_config_init();
ma_result res = ma_engine_init(&engineConfig, &g_audioEngine);
if (res != MA_SUCCESS)
{
fprintf(stderr, "ma_engine_init failed with error code %d.\n", res);
}
// Do not attempt to initialize this again if it fails.
g_audioEngineInitialized = true;
}
if (g_audioEngine.pDevice == nullptr)
{
return;
}
ma_sound &sound = g_embeddedSoundCache[it->second];
if (sound.pDataSource == nullptr)
{
InitEmbeddedSound(it->second);
}
if (sound.pDataSource != nullptr)
{
ma_sound_seek_to_pcm_frame(&sound, 0);
ma_sound_start(&sound);
}
}
void EmbeddedPlayer::Shutdown()
{
for (ma_sound &sound : g_embeddedSoundCache)
{
if (sound.pDataSource != nullptr)
{
ma_sound_uninit(&sound);
}
}
for (ma_decoder &decoder : g_embeddedDecoderCache)
{
if (decoder.pBackend != nullptr)
{
ma_decoder_uninit(&decoder);
}
}
if (g_audioEngine.pDevice != nullptr)
{
ma_engine_uninit(&g_audioEngine);
}
g_audioEngineInitialized = false;
s_isActive = false;
}

View file

@ -0,0 +1,10 @@
#pragma once
struct EmbeddedPlayer
{
inline static bool s_isActive = false;
static void Init();
static void Play(const char *name);
static void Shutdown();
};

View file

@ -1,3 +1,4 @@
#include <apu/embedded_player.h>
#include <kernel/function.h>
#include <kernel/heap.h>
#include <kernel/memory.h>
@ -8,21 +9,24 @@
SWA_API void Game_PlaySound(const char* pName)
{
// TODO: use own sound player.
if (InstallerWizard::s_isVisible)
return;
if (EmbeddedPlayer::s_isActive)
{
EmbeddedPlayer::Play(pName);
}
else
{
guest_stack_var<boost::anonymous_shared_ptr> soundPlayer;
GuestToHostFunction<void>(sub_82B4DF50, soundPlayer.get(), ((be<uint32_t>*)g_memory.Translate(0x83367900))->get(), 7, 0, 0);
guest_stack_var<boost::anonymous_shared_ptr> soundPlayer;
GuestToHostFunction<void>(sub_82B4DF50, soundPlayer.get(), ((be<uint32_t>*)g_memory.Translate(0x83367900))->get(), 7, 0, 0);
auto soundPlayerVtable = (be<uint32_t>*)g_memory.Translate(*(be<uint32_t>*)soundPlayer->get());
uint32_t virtualFunction = *(soundPlayerVtable + 1);
auto soundPlayerVtable = (be<uint32_t>*)g_memory.Translate(*(be<uint32_t>*)soundPlayer->get());
uint32_t virtualFunction = *(soundPlayerVtable + 1);
size_t strLen = strlen(pName);
void* strAllocation = g_userHeap.Alloc(strLen + 1);
memcpy(strAllocation, pName, strLen + 1);
GuestToHostFunction<void>(virtualFunction, soundPlayer->get(), strAllocation, 0);
g_userHeap.Free(strAllocation);
size_t strLen = strlen(pName);
void *strAllocation = g_userHeap.Alloc(strLen + 1);
memcpy(strAllocation, pName, strLen + 1);
GuestToHostFunction<void>(virtualFunction, soundPlayer->get(), strAllocation, 0);
g_userHeap.Free(strAllocation);
}
}
SWA_API void Window_SetFullscreen(bool isEnabled)

View file

@ -2,6 +2,7 @@
#include <nfd.h>
#include <apu/embedded_player.h>
#include <install/installer.h>
#include <gpu/video.h>
#include <gpu/imgui_snapshot.h>
@ -152,6 +153,7 @@ public:
return;
}
int newCursorIndex = -1;
ImVec2 tapDirection = {};
switch (event->type)
{
@ -168,7 +170,7 @@ public:
break;
case SDL_SCANCODE_RETURN:
case SDL_SCANCODE_KP_ENTER:
g_currentCursorAccepted = true;
g_currentCursorAccepted = (g_currentCursorIndex >= 0);
break;
}
@ -189,7 +191,7 @@ public:
tapDirection = { 0.0f, 1.0f };
break;
case SDL_CONTROLLER_BUTTON_A:
g_currentCursorAccepted = true;
g_currentCursorAccepted = (g_currentCursorIndex >= 0);
break;
}
@ -215,14 +217,12 @@ public:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEMOTION:
{
g_currentCursorIndex = -1;
for (size_t i = 0; i < g_currentCursorRects.size(); i++)
{
auto &currentRect = g_currentCursorRects[i];
if (ImGui::IsMouseHoveringRect(currentRect.first, currentRect.second, false))
{
g_currentCursorIndex = int(i);
newCursorIndex = int(i);
if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_LEFT)
{
@ -233,13 +233,17 @@ public:
}
}
if (newCursorIndex < 0)
{
g_currentCursorIndex = -1;
}
break;
}
}
if (tapDirection.x != 0.0f || tapDirection.y != 0.0f)
{
int newCursorIndex = -1;
if (g_currentCursorIndex >= g_currentCursorRects.size() || g_currentCursorIndex < 0)
{
newCursorIndex = g_currentCursorDefault;
@ -278,13 +282,16 @@ public:
}
}
}
}
if (newCursorIndex >= 0)
if (newCursorIndex >= 0)
{
if (g_currentCursorIndex != newCursorIndex)
{
// TODO: Play sound.
g_currentCursorIndex = newCursorIndex;
Game_PlaySound("sys_worldmap_cursor");
}
g_currentCursorIndex = newCursorIndex;
}
}
};
@ -388,6 +395,7 @@ static bool PushCursorRect(ImVec2 min, ImVec2 max, bool &cursorPressed, bool mak
{
if (g_currentCursorAccepted)
{
Game_PlaySound("sys_worldmap_finaldecide");
cursorPressed = true;
g_currentCursorAccepted = false;
}
@ -1411,6 +1419,7 @@ void InstallerWizard::Shutdown()
bool InstallerWizard::Run(bool skipGame)
{
EmbeddedPlayer::Init();
NFD_Init();
// Guarantee one controller is initialized. We'll rely on SDL's event loop to get the controller events.
@ -1443,6 +1452,7 @@ bool InstallerWizard::Run(bool skipGame)
NFD_Quit();
InstallerWizard::Shutdown();
EmbeddedPlayer::Shutdown();
return true;
}

@ -1 +1 @@
Subproject commit 3081bfaec87550e3a085f1ac4048c3b637b5481d
Subproject commit f5352d15813026a1cdf44f67723f3c07567c9229