Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
- name: Configure Project
env:
CCACHE_DIR: /tmp/ccache
run: cmake . --preset ${{ env.CMAKE_PRESET }} -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
run: cmake . --preset ${{ env.CMAKE_PRESET }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache

- name: Build Project
env:
Expand Down Expand Up @@ -132,7 +132,7 @@ jobs:
copy .\private\* .\MarathonRecompLib\private

- name: Configure Project
run: cmake . --preset ${{ env.CMAKE_PRESET }} -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
run: cmake . --preset ${{ env.CMAKE_PRESET }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache

- name: Build Project
run: cmake --build .\out\build\${{ env.CMAKE_PRESET }} --target MarathonRecomp
Expand Down Expand Up @@ -267,7 +267,7 @@ jobs:
- name: Configure Project
env:
CCACHE_DIR: /tmp/ccache
run: cmake . --preset ${{ env.CMAKE_PRESET }} -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
run: cmake . --preset ${{ env.CMAKE_PRESET }} -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache

- name: Build Project
env:
Expand Down
8 changes: 4 additions & 4 deletions MarathonRecomp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ set(MARATHON_RECOMP_APU_CXX_SOURCES
"apu/audio.cpp"
"apu/xma_decoder.cpp"
"apu/embedded_player.cpp"
"apu/driver/sdl2_driver.cpp"
"apu/driver/sdl3_driver.cpp"
)

set(MARATHON_RECOMP_HID_CXX_SOURCES
Expand Down Expand Up @@ -200,7 +200,7 @@ set(MARATHON_RECOMP_UTILS_CXX_SOURCES
)

set(MARATHON_RECOMP_THIRDPARTY_SOURCES
"${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/backends/imgui_impl_sdl2.cpp"
"${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/backends/imgui_impl_sdl3.cpp"
"${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/imgui.cpp"
"${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_demo.cpp"
"${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_draw.cpp"
Expand Down Expand Up @@ -383,8 +383,8 @@ target_link_libraries(MarathonRecomp PRIVATE
nfd::nfd
o1heap
XenonUtils
SDL2::SDL2-static
SDL2_mixer
SDL3::SDL3-static
SDL3_mixer::SDL3_mixer
tomlplusplus::tomlplusplus
MarathonRecompLib
xxHash::xxhash
Expand Down
2 changes: 1 addition & 1 deletion MarathonRecomp/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ PPC_FUNC(sub_825EA610)
if (std::this_thread::get_id() == g_mainThreadId)
{
SDL_PumpEvents();
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
SDL_FlushEvents(SDL_EVENT_FIRST, SDL_EVENT_LAST);

GameWindow::Update();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,46 @@

static PPCFunc* g_clientCallback{};
static uint32_t g_clientCallbackParam{}; // pointer in guest memory
static SDL_AudioDeviceID g_audioDevice{};
static SDL_AudioStream* g_audioStream{};
static bool g_downMixToStereo;

static void CreateAudioDevice()
{
if (g_audioDevice != NULL)
SDL_CloseAudioDevice(g_audioDevice);
if (g_audioStream != nullptr)
{
SDL_DestroyAudioStream(g_audioStream);
g_audioStream = nullptr;
}

bool surround = Config::ChannelConfiguration == EChannelConfiguration::Surround;
int allowedChanges = surround ? SDL_AUDIO_ALLOW_CHANNELS_CHANGE : 0;

SDL_AudioSpec desired{}, obtained{};
SDL_AudioSpec deviceSpec{};
if (surround && SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &deviceSpec, nullptr))
surround = deviceSpec.channels >= XAUDIO_NUM_CHANNELS;

SDL_AudioSpec desired{};
desired.freq = XAUDIO_SAMPLES_HZ;
desired.format = AUDIO_F32SYS;
desired.format = SDL_AUDIO_F32;
desired.channels = surround ? XAUDIO_NUM_CHANNELS : 2;
desired.samples = XAUDIO_NUM_SAMPLES;
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, allowedChanges);

if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS) // This check may fail only when surround sound is enabled.
{
SDL_CloseAudioDevice(g_audioDevice);
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0);
}
g_audioStream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &desired, nullptr, nullptr);

if (!g_audioDevice)
if (!g_audioStream)
LOGFN_ERROR("Failed to open audio device: {}", SDL_GetError());

g_downMixToStereo = (obtained.channels == 2);
g_downMixToStereo = (desired.channels == 2);
}

void XAudioInitializeSystem()
{
#ifdef _WIN32
// Force wasapi on Windows.
SDL_setenv("SDL_AUDIODRIVER", "wasapi", true);
SDL_SetHint(SDL_HINT_AUDIO_DRIVER, "wasapi");
#endif

SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Marathon Recompiled");
SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, "Marathon Recompiled");

if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
if (!SDL_InitSubSystem(SDL_INIT_AUDIO))
{
LOGFN_ERROR("Failed to init audio subsystem: {}", SDL_GetError());
return;
Expand All @@ -69,14 +68,17 @@ static void AudioThread()

while (!g_audioThreadShouldExit)
{
uint32_t queuedAudioSize = SDL_GetQueuedAudioSize(g_audioDevice);
constexpr size_t MAX_LATENCY = 4;
int queuedAudioSize = SDL_GetAudioStreamQueued(g_audioStream);
constexpr size_t MAX_LATENCY = 10;
const size_t callbackAudioSize = channels * XAUDIO_NUM_SAMPLES * sizeof(float);

for (size_t i = queuedAudioSize / callbackAudioSize; i <= MAX_LATENCY; i++)
if (queuedAudioSize >= 0)
{
ctx.ppcContext.r3.u32 = g_clientCallbackParam;
g_clientCallback(ctx.ppcContext, g_memory.base);
for (size_t i = queuedAudioSize / callbackAudioSize; i <= MAX_LATENCY; i++)
{
ctx.ppcContext.r3.u32 = g_clientCallbackParam;
g_clientCallback(ctx.ppcContext, g_memory.base);
}
}

auto now = std::chrono::steady_clock::now();
Expand All @@ -92,7 +94,7 @@ static void AudioThread()

static void CreateAudioThread()
{
SDL_PauseAudioDevice(g_audioDevice, 0);
SDL_ResumeAudioStreamDevice(g_audioStream);
g_audioThreadShouldExit = false;
g_audioThread = std::make_unique<std::thread>(AudioThread);
}
Expand Down Expand Up @@ -143,7 +145,7 @@ void XAudioSubmitFrame(void* samples)
audioFrames[i * 2 + 1] = isnan(samp1) ? 0.0f : samp1;
}

SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
SDL_PutAudioStreamData(g_audioStream, &audioFrames, sizeof(audioFrames));
}
else
{
Expand All @@ -158,7 +160,7 @@ void XAudioSubmitFrame(void* samples)
}
}

SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
SDL_PutAudioStreamData(g_audioStream, &audioFrames, sizeof(audioFrames));
}
}

Expand Down
76 changes: 51 additions & 25 deletions MarathonRecomp/apu/embedded_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ enum class EmbeddedSound

struct EmbeddedSoundData
{
Mix_Chunk* chunk{};
MIX_Audio* audio{};
};

static constexpr size_t EMBEDDED_TRACK_COUNT = 8;

static MIX_Mixer* g_mixer;
static std::array<MIX_Track*, EMBEDDED_TRACK_COUNT> g_tracks = {};

static std::array<EmbeddedSoundData, size_t(EmbeddedSound::Count)> g_embeddedSoundData = {};
static const std::unordered_map<std::string_view, EmbeddedSound> g_embeddedSoundMap =
{
Expand All @@ -40,12 +45,12 @@ static const std::unordered_map<std::string_view, EmbeddedSound> g_embeddedSound
{ "cannot_deside", EmbeddedSound::CannotDeside },
};

static size_t g_channelIndex;
static size_t g_trackIndex;

static void PlayEmbeddedSound(EmbeddedSound s)
{
EmbeddedSoundData &data = g_embeddedSoundData[size_t(s)];
if (data.chunk == nullptr)
if (data.audio == nullptr)
{
// The sound hasn't been created yet, create it and pick it.
const void *soundData = nullptr;
Expand Down Expand Up @@ -85,25 +90,41 @@ static void PlayEmbeddedSound(EmbeddedSound s)
return;
}

data.chunk = Mix_LoadWAV_RW(SDL_RWFromConstMem(soundData, soundDataSize), 1);
data.audio = MIX_LoadAudio_IO(g_mixer, SDL_IOFromConstMem(soundData, soundDataSize), true, true);
}

Mix_VolumeChunk(data.chunk, (Config::MasterVolume * Config::EffectsVolume * EmbeddedPlayer::EFFECTS_VOLUME) * MIX_MAX_VOLUME);
Mix_PlayChannel(g_channelIndex % MIX_CHANNELS, data.chunk, 0);
++g_channelIndex;

MIX_Track *track = g_tracks[g_trackIndex % EMBEDDED_TRACK_COUNT];
++g_trackIndex;

MIX_SetTrackGain(track, Config::MasterVolume * Config::EffectsVolume * EmbeddedPlayer::EFFECTS_VOLUME);
MIX_SetTrackAudio(track, data.audio);
MIX_PlayTrack(track, 0);
}

static Mix_Music* g_installerMusic;
static MIX_Audio* g_installerMusic;
static MIX_Track* g_musicTrack;

void EmbeddedPlayer::Init()
void EmbeddedPlayer::Init()
{
Mix_OpenAudio(XAUDIO_SAMPLES_HZ, AUDIO_F32SYS, 2, 4096);
g_installerMusic = Mix_LoadMUS_RW(SDL_RWFromConstMem(g_installer_music, sizeof(g_installer_music)), 1);
MIX_Init();

SDL_AudioSpec spec{};
spec.freq = XAUDIO_SAMPLES_HZ;
spec.format = SDL_AUDIO_F32;
spec.channels = 2;
g_mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec);

for (MIX_Track *&track : g_tracks)
track = MIX_CreateTrack(g_mixer);

g_installerMusic = MIX_LoadAudio_IO(g_mixer, SDL_IOFromConstMem(g_installer_music, sizeof(g_installer_music)), false, true);
g_musicTrack = MIX_CreateTrack(g_mixer);
MIX_SetTrackAudio(g_musicTrack, g_installerMusic);

s_isActive = true;
}

void EmbeddedPlayer::Play(const char *name)
void EmbeddedPlayer::Play(const char *name)
{
assert(s_isActive && "Playback shouldn't be requested if the Embedded Player isn't active.");

Expand All @@ -118,32 +139,37 @@ void EmbeddedPlayer::Play(const char *name)

void EmbeddedPlayer::PlayMusic()
{
if (!Mix_PlayingMusic())
if (!MIX_TrackPlaying(g_musicTrack))
{
Mix_PlayMusic(g_installerMusic, INT_MAX);
Mix_VolumeMusic(Config::MasterVolume * Config::MusicVolume * MUSIC_VOLUME * MIX_MAX_VOLUME);
SDL_PropertiesID options = SDL_CreateProperties();
SDL_SetNumberProperty(options, MIX_PROP_PLAY_LOOPS_NUMBER, -1);

MIX_SetTrackGain(g_musicTrack, Config::MasterVolume * Config::MusicVolume * MUSIC_VOLUME);
MIX_PlayTrack(g_musicTrack, options);

SDL_DestroyProperties(options);
}
}

void EmbeddedPlayer::FadeOutMusic()
{
if (Mix_PlayingMusic())
Mix_FadeOutMusic(1000);
if (MIX_TrackPlaying(g_musicTrack))
MIX_StopTrack(g_musicTrack, MIX_TrackMSToFrames(g_musicTrack, 1000));
}

void EmbeddedPlayer::Shutdown()
void EmbeddedPlayer::Shutdown()
{
MIX_DestroyMixer(g_mixer);

for (EmbeddedSoundData &data : g_embeddedSoundData)
{
if (data.chunk != nullptr)
Mix_FreeChunk(data.chunk);
if (data.audio != nullptr)
MIX_DestroyAudio(data.audio);
}

Mix_HaltMusic();
Mix_FreeMusic(g_installerMusic);
MIX_DestroyAudio(g_installerMusic);

Mix_CloseAudio();
Mix_Quit();
MIX_Quit();

s_isActive = false;
}
30 changes: 9 additions & 21 deletions MarathonRecomp/gpu/video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,19 +138,7 @@ namespace plume
#ifdef MARATHON_RECOMP_METAL
extern std::unique_ptr<RenderInterface> CreateMetalInterface();
#endif
#ifdef PLUME_SDL_VULKAN_ENABLED
extern std::unique_ptr<RenderInterface> CreateVulkanInterface(RenderWindow sdlWindow);
#else
extern std::unique_ptr<RenderInterface> CreateVulkanInterface();
#endif

static std::unique_ptr<RenderInterface> CreateVulkanInterfaceWrapper() {
#ifdef PLUME_SDL_VULKAN_ENABLED
return CreateVulkanInterface(GameWindow::s_renderWindow);
#else
return CreateVulkanInterface();
#endif
}
}

using namespace plume;
Expand Down Expand Up @@ -1697,7 +1685,7 @@ static void CreateImGuiBackend()
OptionsMenu::Init();
InstallerWizard::Init();

ImGui_ImplSDL2_InitForOther(GameWindow::s_pWindow);
ImGui_ImplSDL3_InitForOther(GameWindow::s_pWindow);

#ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT
g_imFontTexture = LoadTexture(
Expand Down Expand Up @@ -2039,9 +2027,9 @@ static bool TryCreateHostDevice(std::vector<RenderInterfaceFunction*>& interface
}
}

g_backend = (interfaceFunction == CreateVulkanInterfaceWrapper) ? Backend::VULKAN : Backend::D3D12;
g_backend = (interfaceFunction == CreateVulkanInterface) ? Backend::VULKAN : Backend::D3D12;
#elif defined(MARATHON_RECOMP_METAL)
g_backend = (interfaceFunction == CreateVulkanInterfaceWrapper) ? Backend::VULKAN : Backend::METAL;
g_backend = (interfaceFunction == CreateVulkanInterface) ? Backend::VULKAN : Backend::METAL;
#endif
// Enable triangle strip workaround if we are on AMD, as there is a bug where
// restart indices cause triangles to be culled incorrectly. Converting them to degenerate triangles fixes it.
Expand Down Expand Up @@ -2092,13 +2080,13 @@ bool Video::CreateHostDevice(const char* sdlVideoDriver, bool graphicsApiRetry)
std::vector<RenderInterfaceFunction*> interfaceFunctions;

#ifdef MARATHON_RECOMP_D3D12
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateVulkanInterfaceWrapper : CreateD3D12Interface);
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateD3D12Interface : CreateVulkanInterfaceWrapper);
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateVulkanInterface : CreateD3D12Interface);
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateD3D12Interface : CreateVulkanInterface);
#elif defined(MARATHON_RECOMP_METAL)
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateVulkanInterfaceWrapper : CreateMetalInterface);
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateMetalInterface : CreateVulkanInterfaceWrapper);
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateVulkanInterface : CreateMetalInterface);
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateMetalInterface : CreateVulkanInterface);
#else
interfaceFunctions.push_back(CreateVulkanInterfaceWrapper);
interfaceFunctions.push_back(CreateVulkanInterface);
#endif

if (!TryCreateHostDevice(interfaceFunctions, graphicsApiRetry))
Expand Down Expand Up @@ -3010,7 +2998,7 @@ static void DrawFPS()

static void DrawImGui()
{
ImGui_ImplSDL2_NewFrame();
ImGui_ImplSDL3_NewFrame();

auto& io = ImGui::GetIO();
io.DisplaySize = { float(Video::s_viewportWidth), float(Video::s_viewportHeight) };
Expand Down
Loading
Loading