aboutsummaryrefslogtreecommitdiff
path: root/src/aomusicplayer.h
blob: b45c22fe1178069479e0cc60b2b7e7517a639396 (plain)
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);
};