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.
This commit is contained in:
Crystalwarrior 2019-09-20 22:11:37 +03:00
parent 497901e8c3
commit 842b829bee
6 changed files with 277 additions and 34 deletions

View File

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

View File

@ -26,6 +26,7 @@
#include "datatypes.h"
#include "debug_functions.h"
#include "chatlogpiece.h"
#include "scrolltext.h"
#include <QMainWindow>
#include <QLineEdit>
@ -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;

50
include/scrolltext.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef SCROLLTEXT_H
#define SCROLLTEXT_H
#include <QWidget>
#include <QStaticText>
#include <QTimer>
#include <QPainter>
#include <QDebug>
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

View File

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

View File

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

134
src/scrolltext.cpp Normal file
View File

@ -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<qreal>((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<QRgb*>(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<uint>(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();
}