atrooney-online-2/src/aomusicplayer.cpp
Salanto 4fa86c0174
Add option to disable music streaming. (#829)
* Add option to disable streamintg

This one gets a unique message to prevent people forgetting they disable it and the wondering why streaming broke.

* Fix code order

* Fixes the music channel still playing audio when a new stream was blocked.

* Update tooltip

* Update tooltip to reflect the current way the feature works.
2022-07-30 18:42:55 +02:00

239 lines
7.9 KiB
C++

#include "aomusicplayer.h"
AOMusicPlayer::AOMusicPlayer(QWidget *parent, AOApplication *p_ao_app)
{
m_parent = parent;
ao_app = p_ao_app;
}
AOMusicPlayer::~AOMusicPlayer()
{
for (int n_stream = 0; n_stream < m_channelmax; ++n_stream) {
BASS_ChannelStop(m_stream_list[n_stream]);
}
}
QString AOMusicPlayer::play(QString p_song, int channel, bool loop,
int effect_flags)
{
channel = channel % m_channelmax;
if (channel < 0) // wtf?
return "[ERROR] Invalid Channel";
unsigned int flags = BASS_STREAM_PRESCAN | BASS_STREAM_AUTOFREE |
BASS_UNICODE | BASS_ASYNCFILE;
unsigned int streaming_flags = BASS_STREAM_AUTOFREE;
if (loop) {
flags |= BASS_SAMPLE_LOOP;
streaming_flags |= BASS_SAMPLE_LOOP;
}
QString f_path = p_song;
DWORD newstream;
if (f_path.startsWith("http")) {
if (ao_app->is_streaming_disabled()) {
BASS_ChannelStop(m_stream_list[channel]);
return QObject::tr("[MISSING] Streaming disabled.");
}
if (f_path.endsWith(".opus"))
newstream = BASS_OPUS_StreamCreateURL(f_path.toStdString().c_str(), 0, streaming_flags, nullptr, 0);
else if (f_path.endsWith(".mid"))
newstream = BASS_MIDI_StreamCreateURL(f_path.toStdString().c_str(), 0, streaming_flags, nullptr, 0, 1);
else
newstream = BASS_StreamCreateURL(f_path.toStdString().c_str(), 0, streaming_flags, nullptr, 0);
} else {
f_path = ao_app->get_real_path(ao_app->get_music_path(p_song));
if (f_path.endsWith(".opus"))
newstream = BASS_OPUS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags);
else if (f_path.endsWith(".mid"))
newstream = BASS_MIDI_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags, 1);
else if (f_path.endsWith(".mo3") || f_path.endsWith(".xm") || f_path.endsWith(".mod") || f_path.endsWith(".s3m") || f_path.endsWith(".it") || f_path.endsWith(".mtm") || f_path.endsWith(".umx") )
newstream = BASS_MusicLoad(FALSE,f_path.utf16(), 0, 0, flags, 1);
else
newstream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags);
}
int error_code = BASS_ErrorGetCode();
if (ao_app->get_audio_output_device() != "default")
BASS_ChannelSetDevice(m_stream_list[channel], BASS_GetDevice());
QString d_path = f_path + ".txt";
loop_start[channel] = 0;
loop_end[channel] = 0;
if (loop && file_exists(d_path)) // Contains loop/etc. information file
{
QStringList lines = ao_app->read_file(d_path).split("\n");
foreach (QString line, lines) {
QStringList args = line.split("=");
if (args.size() < 2)
continue;
QString arg = args[0].trimmed();
float sample_rate;
BASS_ChannelGetAttribute(newstream, BASS_ATTRIB_FREQ, &sample_rate);
// Grab number of bytes for sample size
int sample_size = 16 / 8;
// number of channels (stereo/mono)
int num_channels = 2;
// Calculate the bytes for loop_start/loop_end to use with the sync proc
QWORD bytes = static_cast<QWORD>(args[1].trimmed().toUInt() *
sample_size * num_channels);
if (arg == "loop_start")
loop_start[channel] = bytes;
else if (arg == "loop_length")
loop_end[channel] = loop_start[channel] + bytes;
else if (arg == "loop_end")
loop_end[channel] = bytes;
}
qDebug() << "Found data file for song" << p_song << "length"
<< BASS_ChannelGetLength(newstream, BASS_POS_BYTE) << "loop start"
<< loop_start[channel] << "loop end" << loop_end[channel];
}
if (BASS_ChannelIsActive(m_stream_list[channel]) == BASS_ACTIVE_PLAYING) {
DWORD oldstream = m_stream_list[channel];
if (effect_flags & SYNC_POS) {
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);
}
if ((effect_flags & FADE_OUT) && m_volume[channel] > 0) {
// Fade out the other sample and stop it (due to -1)
BASS_ChannelSlideAttribute(oldstream, BASS_ATTRIB_VOL | BASS_SLIDE_LOG,
-1, 4000);
}
else
BASS_ChannelStop(
oldstream); // Stop the sample since we don't need it anymore
}
else
BASS_ChannelStop(m_stream_list[channel]);
m_stream_list[channel] = newstream;
BASS_ChannelPlay(newstream, false);
if (effect_flags & FADE_IN) {
// Fade in our sample
BASS_ChannelSetAttribute(newstream, BASS_ATTRIB_VOL, 0);
BASS_ChannelSlideAttribute(newstream, BASS_ATTRIB_VOL,
static_cast<float>(m_volume[channel] / 100.0f),
1000);
}
else
this->set_volume(m_volume[channel], channel);
BASS_ChannelSetSync(newstream, BASS_SYNC_DEV_FAIL, 0,
ao_app->BASSreset, 0);
this->set_looping(loop, channel); // Have to do this here due to any
// crossfading-related changes, etc.
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)
{
BASS_ChannelStop(m_stream_list[channel]);
}
void AOMusicPlayer::set_muted(bool toggle)
{
m_muted = toggle;
// Update all volume based on the mute setting
for (int n_stream = 0; n_stream < m_channelmax; ++n_stream) {
set_volume(m_volume[n_stream], n_stream);
}
}
void AOMusicPlayer::set_volume(int p_value, int channel)
{
m_volume[channel] = p_value;
// If muted, volume will always be 0
float volume = (m_volume[channel] / 100.0f) * !m_muted;
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)
{
Q_UNUSED(handle);
Q_UNUSED(data);
QWORD loop_start = *(static_cast<unsigned *>(user));
BASS_ChannelLock(channel, true);
BASS_ChannelSetPosition(channel, loop_start, BASS_POS_BYTE);
BASS_ChannelLock(channel, false);
}
void AOMusicPlayer::set_looping(bool loop_song, int channel)
{
if (!loop_song) {
if (BASS_ChannelFlags(m_stream_list[channel], 0, 0) & BASS_SAMPLE_LOOP)
BASS_ChannelFlags(m_stream_list[channel], 0,
BASS_SAMPLE_LOOP); // remove the LOOP flag
BASS_ChannelRemoveSync(m_stream_list[channel], loop_sync[channel]);
loop_sync[channel] = 0;
return;
}
BASS_ChannelFlags(m_stream_list[channel], BASS_SAMPLE_LOOP,
BASS_SAMPLE_LOOP); // set the LOOP flag
if (loop_sync[channel] != 0) {
BASS_ChannelRemoveSync(m_stream_list[channel],
loop_sync[channel]); // remove the sync
loop_sync[channel] = 0;
}
if (loop_start[channel] > 0) {
if (loop_end[channel] > 0 && (loop_end[channel] > loop_start[channel]))
{
//Loop when the endpoint is reached.
loop_sync[channel] = BASS_ChannelSetSync(
m_stream_list[channel], BASS_SYNC_END | BASS_SYNC_MIXTIME,
loop_end[channel] , loopProc, &loop_start[channel]);
}
else
{
//Loop when the end of the file is reached.
loop_sync[channel] = BASS_ChannelSetSync(
m_stream_list[channel], BASS_SYNC_POS | BASS_SYNC_MIXTIME,
0 , loopProc, &loop_start[channel]);
}
}
}