mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-29 23:41:19 -06:00
Trophy: Replace sdl mixer library with minimp3 header for playing trophy sounds (#4261)
* Replace sdl mixer library with minimp3 header * clang * set spec before putting in audiostream * respect main audio output device setting * fixup * replace file with submodule * cleanup * capitalize functions like the others * move buffer to heap * use vector for pcm buffer instead
This commit is contained in:
parent
f242655fbb
commit
72f514f350
8
.gitmodules
vendored
8
.gitmodules
vendored
@ -106,10 +106,6 @@
|
||||
[submodule "externals/json"]
|
||||
path = externals/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "externals/sdl3_mixer"]
|
||||
path = externals/sdl3_mixer
|
||||
url = https://github.com/libsdl-org/SDL_mixer
|
||||
shallow = true
|
||||
[submodule "externals/miniz"]
|
||||
path = externals/miniz
|
||||
url = https://github.com/richgel999/miniz
|
||||
@ -136,3 +132,7 @@
|
||||
path = externals/libusb
|
||||
url = https://github.com/shadexternals/libusb.git
|
||||
branch = shadps4
|
||||
[submodule "externals/minimp3"]
|
||||
path = externals/minimp3
|
||||
url = https://github.com/lieff/minimp3
|
||||
shallow = true
|
||||
|
||||
@ -233,11 +233,8 @@ find_package(nlohmann_json 3.12 CONFIG)
|
||||
find_package(PNG 1.6 MODULE)
|
||||
find_package(OpenAL CONFIG)
|
||||
find_package(RenderDoc 1.6.0 MODULE)
|
||||
find_package(SDL3_mixer 2.8.1 CONFIG)
|
||||
find_package(SDL3_image CONFIG)
|
||||
if (SDL3_mixer_FOUND)
|
||||
find_package(SDL3 3.1.2 CONFIG)
|
||||
endif()
|
||||
find_package(SDL3 3.1.2 CONFIG)
|
||||
find_package(stb MODULE)
|
||||
find_package(toml11 4.2.0 CONFIG)
|
||||
find_package(tsl-robin-map 1.3.0 CONFIG)
|
||||
@ -1146,8 +1143,8 @@ add_executable(shadps4
|
||||
|
||||
create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer pugixml::pugixml)
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG minimp3)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_image::SDL3_image pugixml::pugixml)
|
||||
target_link_libraries(shadps4 PRIVATE stb::headers lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
|
||||
15
externals/CMakeLists.txt
vendored
15
externals/CMakeLists.txt
vendored
@ -86,18 +86,6 @@ if (NOT TARGET SDL3_image::SDL3_image)
|
||||
add_subdirectory(sdl3_image)
|
||||
endif()
|
||||
|
||||
# SDL3_mixer
|
||||
if (NOT TARGET SDL3_mixer::SDL3_mixer)
|
||||
set(SDLMIXER_FLAC OFF)
|
||||
set(SDLMIXER_OGG OFF)
|
||||
set(SDLMIXER_MOD OFF)
|
||||
set(SDLMIXER_MIDI OFF)
|
||||
set(SDLMIXER_OPUS OFF)
|
||||
set(SDLMIXER_WAVPACK OFF)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
add_subdirectory(sdl3_mixer)
|
||||
endif()
|
||||
|
||||
# vulkan-headers
|
||||
if (NOT TARGET Vulkan::Headers)
|
||||
set(VULKAN_HEADERS_ENABLE_MODULE OFF)
|
||||
@ -311,6 +299,9 @@ if (NOT TARGET CLI11::CLI11)
|
||||
add_subdirectory(CLI11)
|
||||
endif()
|
||||
|
||||
# minimp3
|
||||
add_library(minimp3 INTERFACE)
|
||||
target_include_directories(minimp3 INTERFACE minimp3)
|
||||
|
||||
#openal
|
||||
if (NOT TARGET OpenAL::OpenAL)
|
||||
|
||||
1
externals/minimp3
vendored
Submodule
1
externals/minimp3
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 7b590fdcfa5a79c033e76eacc05d0c3e4c79f536
|
||||
1
externals/sdl3_mixer
vendored
1
externals/sdl3_mixer
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 4182794ea45fe28568728670c6f1583855d0e85c
|
||||
@ -1,15 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <cmrc/cmrc.hpp>
|
||||
#include <imgui.h>
|
||||
#include "common/assert.h"
|
||||
#include <queue>
|
||||
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#include <minimp3.h>
|
||||
|
||||
#include "common/path_util.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/np/trophy_ui.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
@ -22,9 +25,7 @@ namespace Libraries::Np::NpTrophy {
|
||||
std::optional<TrophyUI> current_trophy_ui;
|
||||
std::queue<TrophyInfo> trophy_queue;
|
||||
std::mutex queueMtx;
|
||||
|
||||
std::string side = "right";
|
||||
|
||||
double trophy_timer;
|
||||
|
||||
TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::string& trophyName,
|
||||
@ -32,7 +33,6 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin
|
||||
: trophy_name(trophyName), trophy_type(rarity) {
|
||||
|
||||
side = EmulatorSettings.GetTrophyNotificationSide();
|
||||
|
||||
trophy_timer = EmulatorSettings.GetTrophyNotificationDuration();
|
||||
|
||||
if (std::filesystem::exists(trophyIconPath)) {
|
||||
@ -86,44 +86,86 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin
|
||||
|
||||
AddLayer(this);
|
||||
|
||||
MIX_Init();
|
||||
mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL);
|
||||
if (!mixer) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Could not initialize SDL Mixer, {}", SDL_GetError());
|
||||
if (SDL_WasInit(SDL_INIT_AUDIO) != 0) {
|
||||
if (!SDL_Init(SDL_INIT_AUDIO)) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Unable to init SDL Audio for trophy sound: {}",
|
||||
SDL_GetError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
audioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nullptr);
|
||||
|
||||
// user selected Sdl Backend, use same device as Sdl main Device
|
||||
if (EmulatorSettings.GetAudioBackend() == 0) {
|
||||
if (EmulatorSettings.GetSDLMainOutputDevice() != "Default Device") {
|
||||
int count;
|
||||
SDL_AudioDeviceID* devices = SDL_GetAudioPlaybackDevices(&count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
std::string name = SDL_GetAudioDeviceName(devices[i]);
|
||||
if (name == EmulatorSettings.GetSDLMainOutputDevice()) {
|
||||
audioDevice = SDL_OpenAudioDevice(devices[i], NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// user selected OpenAl Backend, use same device as OpenAl main Device
|
||||
} else if (EmulatorSettings.GetAudioBackend() == 1) {
|
||||
if (EmulatorSettings.GetOpenALMainOutputDevice() != "Default Device") {
|
||||
int count;
|
||||
SDL_AudioDeviceID* devices = SDL_GetAudioPlaybackDevices(&count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
std::string name = SDL_GetAudioDeviceName(devices[i]);
|
||||
// Device names are the same for openAl/Sdl, just with an added prefix
|
||||
name.erase(0, 15);
|
||||
if (name == EmulatorSettings.GetOpenALMainOutputDevice()) {
|
||||
audioDevice = SDL_OpenAudioDevice(devices[i], NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audioDevice == 0) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Unable to open audio device for trophy sound playback: {}",
|
||||
SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
MIX_SetMasterGain(mixer, static_cast<float>(EmulatorSettings.GetVolumeSlider() / 100.f));
|
||||
auto musicPathMp3 = CustomTrophy_Dir / "trophy.mp3";
|
||||
auto musicPathWav = CustomTrophy_Dir / "trophy.wav";
|
||||
const auto musicPathMp3 = CustomTrophy_Dir / "trophy.mp3";
|
||||
const auto musicPathWav = CustomTrophy_Dir / "trophy.wav";
|
||||
std::vector<unsigned char> sound_data;
|
||||
|
||||
if (std::filesystem::exists(musicPathMp3)) {
|
||||
audio = MIX_LoadAudio(mixer, musicPathMp3.string().c_str(), false);
|
||||
std::ifstream file(musicPathMp3, std::ios::binary);
|
||||
sound_data = std::vector<unsigned char>((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
PlayMp3(sound_data);
|
||||
} else if (std::filesystem::exists(musicPathWav)) {
|
||||
audio = MIX_LoadAudio(mixer, musicPathWav.string().c_str(), false);
|
||||
std::ifstream file(musicPathWav, std::ios::binary);
|
||||
sound_data = std::vector<unsigned char>((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
PlayWav(sound_data);
|
||||
} else {
|
||||
auto soundFile = resource.open("src/images/trophy.wav");
|
||||
std::vector<u8> soundData = std::vector<u8>(soundFile.begin(), soundFile.end());
|
||||
audio =
|
||||
MIX_LoadAudio_IO(mixer, SDL_IOFromMem(soundData.data(), soundData.size()), false, true);
|
||||
// due to low volume of default sound file
|
||||
MIX_SetMasterGain(mixer, MIX_GetMasterGain(mixer) * 1.3f);
|
||||
}
|
||||
|
||||
if (!audio) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Could not loud audio file, {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MIX_PlayAudio(mixer, audio)) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Could not play audio file, {}", SDL_GetError());
|
||||
sound_data = std::vector<unsigned char>(soundFile.begin(), soundFile.end());
|
||||
PlayWav(sound_data);
|
||||
}
|
||||
}
|
||||
|
||||
TrophyUI::~TrophyUI() {
|
||||
MIX_DestroyAudio(audio);
|
||||
MIX_DestroyMixer(mixer);
|
||||
MIX_Quit();
|
||||
if (stream) {
|
||||
SDL_DestroyAudioStream(stream);
|
||||
}
|
||||
|
||||
// if emulator is not using sdl audio backend
|
||||
if (EmulatorSettings.GetAudioBackend() != 0) {
|
||||
SDL_CloseAudioDevice(audioDevice);
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
}
|
||||
|
||||
Finish();
|
||||
}
|
||||
@ -275,6 +317,64 @@ void TrophyUI::Draw() {
|
||||
}
|
||||
}
|
||||
|
||||
void TrophyUI::PlayMp3(std::vector<unsigned char> mp3Data) {
|
||||
mp3dec_t mp3d;
|
||||
mp3dec_frame_info_t info;
|
||||
std::vector<short> pcm(MINIMP3_MAX_SAMPLES_PER_FRAME);
|
||||
mp3dec_init(&mp3d);
|
||||
|
||||
// always s16 when decoded by minimp3, channels/frequency changed later on as necessary
|
||||
SDL_AudioSpec spec = {SDL_AUDIO_S16, 2, 44100};
|
||||
bool specInfoSet = false;
|
||||
|
||||
stream = SDL_CreateAudioStream(&spec, &spec);
|
||||
SDL_BindAudioStream(audioDevice, stream);
|
||||
|
||||
// make this louder than game stream
|
||||
SDL_SetAudioStreamGain(stream,
|
||||
static_cast<float>(EmulatorSettings.GetVolumeSlider() * 0.01f * 1.2f));
|
||||
unsigned char* buffer_ptr = mp3Data.data();
|
||||
size_t remaining_size = mp3Data.size();
|
||||
|
||||
while (remaining_size > 0) {
|
||||
int samples = mp3dec_decode_frame(&mp3d, buffer_ptr, remaining_size, pcm.data(), &info);
|
||||
if (samples > 0) {
|
||||
if (!specInfoSet && info.hz > 0 && info.channels > 0) {
|
||||
spec = {SDL_AUDIO_S16, info.channels, info.hz};
|
||||
SDL_SetAudioStreamFormat(stream, &spec, &spec);
|
||||
specInfoSet = true;
|
||||
}
|
||||
|
||||
SDL_PutAudioStreamData(stream, pcm.data(), samples * 2 * sizeof(short));
|
||||
buffer_ptr += info.frame_bytes;
|
||||
remaining_size -= info.frame_bytes;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrophyUI::PlayWav(std::vector<unsigned char> wavData) {
|
||||
SDL_AudioSpec spec;
|
||||
Uint8* audioBuf = nullptr;
|
||||
Uint32 audioLen = 0;
|
||||
|
||||
SDL_IOStream* io = SDL_IOFromConstMem(wavData.data(), wavData.size());
|
||||
if (!SDL_LoadWAV_IO(io, true, &spec, &audioBuf, &audioLen)) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Unable to load trophy wave file data: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_AudioStream* stream = SDL_CreateAudioStream(&spec, &spec);
|
||||
SDL_BindAudioStream(audioDevice, stream);
|
||||
|
||||
// make this louder than game stream
|
||||
SDL_SetAudioStreamGain(stream,
|
||||
static_cast<float>(EmulatorSettings.GetVolumeSlider() * 0.01f * 1.2f));
|
||||
SDL_PutAudioStreamData(stream, audioBuf, audioLen);
|
||||
SDL_free(audioBuf);
|
||||
}
|
||||
|
||||
void AddTrophyToQueue(const std::filesystem::path& trophyIconPath, const std::string& trophyName,
|
||||
const std::string_view& rarity) {
|
||||
std::lock_guard<std::mutex> lock(queueMtx);
|
||||
|
||||
@ -4,13 +4,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include <queue>
|
||||
#include <SDL3/SDL_audio.h>
|
||||
|
||||
#include "common/fixed_value.h"
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/np/np_trophy.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
#include "imgui/imgui_texture.h"
|
||||
|
||||
@ -27,13 +22,15 @@ public:
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
void PlayMp3(std::vector<unsigned char> mp3Data);
|
||||
void PlayWav(std::vector<unsigned char> wavData);
|
||||
|
||||
std::string trophy_name;
|
||||
std::string_view trophy_type;
|
||||
ImGui::RefCountedTexture trophy_icon;
|
||||
ImGui::RefCountedTexture trophy_type_icon;
|
||||
|
||||
MIX_Mixer* mixer;
|
||||
MIX_Audio* audio;
|
||||
SDL_AudioStream* stream;
|
||||
SDL_AudioDeviceID audioDevice;
|
||||
};
|
||||
|
||||
struct TrophyInfo {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user