-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAudioManager.hpp
160 lines (131 loc) · 4.14 KB
/
AudioManager.hpp
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
* Rhythm Run for Nintendo 3DS
* Lauren Kelly, 2020–2021
*/
#pragma once
#include <3ds.h>
#include <algorithm>
// #include <atomic>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <type_traits>
#include <unordered_map>
#include <opusfile.h>
#include "debug.hpp"
/// Finite state machine to handle background music management
class AudioManager {
public:
AudioManager();
~AudioManager();
// Passthrough function to execute audioThread instance method from threadCreate
static inline void proxyAudioThread(void* a_ctx)
{
static_cast<AudioManager*>(a_ctx)->audioThread();
};
// Passthrough function to execute audioCallback instance method from NDSP
static inline void proxyAudioCallback(void* a_ctx)
{
static_cast<AudioManager*>(a_ctx)->audioCallback();
};
// Add file to the state machine, returning its ID
unsigned int addFile(std::shared_ptr<OggOpusFile> a_file);
// Transition focus to file
// Retval: 0 for success, otherwise error
int switchFileTo(unsigned int a_fileId);
// Remove file from the state machine
void removeFile(unsigned int a_fileId);
/// Stop playback and free the playback thread.
void stop();
/// Is playback 'paused' (whether stopped or not playing)?
inline bool isStopped()
{
return shouldStop;
};
inline bool isPaused()
{
return isStopped() ? true : ndspChnIsPaused(BGM_CHANNEL);
};
void play();
// void pause() {
// shouldStop = true;
// }
inline void pause()
{
eprintf("pa\n");
// shouldPause = true;
ndspChnSetPaused(BGM_CHANNEL, true);
};
inline void unpause()
{
eprintf("upa\n");
// shouldPause = true;
ndspChnSetPaused(BGM_CHANNEL, false);
LightEvent_Signal(&audioEvent);
};
inline void togglePause()
{
eprintf("tpa\n");
// shouldPause = true;
if (!shouldStop)
ndspChnSetPaused(BGM_CHANNEL, !isPaused());
LightEvent_Signal(&audioEvent);
};
inline void seekZero()
{
pause();
op_raw_seek(currentFile.get(), 0);
play();
};
inline void setLoop(bool a_value) { shouldLoop = a_value; };
inline void toggleLoop() { setLoop(!shouldLoop); };
/// LightEvent for signalling the audio thread
LightEvent audioEvent;
/// Should playback be stopped?
bool shouldStop;
/// Should playback loop?
bool shouldLoop;
/// Is playback currently switching audio file?
bool isSkipping;
inline void audioCallback()
{
// Do not signal the audio thread if we want to interrupt its work
// (e.g, when playback is stopped or if we're changing audio data)
if (shouldStop || isSkipping)
return;
// Signal the audio thread to do more work
LightEvent_Signal(&audioEvent);
// svcSleepThread(0);
}
private:
void initPlayback();
void doPlayback();
LightEvent playEvent;
// default to paused
// todo: switch to std::atomic
// bool shouldPause = true;
// bool shouldQuit = false;
ndspWaveBuf waveBufs[3];
int16_t* audioBuffer = nullptr;
Thread threadId;
bool fillBuffer(OggOpusFile* const a_opusFile, ndspWaveBuf& a_waveBuf);
void audioThread();
/// Stores all files
std::unordered_map<unsigned int, std::shared_ptr<OggOpusFile>> files;
/// Stores a reference to the current file
std::shared_ptr<OggOpusFile> currentFile;
/// Stores the current file ID, incremented whenever a file is added
unsigned int fileCounter;
// Defines
static constexpr auto BGM_CHANNEL = 0;
static constexpr auto SAMPLE_RATE = 48000;
static constexpr auto BUFFER_MS = 120;
static constexpr auto SAMPLES_PER_BUF = SAMPLE_RATE * BUFFER_MS / 1000;
static_assert(SAMPLES_PER_BUF == 5760);
static constexpr auto CHANNELS_PER_SAMPLE = 2;
static constexpr auto WAVEBUF_SIZE = SAMPLES_PER_BUF * CHANNELS_PER_SAMPLE * sizeof(std::int16_t);
// threading
static constexpr auto THREAD_AFFINITY = -2;
static constexpr auto THREAD_STACK_SIZE = 32 * 1024;
};