From 842b829bee8b840149c0042bb7adfc3bda6eec9d Mon Sep 17 00:00:00 2001 From: Crystalwarrior Date: Fri, 20 Sep 2019 22:11:37 +0300 Subject: [PATCH] Tweak the ambience/crosfade layer to fade in regardless of another sample being there Fix music not being looped sometimes Prevent channel less than 0 from being passed Set up a new music display element with an attached music name, it displays a scrolling text of the currently playing music Fix music packet processing issues Make SFX slider responsible for all music channels besides 0 (actual music) scrolltext.cpp code recipe was taken from https://stackoverflow.com/questions/10651514/text-scrolling-marquee-in-qlabel - thanks to leemes for that one, I only adapted it for newer C++ version and tweaked some stuff. --- include/aomusicplayer.h | 12 ++-- include/courtroom.h | 4 ++ include/scrolltext.h | 50 +++++++++++++++ src/aomusicplayer.cpp | 38 +++++++++--- src/courtroom.cpp | 73 ++++++++++++++++------ src/scrolltext.cpp | 134 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 277 insertions(+), 34 deletions(-) create mode 100644 include/scrolltext.h create mode 100644 src/scrolltext.cpp diff --git a/include/aomusicplayer.h b/include/aomusicplayer.h index 8ed0771..8e0d239 100644 --- a/include/aomusicplayer.h +++ b/include/aomusicplayer.h @@ -19,16 +19,19 @@ class AOMusicPlayer public: AOMusicPlayer(QWidget *parent, AOApplication *p_ao_app); virtual ~AOMusicPlayer(); - - void play(QString p_song, int channel=0, bool crossfade=false); - void stop(int channel=0); - void set_volume(int p_value, int channel=0); + void set_volume(int p_value, int channel=-1); void set_looping(bool toggle, int channel=0); + const int m_channelmax = 4; + //These have to be public for the stupid sync thing // QWORD loop_start = 0; // QWORD loop_end = 0; +public slots: + void play(QString p_song, int channel=0, bool crossfade=false); + void stop(int channel=0); + private: QWidget *m_parent; AOApplication *ao_app; @@ -36,7 +39,6 @@ private: bool m_looping = false; int m_volume = 0; - const int m_channelmax = 4; // Channel 0 = music // Channel 1 = ambience // Channel 2 = extra diff --git a/include/courtroom.h b/include/courtroom.h index d02c756..465b8ce 100644 --- a/include/courtroom.h +++ b/include/courtroom.h @@ -26,6 +26,7 @@ #include "datatypes.h" #include "debug_functions.h" #include "chatlogpiece.h" +#include "scrolltext.h" #include #include @@ -445,6 +446,9 @@ private: QListWidget *ui_area_list; QListWidget *ui_music_list; + ScrollText *ui_music_name; + AOMovie *ui_music_display; + AOButton *ui_pair_button; QListWidget *ui_pair_list; QSpinBox *ui_pair_offset_spinbox; diff --git a/include/scrolltext.h b/include/scrolltext.h new file mode 100644 index 0000000..cf791c0 --- /dev/null +++ b/include/scrolltext.h @@ -0,0 +1,50 @@ +#ifndef SCROLLTEXT_H +#define SCROLLTEXT_H + +#include +#include +#include +#include +#include + + +class ScrollText : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QString separator READ separator WRITE setSeparator) + +public: + explicit ScrollText(QWidget *parent = nullptr); + +public slots: + QString text() const; + void setText(QString text); + + QString separator() const; + void setSeparator(QString separator); + + +protected: + virtual void paintEvent(QPaintEvent *); + virtual void resizeEvent(QResizeEvent *); + +private: + void updateText(); + QString _text; + QString _separator; + QStaticText staticText; + int singleTextWidth; + QSize wholeTextSize; + int leftMargin; + bool scrollEnabled; + int scrollPos; + QImage alphaChannel; + QImage buffer; + QTimer timer; + +private slots: + virtual void timer_timeout(); +}; + +#endif // SCROLLTEXT_H diff --git a/src/aomusicplayer.cpp b/src/aomusicplayer.cpp index f6bcb17..c845948 100644 --- a/src/aomusicplayer.cpp +++ b/src/aomusicplayer.cpp @@ -18,6 +18,8 @@ AOMusicPlayer::~AOMusicPlayer() void AOMusicPlayer::play(QString p_song, int channel, bool crossfade) { channel = channel % m_channelmax; + if (channel < 0) //wtf? + return; QString f_path = ao_app->get_music_path(p_song); // QString d_path = f_path + ".txt"; @@ -39,27 +41,33 @@ void AOMusicPlayer::play(QString p_song, int channel, bool crossfade) // } unsigned int flags = BASS_STREAM_PRESCAN | BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE; + if (m_looping) + flags |= BASS_SAMPLE_LOOP; DWORD newstream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags); if (ao_app->get_audio_output_device() != "default") BASS_ChannelSetDevice(m_stream_list[channel], BASS_GetDevice()); - if (BASS_ChannelIsActive(m_stream_list[channel]) == BASS_ACTIVE_PLAYING && crossfade) + if (crossfade) { DWORD oldstream = m_stream_list[channel]; - - //Fade out the other sample and stop it - BASS_ChannelSlideAttribute(oldstream, BASS_ATTRIB_VOL|BASS_SLIDE_LOG, -1, 5000); - BASS_ChannelLock(oldstream, true); //Mute the new sample BASS_ChannelSetAttribute(newstream, BASS_ATTRIB_VOL, 0); - //Sync it with the new sample - BASS_ChannelSetPosition(newstream, BASS_ChannelGetPosition(oldstream, BASS_POS_BYTE), BASS_POS_BYTE); + //Crossfade time + if (BASS_ChannelIsActive(oldstream) == BASS_ACTIVE_PLAYING) + { + //Fade out the other sample and stop it + BASS_ChannelSlideAttribute(oldstream, BASS_ATTRIB_VOL|BASS_SLIDE_LOG, -1, 5000); + BASS_ChannelLock(oldstream, true); + //Sync it with the new sample + BASS_ChannelSetPosition(newstream, BASS_ChannelGetPosition(oldstream, BASS_POS_BYTE), BASS_POS_BYTE); + BASS_ChannelLock(oldstream, false); + } //Start it BASS_ChannelPlay(newstream, false); //Fade in our sample BASS_ChannelSlideAttribute(newstream, BASS_ATTRIB_VOL, static_cast(m_volume / 100.0f), 1000); - BASS_ChannelLock(oldstream, false); + m_stream_list[channel] = newstream; } else @@ -67,7 +75,7 @@ void AOMusicPlayer::play(QString p_song, int channel, bool crossfade) BASS_ChannelStop(m_stream_list[channel]); m_stream_list[channel] = newstream; BASS_ChannelPlay(m_stream_list[channel], false); - this->set_volume(m_volume); + this->set_volume(m_volume, channel); } this->set_looping(m_looping); //Have to do this here due to any crossfading-related changes, etc. @@ -82,7 +90,17 @@ void AOMusicPlayer::set_volume(int p_value, int channel) { m_volume = p_value; float volume = m_volume / 100.0f; - BASS_ChannelSetAttribute(m_stream_list[channel], BASS_ATTRIB_VOL, volume); + if (channel < 0) + { + for (int n_stream = 0 ; n_stream < m_channelmax ; ++n_stream) + { + BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, volume); + } + } + else + { + BASS_ChannelSetAttribute(m_stream_list[channel], BASS_ATTRIB_VOL, volume); + } } //void CALLBACK loopProc(HSYNC handle, DWORD channel, DWORD data, void *user) diff --git a/src/courtroom.cpp b/src/courtroom.cpp index d58353f..fa35349 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -125,6 +125,14 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_area_list->hide(); ui_music_list = new QListWidget(this); + ui_music_display = new AOMovie(this, ao_app); + ui_music_display->set_play_once(false); + ui_music_display->setAttribute(Qt::WA_TransparentForMouseEvents); + + ui_music_name = new ScrollText(ui_music_display); + ui_music_name->setText(tr("None")); + ui_music_name->setAttribute(Qt::WA_TransparentForMouseEvents); + ui_ic_chat_name = new QLineEdit(this); ui_ic_chat_name->setFrame(false); ui_ic_chat_name->setPlaceholderText(tr("Showname")); @@ -544,6 +552,24 @@ void Courtroom::set_widgets() set_size_and_pos(ui_area_list, "music_list"); set_size_and_pos(ui_music_list, "music_list"); + set_size_and_pos(ui_music_name, "music_name"); + + ui_music_display->move(0, 0); + design_ini_result = ao_app->get_element_dimensions("music_display", "courtroom_design.ini"); + + if (design_ini_result.width < 0 || design_ini_result.height < 0) + { + qDebug() << "W: could not find \"music_name\" in courtroom_design.ini"; + ui_music_display->hide(); + } + else + { + ui_music_display->move(design_ini_result.x, design_ini_result.y); + ui_music_display->combo_resize(design_ini_result.width, design_ini_result.height); + } + + ui_music_display->play("music_display"); + if (is_ao2_bg) { set_size_and_pos(ui_ic_chat_message, "ao2_ic_chat_message"); @@ -827,6 +853,7 @@ void Courtroom::set_fonts() set_font(ui_server_chatlog, "", "server_chatlog"); set_font(ui_music_list, "", "music_list"); set_font(ui_area_list, "", "area_list"); + set_font(ui_music_name, "", "music_name"); set_dropdowns(); } @@ -1081,7 +1108,12 @@ void Courtroom::enter_courtroom() list_music(); list_areas(); - music_player->set_volume(ui_music_slider->value()); + music_player->set_volume(ui_music_slider->value(), 0); //set music + //Set the ambience and other misc. music layers + for (int i = 1; i < music_player->m_channelmax; ++i) + { + music_player->set_volume(ui_sfx_slider->value(), i); + } sfx_player->set_volume(ui_sfx_slider->value()); objection_player->set_volume(ui_sfx_slider->value()); blip_player->set_volume(ui_blip_slider->value()); @@ -1145,14 +1177,12 @@ void Courtroom::list_areas() for (int n_area = 0 ; n_area < area_list.size() ; ++n_area) { QString i_area = ""; - i_area.append("["); - i_area.append(QString::number(n_area)); - i_area.append("] "); - i_area.append(area_list.at(n_area)); if (ao_app->arup_enabled) { + i_area.prepend("[" + QString::number(n_area) + "] "); //Give it the index + i_area.append("\n "); i_area.append(arup_statuses.at(n_area)); @@ -2873,30 +2903,33 @@ void Courtroom::handle_song(QStringList *p_contents) f_song_clear = f_song_clear.left(f_song_clear.lastIndexOf(".")); int n_char = f_contents.at(1).toInt(); + bool looping = true; + int channel = 0; + bool crossfade = false; if (n_char < 0 || n_char >= char_list.size()) { - music_player->set_looping(true); int channel = 0; if (p_contents->length() > 3 && p_contents->at(3) != "-1") - music_player->set_looping(false); + looping = false; if (p_contents->length() > 4) //eyyy we want to change this song's CHANNEL huh channel = p_contents->at(4).toInt(); //let the music player handle it if it's bigger than the channel list - bool crossfade = false; if (p_contents->length() > 5) //CROSSFADE!? Are you MAD? { - qDebug() << p_contents->at(5); crossfade = p_contents->at(5) == "1"; //let the music player handle it if it's bigger than the channel list } + qDebug() << f_song << channel << p_contents->at(3) << looping; + music_player->set_looping(looping, channel); music_player->play(f_song, channel, crossfade); + if (channel == 0) + ui_music_name->setText(f_song); } else { QString str_char = char_list.at(n_char).name; QString str_show = char_list.at(n_char).name; - music_player->set_looping(true); if (p_contents->length() > 2) { @@ -2905,20 +2938,15 @@ void Courtroom::handle_song(QStringList *p_contents) str_show = p_contents->at(2); } } - if (p_contents->length() > 3) + if (p_contents->length() > 3 && p_contents->at(3) != "-1") { //I am really confused why "-1" is "loop this song" and why anything else passes as "don't loop" //(if we even have this length) but alright - if(p_contents->at(3) != "-1") - { - music_player->set_looping(false); - } + looping = false; } - int channel = 0; if (p_contents->length() > 4) //eyyy we want to change this song's CHANNEL huh channel = p_contents->at(4).toInt(); //let the music player handle it if it's bigger than the channel list - bool crossfade = false; if (p_contents->length() > 5) //CROSSFADE!? Are you MAD? crossfade = p_contents->at(5) == "1"; //let the music player handle it if it's bigger than the channel list @@ -2934,7 +2962,10 @@ void Courtroom::handle_song(QStringList *p_contents) } append_ic_text(f_song_clear, str_show, true); + music_player->set_looping(looping, channel); music_player->play(f_song, channel, crossfade); + if (channel == 0) + ui_music_name->setText(f_song); } } } @@ -3915,13 +3946,18 @@ void Courtroom::on_text_color_changed(int p_color) void Courtroom::on_music_slider_moved(int p_value) { - music_player->set_volume(p_value); + music_player->set_volume(p_value, 0); //Set volume on music layer ui_ic_chat_message->setFocus(); } void Courtroom::on_sfx_slider_moved(int p_value) { sfx_player->set_volume(p_value); + //Set the ambience and other misc. music layers + for (int i = 1; i < music_player->m_channelmax; ++i) + { + music_player->set_volume(p_value, i); + } objection_player->set_volume(p_value); ui_ic_chat_message->setFocus(); } @@ -3986,7 +4022,6 @@ void Courtroom::on_change_character_clicked() { music_player->set_volume(0); sfx_player->set_volume(0); - sfx_player->set_volume(0); blip_player->set_volume(0); set_char_select(); diff --git a/src/scrolltext.cpp b/src/scrolltext.cpp new file mode 100644 index 0000000..6cf075d --- /dev/null +++ b/src/scrolltext.cpp @@ -0,0 +1,134 @@ +#include "scrolltext.h" + + +ScrollText::ScrollText(QWidget *parent) : + QWidget(parent), scrollPos(0) +{ + staticText.setTextFormat(Qt::PlainText); + +// setFixedHeight(fontMetrics().height()*2); //The theme sets this + leftMargin = height() / 3; + + setSeparator(" --- "); + + connect(&timer, SIGNAL(timeout()), this, SLOT(timer_timeout())); + timer.setInterval(50); +} + +QString ScrollText::text() const +{ + return _text; +} + +void ScrollText::setText(QString text) +{ + _text = text; + updateText(); + update(); +} + +QString ScrollText::separator() const +{ + return _separator; +} + +void ScrollText::setSeparator(QString separator) +{ + _separator = separator; + updateText(); + update(); +} + +void ScrollText::updateText() +{ + timer.stop(); + + singleTextWidth = fontMetrics().horizontalAdvance(_text); + scrollEnabled = (singleTextWidth > width() - leftMargin*2); + + if(scrollEnabled) + { + scrollPos = -64; + staticText.setText(_text + _separator); + timer.start(); + } + else + staticText.setText(_text); + + staticText.prepare(QTransform(), font()); + wholeTextSize = QSize(fontMetrics().horizontalAdvance(staticText.text()), fontMetrics().height()); +} + +void ScrollText::paintEvent(QPaintEvent*) +{ + QPainter p(this); + + if(scrollEnabled) + { + buffer.fill(qRgba(0, 0, 0, 0)); + QPainter pb(&buffer); + pb.setPen(p.pen()); + pb.setFont(p.font()); + + int x = qMin(-scrollPos, 0) + leftMargin; + while(x < width()) + { + pb.drawStaticText(QPointF(x, (height() - wholeTextSize.height()) / 2), staticText); + x += wholeTextSize.width(); + } + + //Apply Alpha Channel + pb.setCompositionMode(QPainter::CompositionMode_DestinationIn); + pb.setClipRect(width() - 15, 0, 15, height()); + pb.drawImage(0, 0, alphaChannel); + pb.setClipRect(0, 0, 15, height()); + //initial situation: don't apply alpha channel in the left half of the image at all; apply it more and more until scrollPos gets positive + if(scrollPos < 0) + pb.setOpacity(static_cast((qMax(-8, scrollPos) + 8) / 8.0)); + pb.drawImage(0, 0, alphaChannel); + + //pb.end(); + p.drawImage(0, 0, buffer); + } + else + { + p.drawStaticText(QPointF(leftMargin, (height() - wholeTextSize.height()) / 2), staticText); + } +} + +void ScrollText::resizeEvent(QResizeEvent*) +{ + //When the widget is resized, we need to update the alpha channel. + + alphaChannel = QImage(size(), QImage::Format_ARGB32_Premultiplied); + buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied); + + //Create Alpha Channel: + if(width() > 64) + { + //create first scanline + QRgb* scanline1 = reinterpret_cast(alphaChannel.scanLine(0)); + for(int x = 1; x < 16; ++x) + scanline1[x - 1] = scanline1[width() - x] = qRgba(0, 0, 0, x << 4); + for(int x = 15; x < width() - 15; ++x) + scanline1[x] = qRgb(0, 0, 0); + //copy scanline to the other ones + for(int y = 1; y < height(); ++y) + memcpy(alphaChannel.scanLine(y), scanline1, static_cast(width() * 4)); + } + else + alphaChannel.fill(qRgb(0, 0, 0)); + + + //Update scrolling state + bool newScrollEnabled = (singleTextWidth > width() - leftMargin); + if(newScrollEnabled != scrollEnabled) + updateText(); +} + +void ScrollText::timer_timeout() +{ + scrollPos = (scrollPos + 2) + % wholeTextSize.width(); + update(); +}