Add playback of background/title music in game list (#1033)

* add playback of background/title music

* clang_format

* add windows multimedia build instructions

* fix typo accidentally made to arm

* address comments

* loop music

* feedback

* fix CI

* add newline

* playBGM off by default

---------

Co-authored-by: Charles <charles@superfocus.ai>
This commit is contained in:
tGecko 2024-09-26 08:12:41 +02:00 committed by GitHub
parent ddb82a690b
commit 54e2179337
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 181 additions and 17 deletions

View File

@ -113,6 +113,7 @@ jobs:
target: desktop
arch: win64_msvc2019_64
archives: qtbase qttools
modules: qtmultimedia
- name: Cache CMake Configuration
uses: actions/cache@v4
@ -237,6 +238,7 @@ jobs:
target: desktop
arch: clang_64
archives: qtbase qttools
modules: qtmultimedia
- name: Cache CMake Configuration
uses: actions/cache@v4
@ -341,7 +343,7 @@ jobs:
submodules: recursive
- name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev
- name: Cache CMake Configuration
uses: actions/cache@v4

View File

@ -144,7 +144,7 @@ add_subdirectory(externals)
include_directories(src)
if(ENABLE_QT_GUI)
find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network)
find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network Multimedia)
qt_standard_project_setup()
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON)
@ -653,6 +653,8 @@ qt_add_resources(RESOURCE_FILES src/shadps4.qrc)
set(QT_GUI src/qt_gui/about_dialog.cpp
src/qt_gui/about_dialog.h
src/qt_gui/about_dialog.ui
src/qt_gui/background_music_player.cpp
src/qt_gui/background_music_player.h
src/qt_gui/cheats_patches.cpp
src/qt_gui/cheats_patches.h
src/qt_gui/check_update.cpp
@ -760,7 +762,7 @@ else()
endif()
if (ENABLE_QT_GUI)
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network)
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
add_definitions(-DENABLE_QT_GUI)
endif()

View File

@ -80,7 +80,7 @@ Normal x86-based computers, follow:
1. Open "MSYS2 MINGW64" from your new applications
2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg`
1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools`
1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia`
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`
6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
@ -94,7 +94,7 @@ ARM64-based computers, follow:
1. Open "MSYS2 CLANGARM64" from your new applications
2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg`
1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools`
1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia`
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`
6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`

View File

@ -11,6 +11,7 @@ namespace Config {
static bool isNeo = false;
static bool isFullscreen = false;
static bool playBGM = false;
static u32 screenWidth = 1280;
static u32 screenHeight = 720;
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
@ -64,6 +65,10 @@ bool isFullscreenMode() {
return isFullscreen;
}
bool getPlayBGM() {
return playBGM;
}
u32 getScreenWidth() {
return screenWidth;
}
@ -220,6 +225,10 @@ void setFullscreenMode(bool enable) {
isFullscreen = enable;
}
void setPlayBGM(bool enable) {
playBGM = enable;
}
void setLanguage(u32 language) {
m_language = language;
}
@ -379,6 +388,7 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
isFullscreen = toml::find_or<bool>(general, "Fullscreen", false);
playBGM = toml::find_or<bool>(general, "playBGM", false);
logFilter = toml::find_or<std::string>(general, "logFilter", "");
logType = toml::find_or<std::string>(general, "logType", "sync");
userName = toml::find_or<std::string>(general, "userName", "shadPS4");
@ -473,6 +483,7 @@ void save(const std::filesystem::path& path) {
data["General"]["isPS4Pro"] = isNeo;
data["General"]["Fullscreen"] = isFullscreen;
data["General"]["playBGM"] = playBGM;
data["General"]["logFilter"] = logFilter;
data["General"]["logType"] = logType;
data["General"]["userName"] = userName;
@ -524,6 +535,7 @@ void save(const std::filesystem::path& path) {
void setDefaultValues() {
isNeo = false;
isFullscreen = false;
playBGM = false;
screenWidth = 1280;
screenHeight = 720;
logFilter = "";

View File

@ -13,6 +13,7 @@ void save(const std::filesystem::path& path);
bool isNeoMode();
bool isFullscreenMode();
bool getPlayBGM();
std::string getLogFilter();
std::string getLogType();
std::string getUserName();
@ -47,6 +48,7 @@ void setGpuId(s32 selectedGpuId);
void setScreenWidth(u32 width);
void setScreenHeight(u32 height);
void setFullscreenMode(bool enable);
void setPlayBGM(bool enable);
void setLanguage(u32 language);
void setNeoMode(bool enable);
void setUserName(const std::string& type);

View File

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "background_music_player.h"
BackgroundMusicPlayer::BackgroundMusicPlayer(QObject* parent) : QObject(parent) {
m_mediaPlayer = new QMediaPlayer(this);
m_audioOutput = new QAudioOutput(this);
m_mediaPlayer->setAudioOutput(m_audioOutput);
m_mediaPlayer->setLoops(QMediaPlayer::Infinite);
}
void BackgroundMusicPlayer::playMusic(const QString& snd0path) {
if (snd0path.isEmpty()) {
stopMusic();
return;
}
const auto newMusic = QUrl::fromLocalFile(snd0path);
if (m_mediaPlayer->playbackState() == QMediaPlayer::PlayingState &&
m_currentMusic == newMusic) {
// already playing the correct music
return;
}
m_currentMusic = newMusic;
m_mediaPlayer->setSource(newMusic);
m_mediaPlayer->play();
}
void BackgroundMusicPlayer::stopMusic() {
m_mediaPlayer->stop();
}

View File

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QAudioOutput>
#include <QMediaPlayer>
#include <QObject>
class BackgroundMusicPlayer : public QObject {
Q_OBJECT
public:
static BackgroundMusicPlayer& getInstance() {
static BackgroundMusicPlayer instance;
return instance;
}
void playMusic(const QString& snd0path);
void stopMusic();
private:
BackgroundMusicPlayer(QObject* parent = nullptr);
QMediaPlayer* m_mediaPlayer;
QAudioOutput* m_audioOutput;
QUrl m_currentMusic;
};

View File

@ -39,6 +39,15 @@ GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidg
});
}
void GameGridFrame::PlayBackgroundMusic(QTableWidgetItem* item) {
if (!item) {
BackgroundMusicPlayer::getInstance().stopMusic();
return;
}
const auto snd0path = QString::fromStdString(m_game_info->m_games[item->row()].snd0_path);
BackgroundMusicPlayer::getInstance().playMusic(snd0path);
}
void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool fromSearch) {
QVector<GameInfo> m_games_;
this->clearContents();

View File

@ -5,6 +5,7 @@
#include <QScrollBar>
#include "background_music_player.h"
#include "common/config.h"
#include "game_info.h"
#include "game_list_utils.h"
@ -19,6 +20,7 @@ Q_SIGNALS:
public Q_SLOTS:
void SetGridBackgroundImage(int row, int column);
void RefreshGridBackgroundImage();
void PlayBackgroundMusic(QTableWidgetItem* item);
private:
QImage backgroundImage;

View File

@ -32,6 +32,7 @@ public:
QString iconpath = QString::fromStdString(game.icon_path);
game.icon = QImage(iconpath);
game.pic_path = game.path + "/sce_sys/pic1.png";
game.snd0_path = game.path + "/sce_sys/snd0.at9";
if (const auto title = psf.GetString("TITLE"); title.has_value()) {
game.name = *title;
}

View File

@ -68,6 +68,15 @@ GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidg
});
}
void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) {
if (!item) {
BackgroundMusicPlayer::getInstance().stopMusic();
return;
}
const auto snd0path = QString::fromStdString(m_game_info->m_games[item->row()].snd0_path);
BackgroundMusicPlayer::getInstance().playMusic(snd0path);
}
void GameListFrame::PopulateGameList() {
this->setRowCount(m_game_info->m_games.size());
ResizeIcons(icon_size);

View File

@ -5,6 +5,7 @@
#include <QScrollBar>
#include "background_music_player.h"
#include "game_info.h"
#include "game_list_utils.h"
#include "gui_context_menus.h"
@ -21,6 +22,7 @@ public Q_SLOTS:
void RefreshListBackgroundImage();
void SortNameAscending(int columnIndex);
void SortNameDescending(int columnIndex);
void PlayBackgroundMusic(QTableWidgetItem* item);
private:
void SetTableItem(int row, int column, QString itemStr);

View File

@ -7,6 +7,7 @@ struct GameInfo {
std::string path; // root path of game directory (normally directory that contains eboot.bin)
std::string icon_path; // path of icon0.png
std::string pic_path; // path of pic1.png
std::string snd0_path; // path of snd0.at9
QImage icon;
std::string size;
// variables extracted from param.sfo

View File

@ -501,9 +501,29 @@ void MainWindow::CreateConnects() {
isIconBlack = false;
}
});
connect(m_game_grid_frame.get(), &QTableWidget::cellClicked, this,
&MainWindow::PlayBackgroundMusic);
connect(m_game_list_frame.get(), &QTableWidget::cellClicked, this,
&MainWindow::PlayBackgroundMusic);
}
void MainWindow::PlayBackgroundMusic() {
if (isGameRunning || !Config::getPlayBGM()) {
BackgroundMusicPlayer::getInstance().stopMusic();
return;
}
int itemID = isTableList ? m_game_list_frame->currentItem()->row()
: m_game_grid_frame->crtRow * m_game_grid_frame->columnCnt +
m_game_grid_frame->crtColumn;
const auto snd0path = QString::fromStdString(m_game_info->m_games[itemID].snd0_path);
BackgroundMusicPlayer::getInstance().playMusic(snd0path);
}
void MainWindow::StartGame() {
isGameRunning = true;
BackgroundMusicPlayer::getInstance().stopMusic();
QString gamePath = "";
int table_mode = Config::getTableMode();
if (table_mode == 0) {

View File

@ -7,6 +7,7 @@
#include <QDragEnterEvent>
#include <QTranslator>
#include "background_music_player.h"
#include "common/config.h"
#include "common/path_util.h"
#include "core/file_format/psf.h"
@ -63,9 +64,11 @@ private:
void BootGame();
void AddRecentFiles(QString filePath);
void LoadTranslation();
void PlayBackgroundMusic();
QIcon RecolorIcon(const QIcon& icon, bool isWhite);
bool isIconBlack = false;
bool isTableList = true;
bool isGameRunning = false;
QActionGroup* m_icon_size_act_group = nullptr;
QActionGroup* m_list_mode_act_group = nullptr;
QActionGroup* m_theme_act_group = nullptr;

View File

@ -134,6 +134,9 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
auto checkUpdate = new CheckUpdate(true);
checkUpdate->exec();
});
connect(ui->playBGMCheckBox, &QCheckBox::stateChanged, this,
[](int val) { Config::setPlayBGM(val); });
}
// GPU TAB
@ -192,7 +195,7 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->dumpShadersCheckBox->setChecked(Config::dumpShaders());
ui->nullGpuCheckBox->setChecked(Config::nullGpu());
ui->dumpPM4CheckBox->setChecked(Config::dumpPM4());
ui->playBGMCheckBox->setChecked(Config::getPlayBGM());
ui->fullscreenCheckBox->setChecked(Config::isFullscreenMode());
ui->showSplashCheckBox->setChecked(Config::showSplash());
ui->ps4proCheckBox->setChecked(Config::isNeoMode());

View File

@ -52,7 +52,7 @@
<x>0</x>
<y>0</y>
<width>836</width>
<height>428</height>
<height>432</height>
</rect>
</property>
<property name="sizePolicy">
@ -70,7 +70,7 @@
</attribute>
<layout class="QVBoxLayout" name="generalTabVLayout" stretch="0,0">
<item>
<layout class="QHBoxLayout" name="generalTabHLayout" stretch="1,1,1">
<layout class="QHBoxLayout" name="generalTabHLayoutTop" stretch="1,1,1">
<item>
<layout class="QVBoxLayout" name="systemTabLayoutLeft">
<item>
@ -340,19 +340,55 @@
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="GUITabLayoutMiddle" stretch="1">
<item>
<widget class="QGroupBox" name="GUIgroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>GUI Settings</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget_3">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>241</width>
<height>41</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="playBGMCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Play title music</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>500</width>
<height>20</height>
</size>
<enum>QSizePolicy::Policy::Expanding</enum>
</property>
</spacer>
</item>