载入中...
搜索中...
未找到
Audio.cpp
浏览该文件的文档.
1#include "Audio.h"
2#include "Source.h"
3
4#include "AudioCapabilities.h"
5#include "common/Exception.h"
7#include "sound/Decoder.h"
8#include "sound/Sound.h"
9#include "sound/SoundData.h"
10
11#include <AL/al.h>
12#include <simplesquirrel/simplesquirrel.hpp>
13
14#include <algorithm>
15#include <chrono>
16
17namespace eve {
18namespace audio {
19
21
24 StartupStage stage("audio: OpenAL device/context + worker thread");
25 device = alcOpenDevice(nullptr);
26 if (!device)
27 throw eve::Exception("Could not open OpenAL device");
28 context = alcCreateContext(device, nullptr);
29 if (!context || !alcMakeContextCurrent(context))
30 throw eve::Exception("Could not create OpenAL context");
31 alListenerf(AL_GAIN, masterVolume);
32 worker = std::thread([this] { workerMain(); });
33}
34
36 running = false;
37 cv.notify_all();
38 if (worker.joinable())
39 worker.join();
40
41 {
42 std::lock_guard<std::mutex> lock(mutex);
43 // Sources are owned by callers; just clear tracking.
44 streamSources.clear();
45 allSources.clear();
46 }
47
48 alcMakeContextCurrent(nullptr);
49 if (context) {
50 alcDestroyContext(context);
51 context = nullptr;
52 }
53 if (device) {
54 alcCloseDevice(device);
55 device = nullptr;
56 }
57}
58
59void Audio::workerMain() {
60 while (running.load()) {
61 std::unique_lock<std::mutex> lock(mutex);
62 cv.wait_for(lock, std::chrono::milliseconds(10),
63 [this] { return !running.load() || !streamSources.empty(); });
64 if (!running.load())
65 break;
66 // Keep Audio::mutex held while filling. Source::~Source() removes the
67 // source from streamSources under the same mutex, so a source cannot be
68 // destroyed (and its decoderMutex/pendingMutex freed) while the worker
69 // is mid-fill. A snapshot-then-release pattern would let the destructor
70 // finish between the snapshot and the fill; the worker would then lock
71 // mutexes that no longer exist (std::system_error: mutex lock failed).
72 for (Source *s : streamSources) {
73 if (s)
74 s->fillPendingFromDecoder();
75 }
76 }
77}
78
80 std::lock_guard<std::mutex> lock(mutex);
81 streamSources.push_back(s);
82}
83
85 std::lock_guard<std::mutex> lock(mutex);
86 streamSources.erase(std::remove(streamSources.begin(), streamSources.end(), s), streamSources.end());
87 allSources.erase(std::remove(allSources.begin(), allSources.end(), s), allSources.end());
88}
89
93
94void Audio::notifyWorker() { cv.notify_all(); }
95
97 auto *s = new Source(this, data);
98 std::lock_guard<std::mutex> lock(mutex);
99 allSources.push_back(s);
100 return s;
101}
102
104 if (type != "static" && type != "stream")
105 throw eve::Exception("Invalid source type '%s'", type.c_str());
106 if (type == "static") {
107 auto *sound = sound::Sound::create();
108 auto *sd = sound->newSoundDataFromDecoder(decoder);
109 auto *s = newSource(sd);
110 // SoundData owned by caller eventually; keep sd alive via Source holding pointer only —
111 // Source does not own SoundData. Caller must keep SoundData alive.
112 // For static-from-decoder convenience, leak prevention: Source should own or we document.
113 // Spec: squirrel manages SoundData. Here C++ path: attach as dependency by not deleting.
114 // Store sd on Source via staticData without ownership — caller must delete sd after Source.
115 // Better: Source holds SoundData* without delete; document. For this factory, transfer:
116 // We'll let Source keep staticData pointer; user deletes Source then SoundData.
117 return s;
118 }
119 auto *s = new Source(this, decoder, true);
120 std::lock_guard<std::mutex> lock(mutex);
121 allSources.push_back(s);
122 return s;
123}
124
126 if (type != "static" && type != "stream")
127 throw eve::Exception("Invalid source type '%s'", type.c_str());
128 auto *sound = sound::Sound::create();
129 if (type == "static") {
130 auto *sd = sound->newSoundData(data);
131 return newSource(sd);
132 }
133 auto *dec = sound->newDecoder(data);
134 auto *s = new Source(this, dec, true, true);
135 std::lock_guard<std::mutex> lock(mutex);
136 allSources.push_back(s);
137 return s;
138}
139
141 if (s)
142 s->play();
143}
145 if (s)
146 s->stop();
147}
149 if (s)
150 s->pause();
151}
153 // Hold the mutex while stopping: Source::~Source() → unregisterSource()
154 // blocks on the same mutex, so a concurrently destroyed source cannot be
155 // touched here after it has been removed from allSources.
156 std::lock_guard<std::mutex> lock(mutex);
157 for (Source *s : allSources) {
158 if (s)
159 s->stop();
160 }
161}
162
163void Audio::setVolume(float v) {
164 masterVolume = std::max(0.f, v);
165 alListenerf(AL_GAIN, masterVolume);
166}
167float Audio::getVolume() const { return masterVolume; }
168
169void Audio::setPosition(float x, float y, float z) { alListener3f(AL_POSITION, x, y, z); }
170void Audio::setVelocity(float x, float y, float z) { alListener3f(AL_VELOCITY, x, y, z); }
171void Audio::setOrientation(float fx, float fy, float fz, float ux, float uy, float uz) {
172 float ori[6] = {fx, fy, fz, ux, uy, uz};
173 alListenerfv(AL_ORIENTATION, ori);
174}
175
177 // Same guarantee as workerMain: destruction removes the source under this
178 // mutex, so the pointer stays valid for the whole iteration.
179 std::lock_guard<std::mutex> lock(mutex);
180 for (Source *s : streamSources) {
181 if (s)
182 s->pump();
183 }
184}
185
186void Audio::expose(ssq::Table &table) {
187 auto cls = table.addClass(name, Audio::create, false);
188 expose(cls);
189
190 auto src = table.addClass<Source>(
191 "Source", std::function<Source *()>([]() -> Source * { return nullptr; }), true);
192 src.addFunc("play", &Source::play);
193 src.addFunc("pause", &Source::pause);
194 src.addFunc("stop", &Source::stop);
195 src.addFunc("isPlaying", &Source::isPlaying);
196 src.addFunc("setVolume", &Source::setVolume);
197 src.addFunc("getVolume", &Source::getVolume);
198 src.addFunc("setPitch", &Source::setPitch);
199 src.addFunc("getPitch", &Source::getPitch);
200 src.addFunc("setLooping", &Source::setLooping);
201 src.addFunc("isLooping", &Source::isLooping);
202 src.addFunc("seek", &Source::seek);
203 src.addFunc("tell", &Source::tell);
204 src.addFunc("getDuration", &Source::getDuration);
205 src.addFunc("setPosition", &Source::setPosition);
206 src.addFunc("setVelocity", &Source::setVelocity);
207 src.addFunc("setDirection", &Source::setDirection);
208 src.addFunc("setRelative", &Source::setRelative);
209 src.addFunc("setAttenuationDistances", &Source::setAttenuationDistances);
210}
211
212void Audio::expose(ssq::Class &cls) {
213 cls.addFunc("getName", &Audio::getName);
214 cls.addFunc("newSource", &Audio::newSource);
215 cls.addFunc("newSourceFromDecoder", &Audio::newSourceFromDecoder);
216 cls.addFunc("newSourceFromData", &Audio::newSourceFromData);
217 cls.addFunc("play", &Audio::play);
218 cls.addFunc("stop", &Audio::stop);
219 cls.addFunc("stopAll", &Audio::stopAll);
220 cls.addFunc("pause", &Audio::pause);
221 cls.addFunc("setVolume", &Audio::setVolume);
222 cls.addFunc("getVolume", &Audio::getVolume);
223 cls.addFunc("setPosition", &Audio::setPosition);
224 cls.addFunc("setVelocity", &Audio::setVelocity);
225 cls.addFunc("setOrientation", &Audio::setOrientation);
226}
227
228} // namespace audio
229} // namespace eve
HSQOBJECT cls
Definition ECS.cpp:21
std::string type
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:24
Light2D::Data * data
const char * name
Definition RockMesh.cpp:21
int v
uint32_t s
Definition Weather.cpp:28
virtual std::string getName() const =0
OpenAL audio module: device management, master listener state, and Source factory....
Definition Audio.h:27
void pause(Source *s)
Pauses a source (no-op when s is null).
Definition Audio.cpp:148
Source * newSourceFromDecoder(sound::Decoder *decoder, std::string type)
Creates a Source from a decoder; type must be "static" or "stream".
Definition Audio.cpp:103
float getVolume() const
Definition Audio.cpp:167
void unregisterSource(Source *s)
Internal: removes a source from all module tracking.
Definition Audio.cpp:84
void setVolume(float v)
Sets master volume (clamped to >= 0) applied to the OpenAL listener.
Definition Audio.cpp:163
void stop(Source *s)
Stops playback of a source (no-op when s is null).
Definition Audio.cpp:144
void setVelocity(float x, float y, float z)
Sets the listener velocity (used by OpenAL doppler).
Definition Audio.cpp:170
void stopAll()
Stops every live source registered with this module.
Definition Audio.cpp:152
void setPosition(float x, float y, float z)
Sets the listener position in world units.
Definition Audio.cpp:169
Source * newSourceFromData(Data *data, std::string type)
Creates a Source from raw encoded data; type must be "static" or "stream".
Definition Audio.cpp:125
Source * newSource(sound::SoundData *data)
Creates a static (non-streaming) Source from decoded sound data.
Definition Audio.cpp:96
void play(Source *s)
Starts playback of a source (no-op when s is null).
Definition Audio.cpp:140
~Audio() override
Definition Audio.cpp:35
void registerStream(Source *s)
Internal: registers a streaming source with the decode worker.
Definition Audio.cpp:79
void unregisterStream(Source *s)
Internal: removes a source from worker tracking.
Definition Audio.cpp:90
void setOrientation(float fx, float fy, float fz, float ux, float uy, float uz)
Sets the listener forward and up orientation vectors.
Definition Audio.cpp:171
void pump()
Advances streaming sources; call once per frame from the main thread.
Definition Audio.cpp:176
void notifyWorker()
Internal: wakes the decode worker thread.
Definition Audio.cpp:94
A playable audio source (static buffer or streaming decoder). Not thread-safe for playback control ex...
Definition Source.h:30
void play()
Starts (or resumes) playback.
Definition Source.cpp:120
void pause()
Pauses playback, keeping the play position.
Definition Source.cpp:135
void setVolume(float v)
Sets source gain (clamped to >= 0).
Definition Source.cpp:181
float getVolume() const
Definition Source.cpp:185
void setVelocity(float x, float y, float z)
Sets the source velocity (used by OpenAL doppler).
Definition Source.cpp:239
bool isPlaying() const
Definition Source.cpp:175
float getPitch() const
Definition Source.cpp:191
void setRelative(bool relative)
Makes the source ignore the listener position (head-relative).
Definition Source.cpp:241
void setPosition(float x, float y, float z)
Sets the source position in world units.
Definition Source.cpp:238
void stop()
Stops playback and rewinds the play position.
Definition Source.cpp:140
double getDuration() const
Total duration in seconds (0 for live/unbounded streams).
Definition Source.cpp:229
double tell() const
Current play time in seconds.
Definition Source.cpp:223
void setAttenuationDistances(float ref, float max)
Sets OpenAL reference and maximum attenuation distances.
Definition Source.cpp:244
bool seek(double seconds)
Seeks to a time in seconds; false when seeking is unsupported.
Definition Source.cpp:200
void setLooping(bool l)
Enables/disables looping.
Definition Source.cpp:193
void setDirection(float x, float y, float z)
Sets the source directional cone orientation.
Definition Source.cpp:240
bool isLooping() const
Definition Source.cpp:198
void setPitch(float p)
Sets playback pitch (clamped to >= 0).
Definition Source.cpp:187
Streaming audio decoder (medialoader-backed). Owns the raw encoded bytes and decodes incrementally vi...
Definition Decoder.h:20
Raw PCM audio buffer with format metadata (samples/rate/bit depth/channels).
Definition SoundData.h:13
void registerAudioCapabilities()
Definition Build.cpp:11