diff --git a/rpcs3/Emu/Cell/Modules/cellMusic.cpp b/rpcs3/Emu/Cell/Modules/cellMusic.cpp index 4acb535c73..607e6ee02d 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMusic.cpp @@ -70,7 +70,7 @@ struct music_state vm::ptr param, vm::ptr userData)> func{}; vm::ptr userData{}; - std::mutex mtx; + shared_mutex mtx; std::shared_ptr handler; music_selection_context current_selection_context{}; @@ -79,6 +79,34 @@ struct music_state music_state() { handler = Emu.GetCallbacks().get_music_handler(); + handler->set_status_callback([this](music_handler_base::player_status status) + { + // TODO: disabled until I find a game that uses CELL_MUSIC_EVENT_STATUS_NOTIFICATION + return; + + if (!func) + { + return; + } + + s32 result = CELL_OK; + + switch (status) + { + case music_handler_base::player_status::end_of_media: + result = CELL_MUSIC_PLAYBACK_FINISHED; + break; + default: + return; + } + + sysutil_register_cb([this, &result](ppu_thread& ppu) -> s32 + { + cellMusic.notice("Sending status notification %d", result); + func(ppu, CELL_MUSIC_EVENT_STATUS_NOTIFICATION, vm::addr_t(result), userData); + return CELL_OK; + }); + }); } music_state(utils::serial& ar) @@ -100,6 +128,76 @@ struct music_state ar(userData); } + + // NOTE: This function only uses CELL_MUSIC enums. CELL_MUSIC2 enums are identical. + error_code set_playback_command(s32 command) + { + switch (command) + { + case CELL_MUSIC_PB_CMD_STOP: + handler->stop(); + break; + case CELL_MUSIC_PB_CMD_PAUSE: + handler->pause(); + break; + case CELL_MUSIC_PB_CMD_PLAY: + case CELL_MUSIC_PB_CMD_FASTFORWARD: + case CELL_MUSIC_PB_CMD_FASTREVERSE: + case CELL_MUSIC_PB_CMD_NEXT: + case CELL_MUSIC_PB_CMD_PREV: + { + std::string path; + bool no_more_tracks = false; + { + std::lock_guard lock(mtx); + const std::vector& playlist = current_selection_context.playlist; + const u32 current_track = current_selection_context.current_track; + u32 next_track = current_track; + + if (command == CELL_MUSIC_PB_CMD_NEXT || command == CELL_MUSIC_PB_CMD_PREV) + { + next_track = current_selection_context.step_track(command == CELL_MUSIC_PB_CMD_NEXT); + } + + if (next_track < playlist.size()) + { + path = vfs::get(playlist.at(next_track)); + cellMusic.notice("set_playback_command: current vfs path: '%s' (unresolved='%s')", path, playlist.at(next_track)); + } + else + { + current_selection_context.current_track = current_track; + no_more_tracks = true; + } + } + + if (no_more_tracks) + { + cellMusic.notice("set_playback_command: no more tracks to play"); + return CELL_MUSIC_ERROR_NO_MORE_CONTENT; + } + + switch (command) + { + case CELL_MUSIC_PB_CMD_FASTFORWARD: + handler->fast_forward(path); + break; + case CELL_MUSIC_PB_CMD_FASTREVERSE: + handler->fast_reverse(path); + break; + default: + handler->play(path); + break; + } + + break; + } + default: + break; + } + + return CELL_OK; + } }; error_code cell_music_select_contents() @@ -437,64 +535,8 @@ error_code cellMusicSetPlaybackCommand2(s32 command, vm::ptr param) sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32 { - switch (command) - { - case CELL_MUSIC2_PB_CMD_STOP: - music.handler->stop(); - break; - case CELL_MUSIC2_PB_CMD_PAUSE: - music.handler->pause(); - break; - case CELL_MUSIC2_PB_CMD_PLAY: - case CELL_MUSIC2_PB_CMD_NEXT: - case CELL_MUSIC2_PB_CMD_PREV: - { - std::string path; - bool playback_finished = false; - { - std::lock_guard lock(music.mtx); - const std::vector& playlist = music.current_selection_context.playlist; - u32 next_track = music.current_selection_context.current_track; - - if (command != CELL_MUSIC2_PB_CMD_PLAY) - { - next_track = music.current_selection_context.step_track(command == CELL_MUSIC2_PB_CMD_NEXT); - } - - if (next_track < playlist.size()) - { - path = vfs::get(playlist.at(next_track)); - cellMusic.notice("cellMusicSetPlaybackCommand2: current vfs path: '%s' (unresolved='%s')", path, playlist.at(next_track)); - } - else - { - playback_finished = true; - } - } - - if (playback_finished) - { - // TODO: is CELL_MUSIC2_PLAYBACK_FINISHED correct here ? - cellMusic.notice("cellMusicSetPlaybackCommand2: no more tracks to play"); - music.handler->stop(); - music.func(ppu, CELL_MUSIC2_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(CELL_MUSIC2_PLAYBACK_FINISHED), music.userData); - return CELL_OK; - } - - music.handler->play(path); - break; - } - case CELL_MUSIC2_PB_CMD_FASTFORWARD: - music.handler->fast_forward(); - break; - case CELL_MUSIC2_PB_CMD_FASTREVERSE: - music.handler->fast_reverse(); - break; - default: - break; - } - - music.func(ppu, CELL_MUSIC2_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(CELL_OK), music.userData); + const error_code result = music.set_playback_command(command); + music.func(ppu, CELL_MUSIC2_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(+result), music.userData); return CELL_OK; }); @@ -515,64 +557,8 @@ error_code cellMusicSetPlaybackCommand(s32 command, vm::ptr param) sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32 { - switch (command) - { - case CELL_MUSIC_PB_CMD_STOP: - music.handler->stop(); - break; - case CELL_MUSIC_PB_CMD_PAUSE: - music.handler->pause(); - break; - case CELL_MUSIC_PB_CMD_PLAY: - case CELL_MUSIC_PB_CMD_NEXT: - case CELL_MUSIC_PB_CMD_PREV: - { - std::string path; - bool playback_finished = false; - { - std::lock_guard lock(music.mtx); - const std::vector& playlist = music.current_selection_context.playlist; - u32 next_track = music.current_selection_context.current_track; - - if (command != CELL_MUSIC_PB_CMD_PLAY) - { - next_track = music.current_selection_context.step_track(command == CELL_MUSIC_PB_CMD_NEXT); - } - - if (next_track < playlist.size()) - { - path = vfs::get(playlist.at(next_track)); - cellMusic.notice("cellMusicSetPlaybackCommand: current vfs path: '%s' (unresolved='%s')", path, playlist.at(next_track)); - } - else - { - playback_finished = true; - } - } - - if (playback_finished) - { - // TODO: is CELL_MUSIC_PLAYBACK_FINISHED correct here ? - cellMusic.notice("cellMusicSetPlaybackCommand: no more tracks to play"); - music.handler->stop(); - music.func(ppu, CELL_MUSIC_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(CELL_MUSIC_PLAYBACK_FINISHED), music.userData); - return CELL_OK; - } - - music.handler->play(path); - break; - } - case CELL_MUSIC_PB_CMD_FASTFORWARD: - music.handler->fast_forward(); - break; - case CELL_MUSIC_PB_CMD_FASTREVERSE: - music.handler->fast_reverse(); - break; - default: - break; - } - - music.func(ppu, CELL_MUSIC_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(CELL_OK), music.userData); + const error_code result = music.set_playback_command(command); + music.func(ppu, CELL_MUSIC_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(+result), music.userData); return CELL_OK; }); diff --git a/rpcs3/Emu/Io/Null/null_music_handler.h b/rpcs3/Emu/Io/Null/null_music_handler.h index 791ae7abe3..fe022dc79e 100644 --- a/rpcs3/Emu/Io/Null/null_music_handler.h +++ b/rpcs3/Emu/Io/Null/null_music_handler.h @@ -8,10 +8,10 @@ public: null_music_handler() : music_handler_base() {} void stop() override { m_state = 0; } // CELL_MUSIC_PB_STATUS_STOP - void play(const std::string& /*path*/) override { m_state = 1; } // CELL_MUSIC_PB_STATUS_PLAY void pause() override { m_state = 2; } // CELL_MUSIC_PB_STATUS_PAUSE - void fast_forward() override { m_state = 3; } // CELL_MUSIC_PB_STATUS_FASTFORWARD - void fast_reverse() override { m_state = 4; } // CELL_MUSIC_PB_STATUS_FASTREVERSE + void play(const std::string& /*path*/) override { m_state = 1; } // CELL_MUSIC_PB_STATUS_PLAY + void fast_forward(const std::string& /*path*/) override { m_state = 3; } // CELL_MUSIC_PB_STATUS_FASTFORWARD + void fast_reverse(const std::string& /*path*/) override { m_state = 4; } // CELL_MUSIC_PB_STATUS_FASTREVERSE void set_volume(f32 volume) override { m_volume = volume; } f32 get_volume() const override { return m_volume; } diff --git a/rpcs3/Emu/Io/music_handler_base.h b/rpcs3/Emu/Io/music_handler_base.h index c3eaf65beb..8af59a938d 100644 --- a/rpcs3/Emu/Io/music_handler_base.h +++ b/rpcs3/Emu/Io/music_handler_base.h @@ -6,13 +6,18 @@ class music_handler_base { public: + enum class player_status + { + end_of_media + }; + virtual ~music_handler_base() = default; virtual void stop() = 0; - virtual void play(const std::string& path) = 0; virtual void pause() = 0; - virtual void fast_forward() = 0; - virtual void fast_reverse() = 0; + virtual void play(const std::string& path) = 0; + virtual void fast_forward(const std::string& path) = 0; + virtual void fast_reverse(const std::string& path) = 0; virtual void set_volume(f32 volume) = 0; virtual f32 get_volume() const = 0; @@ -21,6 +26,12 @@ public: return m_state; } + void set_status_callback(std::function status_callback) + { + m_status_callback = std::move(status_callback); + } + protected: atomic_t m_state{0}; + std::function m_status_callback; }; diff --git a/rpcs3/rpcs3qt/qt_music_error_handler.cpp b/rpcs3/rpcs3qt/qt_music_error_handler.cpp index 305d1b531f..aa5af7af8e 100644 --- a/rpcs3/rpcs3qt/qt_music_error_handler.cpp +++ b/rpcs3/rpcs3qt/qt_music_error_handler.cpp @@ -61,8 +61,9 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } -qt_music_error_handler::qt_music_error_handler(std::shared_ptr media_player) +qt_music_error_handler::qt_music_error_handler(std::shared_ptr media_player, std::function status_callback) : m_media_player(std::move(media_player)) + , m_status_callback(std::move(status_callback)) { if (m_media_player) { @@ -79,6 +80,11 @@ qt_music_error_handler::~qt_music_error_handler() void qt_music_error_handler::handle_media_status(QMediaPlayer::MediaStatus status) { music_log.notice("New media status: %s (status=%d)", status, static_cast(status)); + + if (m_status_callback) + { + m_status_callback(status); + } } void qt_music_error_handler::handle_music_state(QMediaPlayer::State state) diff --git a/rpcs3/rpcs3qt/qt_music_error_handler.h b/rpcs3/rpcs3qt/qt_music_error_handler.h index d71a85d639..11c5ff3fbd 100644 --- a/rpcs3/rpcs3qt/qt_music_error_handler.h +++ b/rpcs3/rpcs3qt/qt_music_error_handler.h @@ -8,7 +8,7 @@ class qt_music_error_handler : public QObject Q_OBJECT public: - qt_music_error_handler(std::shared_ptr media_player); + qt_music_error_handler(std::shared_ptr media_player, std::function status_callback); virtual ~qt_music_error_handler(); private Q_SLOTS: @@ -18,4 +18,5 @@ private Q_SLOTS: private: std::shared_ptr m_media_player; + std::function m_status_callback = nullptr; }; diff --git a/rpcs3/rpcs3qt/qt_music_handler.cpp b/rpcs3/rpcs3qt/qt_music_handler.cpp index 283c23848c..39c40a5915 100644 --- a/rpcs3/rpcs3qt/qt_music_handler.cpp +++ b/rpcs3/rpcs3qt/qt_music_handler.cpp @@ -15,7 +15,33 @@ qt_music_handler::qt_music_handler() m_media_player = std::make_shared(); m_media_player->setAudioRole(QAudio::Role::MusicRole); - m_error_handler = std::make_unique(m_media_player); + m_error_handler = std::make_unique(m_media_player, + [this](QMediaPlayer::MediaStatus status) + { + if (!m_status_callback) + { + return; + } + + switch (status) + { + case QMediaPlayer::MediaStatus::UnknownMediaStatus: + case QMediaPlayer::MediaStatus::NoMedia: + case QMediaPlayer::MediaStatus::LoadingMedia: + case QMediaPlayer::MediaStatus::LoadedMedia: + case QMediaPlayer::MediaStatus::StalledMedia: + case QMediaPlayer::MediaStatus::BufferingMedia: + case QMediaPlayer::MediaStatus::BufferedMedia: + case QMediaPlayer::MediaStatus::InvalidMedia: + break; + case QMediaPlayer::MediaStatus::EndOfMedia: + m_status_callback(player_status::end_of_media); + break; + default: + music_log.error("Ignoring unknown status %d", static_cast(status)); + break; + } + }); } qt_music_handler::~qt_music_handler() @@ -29,29 +55,6 @@ qt_music_handler::~qt_music_handler() }); } -void qt_music_handler::play(const std::string& path) -{ - std::lock_guard lock(m_mutex); - - Emu.BlockingCallFromMainThread([&path, this]() - { - if (m_state == CELL_MUSIC_PB_STATUS_PAUSE) - { - music_log.notice("Resuming music: %s", path); - } - else - { - music_log.notice("Playing music: %s", path); - m_media_player->setPlaybackRate(1.0); - m_media_player->setMedia(QUrl(QString::fromStdString(path))); - } - - m_media_player->play(); - }); - - m_state = CELL_MUSIC_PB_STATUS_PLAY; -} - void qt_music_handler::stop() { std::lock_guard lock(m_mutex); @@ -78,27 +81,61 @@ void qt_music_handler::pause() m_state = CELL_MUSIC_PB_STATUS_PAUSE; } -void qt_music_handler::fast_forward() +void qt_music_handler::play(const std::string& path) { std::lock_guard lock(m_mutex); - Emu.BlockingCallFromMainThread([this]() + Emu.BlockingCallFromMainThread([&path, this]() { + if (m_path != path) + { + m_path = path; + m_media_player->setMedia(QUrl(QString::fromStdString(path))); + } + + music_log.notice("Playing music: %s", path); + m_media_player->setPlaybackRate(1.0); + m_media_player->play(); + }); + + m_state = CELL_MUSIC_PB_STATUS_PLAY; +} + +void qt_music_handler::fast_forward(const std::string& path) +{ + std::lock_guard lock(m_mutex); + + Emu.BlockingCallFromMainThread([&path, this]() + { + if (m_path != path) + { + m_path = path; + m_media_player->setMedia(QUrl(QString::fromStdString(path))); + } + music_log.notice("Fast-forwarding music..."); m_media_player->setPlaybackRate(2.0); + m_media_player->play(); }); m_state = CELL_MUSIC_PB_STATUS_FASTFORWARD; } -void qt_music_handler::fast_reverse() +void qt_music_handler::fast_reverse(const std::string& path) { std::lock_guard lock(m_mutex); - Emu.BlockingCallFromMainThread([this]() + Emu.BlockingCallFromMainThread([&path, this]() { + if (m_path != path) + { + m_path = path; + m_media_player->setMedia(QUrl(QString::fromStdString(path))); + } + music_log.notice("Fast-reversing music..."); m_media_player->setPlaybackRate(-2.0); + m_media_player->play(); }); m_state = CELL_MUSIC_PB_STATUS_FASTREVERSE; diff --git a/rpcs3/rpcs3qt/qt_music_handler.h b/rpcs3/rpcs3qt/qt_music_handler.h index 83793383ec..fbfe3d5e19 100644 --- a/rpcs3/rpcs3qt/qt_music_handler.h +++ b/rpcs3/rpcs3qt/qt_music_handler.h @@ -13,10 +13,10 @@ public: virtual ~qt_music_handler(); void stop() override; - void play(const std::string& path) override; void pause() override; - void fast_forward() override; - void fast_reverse() override; + void play(const std::string& path) override; + void fast_forward(const std::string& path) override; + void fast_reverse(const std::string& path) override; void set_volume(f32 volume) override; f32 get_volume() const override; @@ -24,4 +24,5 @@ private: mutable std::mutex m_mutex; std::unique_ptr m_error_handler; std::shared_ptr m_media_player; + std::string m_path; };