1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
#pragma once
#include "aoapplication.h"
#include <QNetworkAccessManager>
#include <QPointer>
class AOMusicPlayer : public QObject
{
Q_OBJECT
public:
// 0 = music
// 1 = ambience
static constexpr int STREAM_COUNT = 2;
explicit AOMusicPlayer(AOApplication *ao_app);
~AOMusicPlayer();
void setMuted(bool enabled);
QString playStream(QString song, int streamId, bool loopEnabled, int effectFlags);
void setStreamVolume(int value, int streamId);
void setStreamLooping(bool enabled, int streamId);
void play_from_url(const QString &url, int id, bool looping, int flags);
void play_from_file(const QString &path, int id, bool looping, int flags);
private:
struct Stream
{
ma_sound sound;
QByteArray buffer;
ma_decoder decoder;
// I've had use-after-free crashes on `stop` due to QNetworkReply's lifetime
// being unclear (specifically, calling `abort` on it). QPointer is a
// bandaid fix.
QPointer<QNetworkReply> reply;
bool has_decoder;
enum
{
standby,
playing,
} state = standby;
};
// Default flags for music: STREAM decodes incrementally in 2-second chunks,
// the other two disable 3D positioning and Doppler effect.
const ma_uint32 m_flags = MA_SOUND_FLAG_STREAM | MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_NO_PITCH;
AOApplication *ao_app;
float m_volume[STREAM_COUNT]{};
bool m_muted = false;
Stream m_stream[STREAM_COUNT]{};
ma_pcm_rb m_audio_ring;
quint32 m_loop_start[STREAM_COUNT]{};
quint32 m_loop_end[STREAM_COUNT]{};
bool ensureValidStreamId(int streamId);
void on_url_download_finished(int id, QNetworkReply *reply, bool looping);
void start_playback(int id);
void stop(int id);
signals:
void track_ready(QString label);
};
|