Fix animation freeze when loading music files (#609)

* Implement async music loading

* Simplify music filename parsing and fix "missing" on streams

Co-authored-by: oldmud0 <oldmud0@users.noreply.github.com>
This commit is contained in:
Salanto 2021-12-24 21:05:01 +01:00 committed by GitHub
parent d1fb7fde16
commit 5ce0e6416e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 32 deletions

View File

@ -10,6 +10,8 @@
#include <QDebug> #include <QDebug>
#include <QWidget> #include <QWidget>
#include <string.h> #include <string.h>
#include <QFuture>
#include <QFutureWatcher>
class AOMusicPlayer { class AOMusicPlayer {
public: public:
@ -24,8 +26,10 @@ public:
int loop_start[4] = {0, 0, 0, 0}; int loop_start[4] = {0, 0, 0, 0};
int loop_end[4] = {0, 0, 0, 0}; int loop_end[4] = {0, 0, 0, 0};
QFutureWatcher<QString> music_watcher;
public slots: public slots:
int play(QString p_song, int channel = 0, bool loop = false, QString play(QString p_song, int channel = 0, bool loop = false,
int effect_flags = 0); int effect_flags = 0);
void stop(int channel = 0); void stop(int channel = 0);

View File

@ -57,6 +57,8 @@
#include <QTextCharFormat> #include <QTextCharFormat>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QFuture>
#include <algorithm> #include <algorithm>
#include <stack> #include <stack>
@ -814,6 +816,8 @@ public slots:
bool steno); bool steno);
void on_reload_theme_clicked(); void on_reload_theme_clicked();
void update_ui_music_name();
private slots: private slots:
void start_chat_ticking(); void start_chat_ticking();
void play_sfx(); void play_sfx();

View File

@ -13,12 +13,13 @@ AOMusicPlayer::~AOMusicPlayer()
} }
} }
int AOMusicPlayer::play(QString p_song, int channel, bool loop, QString AOMusicPlayer::play(QString p_song, int channel, bool loop,
int effect_flags) int effect_flags)
{ {
QFuture<QString> invoking_future = music_watcher.future();
channel = channel % m_channelmax; channel = channel % m_channelmax;
if (channel < 0) // wtf? if (channel < 0) // wtf?
return BASS_ERROR_NOCHAN; return "[ERROR] Invalid Channel";
QString f_path = ao_app->get_real_path(ao_app->get_music_path(p_song)); QString f_path = ao_app->get_real_path(ao_app->get_music_path(p_song));
unsigned int flags = BASS_STREAM_PRESCAN | BASS_STREAM_AUTOFREE | unsigned int flags = BASS_STREAM_PRESCAN | BASS_STREAM_AUTOFREE |
@ -43,6 +44,14 @@ int AOMusicPlayer::play(QString p_song, int channel, bool loop,
newstream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags); newstream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags);
} }
int error_code = BASS_ErrorGetCode();
if (invoking_future.isCanceled() && channel == 0) {
// Target future has changed. This stream has become irrelevant.
// So even if the stream manages to finish after the latest one, we don't run
// into order issues.
return QString();
}
if (ao_app->get_audio_output_device() != "default") if (ao_app->get_audio_output_device() != "default")
BASS_ChannelSetDevice(m_stream_list[channel], BASS_GetDevice()); BASS_ChannelSetDevice(m_stream_list[channel], BASS_GetDevice());
@ -109,7 +118,7 @@ int AOMusicPlayer::play(QString p_song, int channel, bool loop,
BASS_ChannelStop(m_stream_list[channel]); BASS_ChannelStop(m_stream_list[channel]);
m_stream_list[channel] = newstream; m_stream_list[channel] = newstream;
BASS_ChannelPlay(m_stream_list[channel], false); BASS_ChannelPlay(newstream, false);
if (effect_flags & FADE_IN) { if (effect_flags & FADE_IN) {
// Fade in our sample // Fade in our sample
BASS_ChannelSetAttribute(newstream, BASS_ATTRIB_VOL, 0); BASS_ChannelSetAttribute(newstream, BASS_ATTRIB_VOL, 0);
@ -120,12 +129,32 @@ int AOMusicPlayer::play(QString p_song, int channel, bool loop,
else else
this->set_volume(m_volume[channel], channel); this->set_volume(m_volume[channel], channel);
BASS_ChannelSetSync(m_stream_list[channel], BASS_SYNC_DEV_FAIL, 0, BASS_ChannelSetSync(newstream, BASS_SYNC_DEV_FAIL, 0,
ao_app->BASSreset, 0); ao_app->BASSreset, 0);
this->set_looping(loop, channel); // Have to do this here due to any this->set_looping(loop, channel); // Have to do this here due to any
// crossfading-related changes, etc. // crossfading-related changes, etc.
return BASS_ErrorGetCode();
bool is_stop = (p_song == "~stop.mp3");
QString p_song_clear = QUrl(p_song).fileName();
p_song_clear = p_song_clear.left(p_song_clear.lastIndexOf('.'));
if (is_stop) {
return QObject::tr("None");
}
if (error_code == BASS_ERROR_HANDLE) { // Cheap hack to see if file missing
return QObject::tr("[MISSING] %1").arg(p_song_clear);
}
if (p_song.startsWith("http") && channel == 0) {
return QObject::tr("[STREAM] %1").arg(p_song_clear);
}
if (channel == 0)
return p_song_clear;
return "";
} }
void AOMusicPlayer::stop(int channel) void AOMusicPlayer::stop(int channel)

View File

@ -27,6 +27,8 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
music_player = new AOMusicPlayer(this, ao_app); music_player = new AOMusicPlayer(this, ao_app);
music_player->set_volume(0); music_player->set_volume(0);
connect(&music_player->music_watcher, &QFutureWatcher<QString>::finished,
this, &Courtroom::update_ui_music_name, Qt::QueuedConnection);
sfx_player = new AOSfxPlayer(this, ao_app); sfx_player = new AOSfxPlayer(this, ao_app);
sfx_player->set_volume(0); sfx_player->set_volume(0);
@ -3807,13 +3809,8 @@ void Courtroom::handle_song(QStringList *p_contents)
int effect_flags = 0; // No effects by default - vanilla functionality int effect_flags = 0; // No effects by default - vanilla functionality
QString f_song = f_contents.at(0); QString f_song = f_contents.at(0);
QString f_song_clear = f_song.left(f_song.lastIndexOf(".")); QString f_song_clear = QUrl(f_song).fileName();
if (f_song.startsWith("http")) { f_song_clear = f_song_clear.left(f_song_clear.lastIndexOf('.'));
QByteArray f_song_bytearray = f_song.toUtf8();
QString f_song_decoded = QUrl::fromPercentEncoding(f_song_bytearray);
f_song_clear = f_song_decoded.left(f_song_decoded.lastIndexOf("."));
}
f_song_clear = f_song_clear.right(f_song_clear.length() - (f_song_clear.lastIndexOf("/") + 1));
int n_char = f_contents.at(1).toInt(&ok); int n_char = f_contents.at(1).toInt(&ok);
if (!ok) if (!ok)
@ -3862,27 +3859,25 @@ void Courtroom::handle_song(QStringList *p_contents)
} }
} }
int error_code = music_player->play(f_song, channel, looping, effect_flags); QFuture<QString> future = QtConcurrent::run(music_player, &AOMusicPlayer::play, f_song, channel,
looping, effect_flags);
if (is_stop) { if (channel == 0) {
ui_music_name->setText(tr("None")); // Current song UI only displays the song playing, not other channels.
return; // Any other music playing is irrelevant.
if (music_player->music_watcher.isRunning()) {
music_player->music_watcher.cancel();
}
music_player->music_watcher.setFuture(future);
ui_music_name->setText(tr("[LOADING] %1").arg(f_song_clear));
} }
}
if (error_code == BASS_ERROR_HANDLE) { // Cheap hack to see if file missing void Courtroom::update_ui_music_name()
ui_music_name->setText(tr("[MISSING] %1").arg(f_song_clear)); {
return; QString result = music_player->music_watcher.result();
} if (result.isEmpty())
return;
if (f_song.startsWith("http") && channel == 0) { ui_music_name->setText(result);
ui_music_name->setText(tr("[STREAM] %1").arg(f_song_clear));
return;
}
if (channel == 0){
ui_music_name->setText(f_song_clear);
return;
}
} }
void Courtroom::handle_wtce(QString p_wtce, int variant) void Courtroom::handle_wtce(QString p_wtce, int variant)