Merge branch 'master' into vk-rework

This commit is contained in:
kd-11 2025-04-23 14:58:40 +03:00 committed by GitHub
commit 1cdd1ec77b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 370 additions and 32 deletions

View file

@ -1732,7 +1732,7 @@ void patch_engine::save_config(const patch_map& patches_map)
fs::pending_file file(path);
if (!file.file || (file.file.write(out.c_str(), out.size()), !file.commit()))
if (!file.file || file.file.write(out.c_str(), out.size() < out.size() || !file.commit()))
{
patch_log.error("Failed to create patch config file %s (error=%s)", path, fs::g_tls_error);
}

View file

@ -177,6 +177,11 @@ struct music_state
return CELL_MUSIC_ERROR_NO_MORE_CONTENT;
}
if (!fs::is_file(path))
{
cellMusic.error("set_playback_command: File does not exist: '%s'", path);
}
switch (command)
{
case CELL_MUSIC_PB_CMD_FASTFORWARD:

View file

@ -77,7 +77,7 @@ std::string music_selection_context::get_yaml_path() const
if (!fs::create_path(path))
{
cellMusicSelectionContext.fatal("Failed to create path: %s (%s)", path, fs::g_tls_error);
cellMusicSelectionContext.fatal("get_yaml_path: Failed to create path: %s (%s)", path, fs::g_tls_error);
}
return path + hash + ".yml";
@ -101,13 +101,18 @@ void music_selection_context::set_playlist(const std::string& path)
continue;
}
playlist.push_back(dir_path + std::string(path + "/" + dir_entry.name).substr(vfs_dir_path.length()));
std::string track = dir_path + std::string(path + "/" + dir_entry.name).substr(vfs_dir_path.length());
cellMusicSelectionContext.notice("set_playlist: Adding track to playlist: '%s'. (path: '%s', name: '%s')", track, path, dir_entry.name);
playlist.push_back(std::move(track));
}
}
else
{
content_type = CELL_SEARCH_CONTENTTYPE_MUSIC;
playlist.push_back(dir_path + path.substr(vfs_dir_path.length()));
std::string track = dir_path + path.substr(vfs_dir_path.length());
cellMusicSelectionContext.notice("set_playlist: Adding track to playlist: '%s'. (path: '%s')", track, path);
playlist.push_back(std::move(track));
}
valid = true;
@ -118,7 +123,7 @@ void music_selection_context::create_playlist(const std::string& new_hash)
hash = new_hash;
const std::string yaml_path = get_yaml_path();
cellMusicSelectionContext.notice("Saving music playlist file %s", yaml_path);
cellMusicSelectionContext.notice("create_playlist: Saving music playlist file %s", yaml_path);
YAML::Emitter out;
out << YAML::BeginMap;
@ -140,9 +145,9 @@ void music_selection_context::create_playlist(const std::string& new_hash)
fs::pending_file file(yaml_path);
if (!file.file || (file.file.write(out.c_str(), out.size()), !file.commit()))
if (!file.file || file.file.write(out.c_str(), out.size() < out.size() || !file.commit()))
{
cellMusicSelectionContext.error("Failed to create music playlist file %s (error=%s)", yaml_path, fs::g_tls_error);
cellMusicSelectionContext.error("create_playlist: Failed to create music playlist file '%s' (error=%s)", yaml_path, fs::g_tls_error);
}
}
@ -151,7 +156,7 @@ bool music_selection_context::load_playlist()
playlist.clear();
const std::string path = get_yaml_path();
cellMusicSelectionContext.notice("Loading music playlist file %s", path);
cellMusicSelectionContext.notice("load_playlist: Loading music playlist file '%s'", path);
std::string content;
{
@ -160,7 +165,7 @@ bool music_selection_context::load_playlist()
if (!file)
{
cellMusicSelectionContext.error("Failed to load music playlist file %s: %s", path, fs::g_tls_error);
cellMusicSelectionContext.error("load_playlist: Failed to load music playlist file '%s': %s", path, fs::g_tls_error);
return false;
}
@ -171,7 +176,7 @@ bool music_selection_context::load_playlist()
if (!error.empty() || !root)
{
cellMusicSelectionContext.error("Failed to load music playlist file %s:\n%s", path, error);
cellMusicSelectionContext.error("load_playlist: Failed to load music playlist file '%s':\n%s", path, error);
return false;
}
@ -180,54 +185,54 @@ bool music_selection_context::load_playlist()
const std::string version = get_yaml_node_value<std::string>(root["Version"], err);
if (!err.empty())
{
cellMusicSelectionContext.error("No Version entry found. Error: '%s' (file: %s)", err, path);
cellMusicSelectionContext.error("load_playlist: No Version entry found. Error: '%s' (file: '%s')", err, path);
return false;
}
if (version != target_version)
{
cellMusicSelectionContext.error("Version '%s' does not match music playlist target '%s' (file: %s)", version, target_version, path);
cellMusicSelectionContext.error("load_playlist: Version '%s' does not match music playlist target '%s' (file: '%s')", version, target_version, path);
return false;
}
const std::string file_type = get_yaml_node_value<std::string>(root["FileType"], err);
if (!err.empty())
{
cellMusicSelectionContext.error("No FileType entry found. Error: '%s' (file: %s)", err, path);
cellMusicSelectionContext.error("load_playlist: No FileType entry found. Error: '%s' (file: '%s')", err, path);
return false;
}
if (file_type != target_file_type)
{
cellMusicSelectionContext.error("FileType '%s' does not match music playlist target '%s' (file: %s)", file_type, target_file_type, path);
cellMusicSelectionContext.error("load_playlist: FileType '%s' does not match music playlist target '%s' (file: '%s')", file_type, target_file_type, path);
return false;
}
content_type = static_cast<CellSearchContentType>(get_yaml_node_value<u32>(root["ContentType"], err));
if (!err.empty())
{
cellMusicSelectionContext.error("No ContentType entry found. Error: '%s' (file: %s)", err, path);
cellMusicSelectionContext.error("load_playlist: No ContentType entry found. Error: '%s' (file: '%s')", err, path);
return false;
}
context_option = static_cast<CellSearchContextOption>(get_yaml_node_value<u32>(root["ContextOption"], err));
if (!err.empty())
{
cellMusicSelectionContext.error("No ContextOption entry found. Error: '%s' (file: %s)", err, path);
cellMusicSelectionContext.error("load_playlist: No ContextOption entry found. Error: '%s' (file: '%s')", err, path);
return false;
}
repeat_mode = static_cast<CellSearchRepeatMode>(get_yaml_node_value<u32>(root["RepeatMode"], err));
if (!err.empty())
{
cellMusicSelectionContext.error("No RepeatMode entry found. Error: '%s' (file: %s)", err, path);
cellMusicSelectionContext.error("load_playlist: No RepeatMode entry found. Error: '%s' (file: '%s')", err, path);
return false;
}
first_track = get_yaml_node_value<u32>(root["FirstTrack"], err);
if (!err.empty())
{
cellMusicSelectionContext.error("No FirstTrack entry found. Error: '%s' (file: %s)", err, path);
cellMusicSelectionContext.error("load_playlist: No FirstTrack entry found. Error: '%s' (file: '%s')", err, path);
return false;
}
@ -235,15 +240,17 @@ bool music_selection_context::load_playlist()
if (!track_node || track_node.Type() != YAML::NodeType::Sequence)
{
cellMusicSelectionContext.error("No Tracks entry found or Tracks is not a Sequence. (file: %s)", path);
cellMusicSelectionContext.error("load_playlist: No Tracks entry found or Tracks is not a Sequence. (file: '%s')", path);
return false;
}
for (usz i = 0; i < track_node.size(); i++)
{
cellMusicSelectionContext.notice("load_playlist: Adding track to playlist: '%s'. (file: '%s')", track_node[i].Scalar(), path);
playlist.push_back(track_node[i].Scalar());
}
cellMusicSelectionContext.notice("load_playlist: Loaded music playlist file '%s' (context: %s)", path, to_string());
valid = true;
return true;
}
@ -254,13 +261,13 @@ void music_selection_context::set_track(std::string_view track)
if (playlist.empty())
{
cellMusicSelectionContext.error("No tracks to play... (requested path='%s')", track);
cellMusicSelectionContext.error("set_track: No tracks to play... (requested path='%s')", track);
return;
}
for (usz i = 0; i < playlist.size(); i++)
{
cellMusicSelectionContext.error("Comparing track '%s' vs '%s'", track, playlist[i]);
cellMusicSelectionContext.error("set_track: Comparing track '%s' vs '%s'", track, playlist[i]);
if (track.ends_with(playlist[i]))
{
first_track = current_track = static_cast<u32>(i);
@ -268,14 +275,14 @@ void music_selection_context::set_track(std::string_view track)
}
}
cellMusicSelectionContext.error("Track '%s' not found...", track);
cellMusicSelectionContext.error("set_track: Track '%s' not found...", track);
}
u32 music_selection_context::step_track(bool next)
{
if (playlist.empty())
{
cellMusicSelectionContext.error("No tracks to play...");
cellMusicSelectionContext.error("step_track: No tracks to play...");
current_track = umax;
return umax;
}
@ -290,7 +297,7 @@ u32 music_selection_context::step_track(bool next)
if (++current_track >= playlist.size())
{
// We are at the end of the playlist.
cellMusicSelectionContext.notice("No more tracks to play in playlist...");
cellMusicSelectionContext.notice("step_track: No more tracks to play in playlist...");
current_track = umax;
return umax;
}
@ -301,7 +308,7 @@ u32 music_selection_context::step_track(bool next)
if (current_track == 0)
{
// We are at the start of the playlist.
cellMusicSelectionContext.notice("No more tracks to play in playlist...");
cellMusicSelectionContext.notice("step_track: No more tracks to play in playlist...");
current_track = umax;
return umax;
}
@ -339,13 +346,13 @@ u32 music_selection_context::step_track(bool next)
case CELL_SEARCH_REPEATMODE_NOREPEAT1:
{
// We are done. We only wanted to decode a single track.
cellMusicSelectionContext.notice("No more tracks to play...");
cellMusicSelectionContext.notice("step_track: No more tracks to play...");
current_track = umax;
return umax;
}
default:
{
fmt::throw_exception("Unknown repeat mode %d", static_cast<u32>(repeat_mode));
fmt::throw_exception("step_track: Unknown repeat mode %d", static_cast<u32>(repeat_mode));
}
}
@ -354,7 +361,7 @@ u32 music_selection_context::step_track(bool next)
if (next ? current_track == 0 : current_track == (playlist.size() - 1))
{
// We reached the first or last track again. Let's shuffle!
cellMusicSelectionContext.notice("Shuffling playlist...");
cellMusicSelectionContext.notice("step_track: Shuffling playlist...");
auto engine = std::default_random_engine{};
std::shuffle(std::begin(playlist), std::end(playlist), engine);
}

View file

@ -328,6 +328,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_main_window.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_music_player_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_memory_viewer_panel.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -610,6 +613,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_main_window.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_music_player_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_memory_viewer_panel.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -865,6 +871,7 @@
<ClCompile Include="rpcs3qt\kernel_explorer.cpp" />
<ClCompile Include="rpcs3qt\log_frame.cpp" />
<ClCompile Include="rpcs3qt\main_window.cpp" />
<ClCompile Include="rpcs3qt\music_player_dialog.cpp" />
<ClCompile Include="rpcs3qt\memory_string_searcher.cpp" />
<ClCompile Include="rpcs3qt\memory_viewer_panel.cpp" />
<ClCompile Include="rpcs3qt\msg_dialog_frame.cpp" />
@ -930,6 +937,16 @@
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<CustomBuild Include="rpcs3qt\music_player_dialog.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing music_player_dialog.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing music_player_dialog.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<CustomBuild Include="rpcs3qt\pad_settings_dialog.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing pad_settings_dialog.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
@ -1067,6 +1084,7 @@
<ClInclude Include="QTGeneratedFiles\ui_camera_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_ps_move_tracker_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_main_window.h" />
<ClInclude Include="QTGeneratedFiles\ui_music_player_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_pad_led_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_pad_motion_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_pad_settings_dialog.h" />
@ -1937,6 +1955,18 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="rpcs3qt\music_player_dialog.ui">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="rpcs3qt\pad_settings_dialog.ui">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>

View file

@ -273,6 +273,12 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_main_window.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_music_player_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_music_player_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_pad_settings_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
@ -435,6 +441,9 @@
<ClCompile Include="rpcs3qt\main_window.cpp">
<Filter>Gui</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\music_player_dialog.cpp">
<Filter>Gui\misc dialogs</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_syntax_highlighter.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
@ -1202,6 +1211,9 @@
<ClInclude Include="QTGeneratedFiles\ui_main_window.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="QTGeneratedFiles\ui_music_player_dialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="QTGeneratedFiles\ui_pad_settings_dialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
@ -1423,6 +1435,9 @@
<CustomBuild Include="rpcs3qt\main_window.ui">
<Filter>Form Files</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\music_player_dialog.ui">
<Filter>Form Files</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\pad_settings_dialog.ui">
<Filter>Form Files</Filter>
</CustomBuild>
@ -1480,6 +1495,9 @@
<CustomBuild Include="rpcs3qt\main_window.h">
<Filter>Gui</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\music_player_dialog.h">
<Filter>Gui\misc dialogs</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\kernel_explorer.h">
<Filter>Gui\dev tools</Filter>
</CustomBuild>

View file

@ -57,6 +57,7 @@ add_library(rpcs3_ui STATIC
movie_item.cpp
movie_item_base.cpp
msg_dialog_frame.cpp
music_player_dialog.cpp
osk_dialog_frame.cpp
pad_led_settings_dialog.cpp
pad_motion_settings_dialog.cpp
@ -119,6 +120,7 @@ add_library(rpcs3_ui STATIC
about_dialog.ui
camera_settings_dialog.ui
main_window.ui
music_player_dialog.ui
pad_led_settings_dialog.ui
pad_motion_settings_dialog.ui
pad_settings_dialog.ui

View file

@ -41,6 +41,7 @@
#include "basic_mouse_settings_dialog.h"
#include "vfs_tool_dialog.h"
#include "welcome_dialog.h"
#include "music_player_dialog.h"
#include <thread>
#include <unordered_set>
@ -3143,6 +3144,12 @@ void main_window::CreateConnects()
dlg->show();
});
connect(ui->actionMusic_Player, &QAction::triggered, this, [this]()
{
music_player_dialog* dlg = new music_player_dialog(this);
dlg->open();
});
connect(ui->showDebuggerAct, &QAction::triggered, this, [this](bool checked)
{
checked ? m_debugger_frame->show() : m_debugger_frame->hide();

View file

@ -318,6 +318,7 @@
<addaction name="toolsSystemCommandsAct"/>
<addaction name="patchCreatorAct"/>
<addaction name="toolsVfsDialogAct"/>
<addaction name="actionMusic_Player"/>
<addaction name="separator"/>
<addaction name="toolsDecryptSprxLibsAct"/>
<addaction name="separator"/>
@ -1424,6 +1425,11 @@
<string>Prefer Game Data Icons</string>
</property>
</action>
<action name="actionMusic_Player">
<property name="text">
<string>Music Player</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View file

@ -0,0 +1,47 @@
#include "music_player_dialog.h"
#include "ui_music_player_dialog.h"
#include "qt_music_handler.h"
#include "Emu/System.h"
#include "Emu/VFS.h"
#include <QFileDialog>
#include <QSlider>
music_player_dialog::music_player_dialog(QWidget* parent)
: QDialog(parent), ui(new Ui::music_player_dialog)
{
setAttribute(Qt::WA_DeleteOnClose);
ui->setupUi(this);
m_handler = std::make_unique<qt_music_handler>();
connect(ui->fileSelectButton, &QPushButton::clicked, this, [this](){ select_file(); });
connect(ui->playButton, &QPushButton::clicked, this, [this](){ m_handler->play(m_file_path); });
connect(ui->pauseButton, &QPushButton::clicked, this, [this](){ m_handler->pause(); });
connect(ui->stopButton, &QPushButton::clicked, this, [this](){ m_handler->stop(); });
connect(ui->forwardButton, &QPushButton::clicked, this, [this](){ m_handler->fast_forward(m_file_path); });
connect(ui->reverseButton, &QPushButton::clicked, this, [this](){ m_handler->fast_reverse(m_file_path); });
connect(ui->volumeSlider, &QSlider::valueChanged, this, [this](int value){ m_handler->set_volume(std::clamp(value / 100.0f, 0.0f, 1.0f)); });
}
music_player_dialog::~music_player_dialog()
{
}
void music_player_dialog::select_file()
{
// Initialize Emu if not yet initialized (e.g. Emu.Kill() was previously invoked) before using some of the following vfs:: functions (e.g. vfs::get())
if (Emu.IsStopped())
{
Emu.Init();
}
const std::string vfs_dir_path = vfs::get("/dev_hdd0/music");
const QString file_path = QFileDialog::getOpenFileName(this, tr("Select audio file"), QString::fromStdString(vfs_dir_path), tr("Audio files (*.mp3;*.wav;*.aac;*.ogg;*.flac;*.m4a;*.alac);;All files (*.*)"));
if (!file_path.isEmpty())
{
m_file_path = file_path.toStdString();
}
}

View file

@ -0,0 +1,26 @@
#pragma once
#include <QDialog>
class music_handler_base;
namespace Ui
{
class music_player_dialog;
}
class music_player_dialog : public QDialog
{
Q_OBJECT
public:
explicit music_player_dialog(QWidget* parent = nullptr);
~music_player_dialog();
private:
void select_file();
std::unique_ptr<Ui::music_player_dialog> ui;
std::unique_ptr<music_handler_base> m_handler;
std::string m_file_path;
};

View file

@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>music_player_dialog</class>
<widget class="QDialog" name="music_player_dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Music Player</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="topLayout">
<item>
<spacer name="topSpacerLeft">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="fileSelectButton">
<property name="text">
<string>Select file</string>
</property>
</widget>
</item>
<item>
<spacer name="topSpacerRight">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="bottomLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="reverseButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::MediaSeekBackward"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="playButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::MediaPlaybackStart"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="forwardButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::MediaSeekForward"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pauseButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::MediaPlaybackPause"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stopButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::MediaPlaybackStop"/>
</property>
</widget>
</item>
<item>
<spacer name="buttonSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="volumeLabel">
<property name="text">
<string>Vol:</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="volumeSlider">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -2,6 +2,7 @@
#include "Emu/Cell/Modules/cellMusic.h"
#include "Emu/System.h"
#include "util/logs.hpp"
#include "Utilities/File.h"
#include <QAudioOutput>
#include <QUrl>
@ -68,11 +69,12 @@ qt_music_handler::qt_music_handler()
music_log.notice("Constructing Qt music handler...");
m_media_player = std::make_unique<QMediaPlayer>();
m_media_player->setAudioOutput(new QAudioOutput());
m_media_player->setAudioOutput(new QAudioOutput(m_media_player.get()));
connect(m_media_player.get(), &QMediaPlayer::mediaStatusChanged, this, &qt_music_handler::handle_media_status);
connect(m_media_player.get(), &QMediaPlayer::playbackStateChanged, this, &qt_music_handler::handle_music_state);
connect(m_media_player.get(), &QMediaPlayer::errorOccurred, this, &qt_music_handler::handle_music_error);
connect(m_media_player->audioOutput(), &QAudioOutput::volumeChanged, this, &qt_music_handler::handle_volume_change);
}
qt_music_handler::~qt_music_handler()
@ -117,13 +119,18 @@ void qt_music_handler::play(const std::string& path)
Emu.BlockingCallFromMainThread([&path, this]()
{
if (!fs::is_file(path))
{
music_log.error("play: File does not exist: '%s'", path);
}
if (m_path != path)
{
m_path = path;
m_media_player->setSource(QUrl::fromLocalFile(QString::fromStdString(path)));
}
music_log.notice("Playing music: %s", path);
music_log.notice("Playing music: '%s'", path);
m_media_player->setPlaybackRate(1.0);
m_media_player->play();
});
@ -137,13 +144,18 @@ void qt_music_handler::fast_forward(const std::string& path)
Emu.BlockingCallFromMainThread([&path, this]()
{
if (!fs::is_file(path))
{
music_log.error("fast_forward: File does not exist: '%s'", path);
}
if (m_path != path)
{
m_path = path;
m_media_player->setSource(QUrl::fromLocalFile(QString::fromStdString(path)));
}
music_log.notice("Fast-forwarding music...");
music_log.notice("Fast-forwarding music: '%s'", path);
m_media_player->setPlaybackRate(2.0);
m_media_player->play();
});
@ -157,13 +169,18 @@ void qt_music_handler::fast_reverse(const std::string& path)
Emu.BlockingCallFromMainThread([&path, this]()
{
if (!fs::is_file(path))
{
music_log.error("fast_reverse: File does not exist: '%s'", path);
}
if (m_path != path)
{
m_path = path;
m_media_player->setSource(QUrl::fromLocalFile(QString::fromStdString(path)));
}
music_log.notice("Fast-reversing music...");
music_log.notice("Fast-reversing music: '%s'", path);
m_media_player->setPlaybackRate(-2.0); // NOTE: This doesn't work on the current Qt version
m_media_player->play();
});
@ -234,3 +251,8 @@ void qt_music_handler::handle_music_error(QMediaPlayer::Error error, const QStri
{
music_log.error("Error event: \"%s\" (error=%s)", errorString, error);
}
void qt_music_handler::handle_volume_change(float volume) const
{
music_log.notice("Volume changed: %f", volume);
}

View file

@ -26,6 +26,7 @@ private Q_SLOTS:
void handle_media_status(QMediaPlayer::MediaStatus status);
void handle_music_state(QMediaPlayer::PlaybackState state);
void handle_music_error(QMediaPlayer::Error error, const QString& errorString);
void handle_volume_change(float volume) const;
private:
mutable std::mutex m_mutex;