From 892da25cf0dd1483a9fc02718ff6d58ad939896c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 10 Mar 2026 07:34:45 +0100 Subject: [PATCH 1/2] overlays: support playing audio with video_view --- .../Emu/RSX/Overlays/overlay_save_dialog.cpp | 7 ++++--- rpcs3/Emu/RSX/Overlays/overlay_video.cpp | 19 +++++++++---------- rpcs3/Emu/RSX/Overlays/overlay_video.h | 8 ++++---- rpcs3/rpcs3qt/qt_video_source.cpp | 14 ++++++++++++-- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.cpp index 604dee7fc4..2847db969c 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.cpp @@ -9,10 +9,11 @@ namespace rsx { save_dialog::save_dialog_entry::save_dialog_entry(const std::string& text1, const std::string& text2, const std::string& text3, u8 resource_id, const std::vector& icon_buf, const std::string& video_path) { + const std::string audio_path; // no audio here std::unique_ptr image = resource_id != image_resource_id::raw_image - ? std::make_unique(video_path, resource_id) - : !icon_buf.empty() ? std::make_unique(video_path, icon_buf) - : std::make_unique(video_path, resource_config::standard_image_resource::save); // Fallback + ? std::make_unique(video_path, audio_path, resource_id) + : !icon_buf.empty() ? std::make_unique(video_path, audio_path, icon_buf) + : std::make_unique(video_path, audio_path, resource_config::standard_image_resource::save); // Fallback image->set_size(160, 110); image->set_padding(36, 36, 11, 11); // Square image, 88x88 diff --git a/rpcs3/Emu/RSX/Overlays/overlay_video.cpp b/rpcs3/Emu/RSX/Overlays/overlay_video.cpp index ac6017d35f..44ca1f53ef 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_video.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_video.cpp @@ -6,9 +6,9 @@ namespace rsx { namespace overlays { - video_view::video_view(const std::string& video_path, const std::string& thumbnail_path) + video_view::video_view(const std::string& video_path, const std::string& audio_path, const std::string& thumbnail_path) { - init_video(video_path); + init_video(video_path, audio_path); if (!thumbnail_path.empty()) { @@ -17,9 +17,9 @@ namespace rsx } } - video_view::video_view(const std::string& video_path, const std::vector& thumbnail_buf) + video_view::video_view(const std::string& video_path, const std::string& audio_path, const std::vector& thumbnail_buf) { - init_video(video_path); + init_video(video_path, audio_path); if (!thumbnail_buf.empty()) { @@ -28,10 +28,10 @@ namespace rsx } } - video_view::video_view(const std::string& video_path, u8 thumbnail_id) + video_view::video_view(const std::string& video_path, const std::string& audio_path, u8 thumbnail_id) : m_thumbnail_id(thumbnail_id) { - init_video(video_path); + init_video(video_path, audio_path); set_image_resource(thumbnail_id); } @@ -39,13 +39,11 @@ namespace rsx { } - void video_view::init_video(const std::string& video_path) + void video_view::init_video(const std::string& video_path, const std::string& audio_path) { if (video_path.empty()) return; - m_video_source = Emu.GetCallbacks().make_video_source(); - ensure(!!m_video_source); - + m_video_source = ensure(Emu.GetCallbacks().make_video_source()); m_video_source->set_update_callback([this]() { if (m_video_active) @@ -54,6 +52,7 @@ namespace rsx } }); m_video_source->set_video_path(video_path); + m_video_source->set_audio_path(audio_path); } void video_view::set_active(bool active) diff --git a/rpcs3/Emu/RSX/Overlays/overlay_video.h b/rpcs3/Emu/RSX/Overlays/overlay_video.h index 92297dee42..ab148c508d 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_video.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_video.h @@ -19,9 +19,9 @@ namespace rsx class video_view final : public image_view { public: - video_view(const std::string& video_path, const std::string& thumbnail_path); - video_view(const std::string& video_path, const std::vector& thumbnail_buf); - video_view(const std::string& video_path, u8 thumbnail_id); + video_view(const std::string& video_path, const std::string& audio_path, const std::string& thumbnail_path); + video_view(const std::string& video_path, const std::string& audio_path, const std::vector& thumbnail_buf); + video_view(const std::string& video_path, const std::string& audio_path, u8 thumbnail_id); virtual ~video_view(); void set_active(bool active); @@ -30,7 +30,7 @@ namespace rsx compiled_resource& get_compiled() override; private: - void init_video(const std::string& video_path); + void init_video(const std::string& video_path, const std::string& audio_path); usz m_buffer_index = 0; std::array, 2> m_video_info; // double buffer diff --git a/rpcs3/rpcs3qt/qt_video_source.cpp b/rpcs3/rpcs3qt/qt_video_source.cpp index 2d4ce34aa3..8877e00c0c 100644 --- a/rpcs3/rpcs3qt/qt_video_source.cpp +++ b/rpcs3/rpcs3qt/qt_video_source.cpp @@ -335,7 +335,11 @@ void qt_video_source_wrapper::set_video_path(const std::string& video_path) { Emu.CallFromMainThread([this, path = video_path]() { - m_qt_video_source = std::make_unique(); + if (!m_qt_video_source) + { + m_qt_video_source = std::make_unique(); + } + m_qt_video_source->m_image_change_callback = [this](const QVideoFrame& frame) { std::unique_lock lock(m_qt_video_source->m_image_mutex); @@ -371,7 +375,12 @@ void qt_video_source_wrapper::set_audio_path(const std::string& audio_path) { Emu.CallFromMainThread([this, path = audio_path]() { - // TODO + if (!m_qt_video_source) + { + m_qt_video_source = std::make_unique(); + } + + m_qt_video_source->set_audio_path(path); }); } @@ -379,6 +388,7 @@ void qt_video_source_wrapper::set_active(bool active) { Emu.CallFromMainThread([this, active]() { + ensure(m_qt_video_source); m_qt_video_source->set_active(true); }); } From e5dee277adc9db8a52a2a0864c0a0c3cc0d0896a Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 10 Mar 2026 08:56:04 +0100 Subject: [PATCH 2/2] overlays: play SND0.AT3 during initialization --- rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/Emu/RSX/Overlays/overlay_audio.cpp | 30 ++++++++++++++++++++++ rpcs3/Emu/RSX/Overlays/overlay_audio.h | 23 +++++++++++++++++ rpcs3/Emu/RSX/Overlays/overlay_manager.cpp | 17 ++++++++++++ rpcs3/Emu/RSX/Overlays/overlay_manager.h | 6 +++++ rpcs3/Emu/RSX/RSXThread.cpp | 10 ++++++++ rpcs3/emucore.vcxproj | 4 ++- rpcs3/emucore.vcxproj.filters | 6 +++++ 8 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 rpcs3/Emu/RSX/Overlays/overlay_audio.cpp create mode 100644 rpcs3/Emu/RSX/Overlays/overlay_audio.h diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index edb98a6fa8..63951cfe92 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -500,6 +500,7 @@ target_sources(rpcs3_emu PRIVATE RSX/Overlays/overlays.cpp RSX/Overlays/overlay_animated_icon.cpp RSX/Overlays/overlay_animation.cpp + RSX/Overlays/overlay_audio.cpp RSX/Overlays/overlay_compile_notification.cpp RSX/Overlays/overlay_controls.cpp RSX/Overlays/overlay_cursor.cpp diff --git a/rpcs3/Emu/RSX/Overlays/overlay_audio.cpp b/rpcs3/Emu/RSX/Overlays/overlay_audio.cpp new file mode 100644 index 0000000000..5483a5242e --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/overlay_audio.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" +#include "overlay_audio.h" +#include "Emu/System.h" + +namespace rsx +{ + namespace overlays + { + audio_player::audio_player(const std::string& audio_path) + { + init_audio(audio_path); + } + + void audio_player::init_audio(const std::string& audio_path) + { + if (audio_path.empty()) return; + + m_video_source = ensure(Emu.GetCallbacks().make_video_source()); + m_video_source->set_audio_path(audio_path); + } + + void audio_player::set_active(bool active) + { + if (m_video_source) + { + m_video_source->set_active(active); + } + } + } +} diff --git a/rpcs3/Emu/RSX/Overlays/overlay_audio.h b/rpcs3/Emu/RSX/Overlays/overlay_audio.h new file mode 100644 index 0000000000..cb37569225 --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/overlay_audio.h @@ -0,0 +1,23 @@ +#pragma once + +#include "util/video_source.h" + +namespace rsx +{ + namespace overlays + { + class audio_player + { + public: + audio_player(const std::string& audio_path); + ~audio_player() = default; + + void set_active(bool active); + + private: + void init_audio(const std::string& audio_path); + + std::unique_ptr m_video_source; + }; + } +} diff --git a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp index 485a59443e..9ffa9b14e3 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp @@ -167,6 +167,23 @@ namespace rsx } } + void display_manager::start_audio(const std::string& audio_path) + { + if (audio_path.empty()) + { + m_audio_player.reset(); + return; + } + + m_audio_player = std::make_unique(audio_path); + m_audio_player->set_active(true); + } + + void display_manager::stop_audio() + { + m_audio_player.reset(); + } + void display_manager::on_overlay_activated(const std::shared_ptr& /*item*/) { // TODO: Internal management, callbacks, etc diff --git a/rpcs3/Emu/RSX/Overlays/overlay_manager.h b/rpcs3/Emu/RSX/Overlays/overlay_manager.h index 7146be3dda..e42f3721b3 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_manager.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_manager.h @@ -1,6 +1,7 @@ #pragma once #include "overlays.h" +#include "overlay_audio.h" #include "Emu/IdManager.h" #include "Utilities/mutex.h" @@ -25,6 +26,8 @@ namespace rsx lf_queue m_type_ids_to_remove; atomic_t m_pending_removals_count = 0; + std::unique_ptr m_audio_player; + bool remove_type(u32 type_id); bool remove_uid(u32 uid); @@ -167,6 +170,9 @@ namespace rsx std::function on_input_loop_exit = nullptr, // [optional] What to do with the result if any std::function input_loop_override = nullptr); // [optional] What to do during the input loop. By default calls user_interface::run_input_loop + void start_audio(const std::string& audio_path); + void stop_audio(); + private: struct overlay_input_thread { diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index a8aa7cdf60..7d634de677 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -711,6 +711,11 @@ namespace rsx if (g_cfg.misc.use_native_interface && (g_cfg.video.renderer == video_renderer::opengl || g_cfg.video.renderer == video_renderer::vulkan)) { m_overlay_manager = g_fxo->init(0); + + if (const std::string audio_path = Emu.GetSfoDir(true) + "/SND0.AT3"; fs::is_file(audio_path)) + { + m_overlay_manager->start_audio(audio_path); + } } if (!_ar) @@ -1101,6 +1106,11 @@ namespace rsx thread_ctrl::set_thread_affinity_mask(thread_ctrl::get_affinity_mask(thread_class::rsx)); } + if (auto manager = g_fxo->try_get()) + { + manager->stop_audio(); + } + while (!test_stopped()) { // Wait for external pause events diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 8df2ef3092..0e07226729 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -145,6 +145,7 @@ + @@ -704,6 +705,7 @@ + @@ -1103,4 +1105,4 @@ - + \ No newline at end of file diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 5ca602fbec..7dfd6a3f34 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1411,6 +1411,9 @@ Loader + + Emu\GPU\RSX\Overlays + @@ -2833,6 +2836,9 @@ Loader + + Emu\GPU\RSX\Overlays +