mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2026-04-27 12:51:42 +00:00
Embedded player implementation.
This commit is contained in:
parent
e19e18b14d
commit
e714c69f1d
7 changed files with 194 additions and 24 deletions
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#include <apu/audio.h>
|
||||
|
|
|
|||
142
UnleashedRecomp/apu/embedded_player.cpp
Normal file
142
UnleashedRecomp/apu/embedded_player.cpp
Normal 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;
|
||||
}
|
||||
10
UnleashedRecomp/apu/embedded_player.h
Normal file
10
UnleashedRecomp/apu/embedded_player.h
Normal 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();
|
||||
};
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 ¤tRect = 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
|
||||
Loading…
Add table
Reference in a new issue