cellMusic: Fix resume, fast forward and rewind

- Sadly rewind does not work with the QMediaPlayer on windows
This commit is contained in:
Megamouse 2022-07-26 23:49:27 +02:00
parent 577f379a12
commit d80146c704
7 changed files with 198 additions and 156 deletions

View File

@ -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;
});

View File

@ -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; }

View File

@ -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;
};

View File

@ -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)

View File

@ -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;
};

View File

@ -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;

View File

@ -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;
};