mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-21 00:39:53 +00:00
cellMusic: Fix resume, fast forward and rewind
- Sadly rewind does not work with the QMediaPlayer on windows
This commit is contained in:
parent
577f379a12
commit
d80146c704
@ -70,7 +70,7 @@ struct music_state
|
||||
|
||||
vm::ptr<void(u32 event, vm::ptr<void> param, vm::ptr<void> userData)> func{};
|
||||
vm::ptr<void> userData{};
|
||||
std::mutex mtx;
|
||||
shared_mutex mtx;
|
||||
std::shared_ptr<music_handler_base> 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<std::string>& 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<void> 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<std::string>& 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<void> 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<std::string>& 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;
|
||||
});
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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<void(player_status)> status_callback)
|
||||
{
|
||||
m_status_callback = std::move(status_callback);
|
||||
}
|
||||
|
||||
protected:
|
||||
atomic_t<s32> m_state{0};
|
||||
std::function<void(player_status status)> m_status_callback;
|
||||
};
|
||||
|
@ -61,8 +61,9 @@ void fmt_class_string<QMediaPlayer::State>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
qt_music_error_handler::qt_music_error_handler(std::shared_ptr<QMediaPlayer> media_player)
|
||||
qt_music_error_handler::qt_music_error_handler(std::shared_ptr<QMediaPlayer> media_player, std::function<void(QMediaPlayer::MediaStatus)> 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<int>(status));
|
||||
|
||||
if (m_status_callback)
|
||||
{
|
||||
m_status_callback(status);
|
||||
}
|
||||
}
|
||||
|
||||
void qt_music_error_handler::handle_music_state(QMediaPlayer::State state)
|
||||
|
@ -8,7 +8,7 @@ class qt_music_error_handler : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
qt_music_error_handler(std::shared_ptr<QMediaPlayer> media_player);
|
||||
qt_music_error_handler(std::shared_ptr<QMediaPlayer> media_player, std::function<void(QMediaPlayer::MediaStatus)> status_callback);
|
||||
virtual ~qt_music_error_handler();
|
||||
|
||||
private Q_SLOTS:
|
||||
@ -18,4 +18,5 @@ private Q_SLOTS:
|
||||
|
||||
private:
|
||||
std::shared_ptr<QMediaPlayer> m_media_player;
|
||||
std::function<void(QMediaPlayer::MediaStatus)> m_status_callback = nullptr;
|
||||
};
|
||||
|
@ -15,7 +15,33 @@ qt_music_handler::qt_music_handler()
|
||||
m_media_player = std::make_shared<QMediaPlayer>();
|
||||
m_media_player->setAudioRole(QAudio::Role::MusicRole);
|
||||
|
||||
m_error_handler = std::make_unique<qt_music_error_handler>(m_media_player);
|
||||
m_error_handler = std::make_unique<qt_music_error_handler>(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<int>(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;
|
||||
|
@ -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<qt_music_error_handler> m_error_handler;
|
||||
std::shared_ptr<QMediaPlayer> m_media_player;
|
||||
std::string m_path;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user