atrooney-online-2/src/aomusicplayer.cpp
oldmud0 d27501313c Finish mounting feature
To pull this one off, a new class called VPath was created that denotes
"virtual" paths that can exist anywhere among the list of mount points.
It is functionally identical to QString, except that implicit conversion
between QString and VPath is not allowed. This makes it easy to spot
errors in path resolution at compile time, since get_real_path must be
called to resolve a VPath into an absolute path that can be passed into
a Qt function as a QString.

Other functions, such as the get_*_suffix functions, also return an
absolute path QString for convenience.

As for the rest of the functions that return a VPath, you will need to
call get_real_path yourself.

Finally, a path resolution cache was added to try to avoid blowing
up what is already a massive lookup cost for assets. The cache
is invalidated when the mount path list is changed.

Currently, this cache isn't bounded. Might need to write an LRU cache
if issues arise.
2021-06-05 14:58:40 -05:00

190 lines
6.3 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]);
}
}
void AOMusicPlayer::play(QString p_song, int channel, bool loop,
int effect_flags)
{
channel = channel % m_channelmax;
if (channel < 0) // wtf?
return;
QString f_path = ao_app->get_real_path(ao_app->get_music_path(p_song));
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;
}
DWORD newstream;
if (f_path.startsWith("http")) {
if (f_path.endsWith(".opus"))
newstream = BASS_OPUS_StreamCreateURL(f_path.toStdString().c_str(), 0, streaming_flags, nullptr, 0);
else
newstream = BASS_StreamCreateURL(f_path.toStdString().c_str(), 0, streaming_flags, nullptr, 0);
} else {
if (f_path.endsWith(".opus"))
newstream = BASS_OPUS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags);
else
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());
QString d_path = f_path + ".txt";
loop_start[channel] = 0;
loop_end[channel] = BASS_ChannelGetLength(newstream, BASS_POS_BYTE);
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().toFloat() *
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 << "loop end" << loop_end;
}
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(m_stream_list[channel], 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(m_stream_list[channel], 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.
}
void AOMusicPlayer::stop(int channel)
{
BASS_ChannelStop(m_stream_list[channel]);
}
void AOMusicPlayer::set_volume(int p_value, int channel)
{
m_volume[channel] = p_value;
float volume = m_volume[channel] / 100.0f;
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)
{
UNUSED(handle);
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 toggle, int channel)
{
m_looping = toggle;
if (!m_looping) {
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;
}
else {
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] =
BASS_ChannelGetLength(m_stream_list[channel], BASS_POS_BYTE);
if (loop_end[channel] >
0) // Don't loop zero length songs even if we're asked to
loop_sync[channel] = BASS_ChannelSetSync(
m_stream_list[channel], BASS_SYNC_POS | BASS_SYNC_MIXTIME,
loop_end[channel], loopProc, &loop_start[channel]);
}
}
}