载入中...
搜索中...
未找到
Source.cpp
浏览该文件的文档.
1#include "Source.h"
2#include "Audio.h"
3
4#include "common/Exception.h"
5#include "sound/Decoder.h"
6#include "sound/SoundData.h"
7
8#include <algorithm>
9#include <cstring>
10
11namespace eve {
12namespace audio {
13
14namespace {
15constexpr size_t kMaxPendingChunks = 8;
16}
17
19 : audio(audio), staticData(data), streaming(false) {
20 if (!audio || !data)
21 throw eve::Exception("Invalid Source arguments");
22 alGenSources(1, &alSource);
23 ensureStaticBuffer();
24 applyGainPitch();
25 alSourcei(alSource, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
26}
27
28Source::Source(Audio *audio, sound::Decoder *decoder, bool streaming, bool takeDecoderOwnership)
29 : audio(audio), decoder(decoder), ownsDecoder(takeDecoderOwnership), streaming(streaming) {
30 if (!audio || !decoder)
31 throw eve::Exception("Invalid Source arguments");
32 alGenSources(1, &alSource);
33 if (streaming) {
34 streamBufferCount = kStreamBufferCount;
35 alGenBuffers(streamBufferCount, streamBuffers);
36 wantsData = true;
37 audio->registerStream(this);
38 audio->notifyWorker();
39 } else {
40 // Decode fully to static buffer via temporary SoundData path in Audio factory.
41 throw eve::Exception("Non-stream Decoder Source should be created via SoundData");
42 }
43 applyGainPitch();
44}
45
47 stop();
48 if (audio) {
49 // Static sources are tracked in allSources but never registered as streams;
50 // always detach so stopAll cannot touch a deleted Source.
51 audio->unregisterSource(this);
52 audio = nullptr;
53 }
54 if (alSource) {
55 alSourcei(alSource, AL_BUFFER, 0);
56 alDeleteSources(1, &alSource);
57 alSource = 0;
58 }
59 if (alBuffer) {
60 alDeleteBuffers(1, &alBuffer);
61 alBuffer = 0;
62 }
63 if (streamBufferCount > 0) {
64 alDeleteBuffers(streamBufferCount, streamBuffers);
65 streamBufferCount = 0;
66 }
67 {
68 // Audio::workerMain()/pump()/stopAll() hold Audio::mutex across the whole
69 // dispatch loop, and unregisterSource() above took that same mutex, so no
70 // in-flight fillPendingFromDecoder() can still reference this Source. The
71 // decoderMutex lock below remains as defense in depth before freeing the
72 // decoder (and the mutexes themselves are destroyed only after this body).
73 std::lock_guard<std::mutex> lock(decoderMutex);
74 if (ownsDecoder) {
75 delete decoder;
76 decoder = nullptr;
77 }
78 }
79}
80
81ALenum Source::alFormat() const {
82 int ch = 0, bits = 0;
83 if (staticData) {
84 ch = staticData->getChannelCount();
85 bits = staticData->getBitDepth();
86 } else {
87 std::lock_guard<std::mutex> lock(decoderMutex);
88 if (decoder) {
89 ch = decoder->getChannelCount();
90 bits = decoder->getBitDepth();
91 }
92 }
93 if (ch == 1 && bits == 8) return AL_FORMAT_MONO8;
94 if (ch == 1 && bits == 16) return AL_FORMAT_MONO16;
95 if (ch == 2 && bits == 8) return AL_FORMAT_STEREO8;
96 if (ch == 2 && bits == 16) return AL_FORMAT_STEREO16;
97 throw eve::Exception("Unsupported PCM format for OpenAL");
98}
99
100int Source::decoderSampleRateOr(int fallback) const {
101 std::lock_guard<std::mutex> lock(decoderMutex);
102 return decoder ? decoder->getSampleRate() : fallback;
103}
104
105void Source::ensureStaticBuffer() {
106 if (alBuffer || !staticData)
107 return;
108 alGenBuffers(1, &alBuffer);
109 alBufferData(alBuffer, alFormat(), staticData->getData(),
110 static_cast<ALsizei>(staticData->getSize()), staticData->getSampleRate());
111 alSourcei(alSource, AL_BUFFER, static_cast<ALint>(alBuffer));
112}
113
114void Source::applyGainPitch() {
115 float master = audio ? audio->getVolume() : 1.f;
116 alSourcef(alSource, AL_GAIN, volume * master);
117 alSourcef(alSource, AL_PITCH, pitch);
118}
119
121 if (streaming) {
122 playing = true;
123 wantsData = true;
124 if (audio)
125 audio->notifyWorker();
126 alSourcePlay(alSource);
127 return;
128 }
129 ensureStaticBuffer();
130 applyGainPitch();
131 alSourcePlay(alSource);
132 playing = true;
133}
134
136 alSourcePause(alSource);
137 playing = false;
138}
139
141 if (!alSource)
142 return;
143 alSourceStop(alSource);
144 playing = false;
145 if (streaming) {
146 // Unqueue all
147 ALint processed = 0;
148 alGetSourcei(alSource, AL_BUFFERS_PROCESSED, &processed);
149 while (processed-- > 0) {
150 ALuint b = 0;
151 alSourceUnqueueBuffers(alSource, 1, &b);
152 }
153 ALint queued = 0;
154 alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued);
155 while (queued-- > 0) {
156 ALuint b = 0;
157 alSourceUnqueueBuffers(alSource, 1, &b);
158 }
159 {
160 std::lock_guard<std::mutex> lock(pendingMutex);
161 while (!pending.empty())
162 pending.pop();
163 }
164 {
165 std::lock_guard<std::mutex> lock(decoderMutex);
166 if (decoder)
167 decoder->rewind();
168 }
169 wantsData = true;
170 } else {
171 alSourceRewind(alSource);
172 }
173}
174
175bool Source::isPlaying() const {
176 ALint state = 0;
177 alGetSourcei(alSource, AL_SOURCE_STATE, &state);
178 return state == AL_PLAYING;
179}
180
181void Source::setVolume(float v) {
182 volume = std::max(0.f, v);
183 applyGainPitch();
184}
185float Source::getVolume() const { return volume; }
186
187void Source::setPitch(float p) {
188 pitch = std::max(0.001f, p);
189 applyGainPitch();
190}
191float Source::getPitch() const { return pitch; }
192
193void Source::setLooping(bool l) {
194 looping = l;
195 if (!streaming)
196 alSourcei(alSource, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
197}
198bool Source::isLooping() const { return looping; }
199
200bool Source::seek(double seconds) {
201 if (streaming) {
202 {
203 std::lock_guard<std::mutex> lock(decoderMutex);
204 if (!decoder || !decoder->isSeekable())
205 return false;
206 }
207 // stop() takes decoderMutex itself (for rewind), so it must not be held here.
208 stop();
209 {
210 std::lock_guard<std::mutex> lock(decoderMutex);
211 if (!decoder || !decoder->seek(seconds))
212 return false;
213 }
214 wantsData = true;
215 if (audio)
216 audio->notifyWorker();
217 return true;
218 }
219 alSourcef(alSource, AL_SEC_OFFSET, static_cast<ALfloat>(seconds));
220 return true;
221}
222
223double Source::tell() const {
224 ALfloat sec = 0.f;
225 alGetSourcef(alSource, AL_SEC_OFFSET, &sec);
226 return static_cast<double>(sec);
227}
228
229double Source::getDuration() const {
230 if (staticData)
231 return staticData->getDuration();
232 std::lock_guard<std::mutex> lock(decoderMutex);
233 if (decoder)
234 return decoder->getDuration();
235 return -1.0;
236}
237
238void Source::setPosition(float x, float y, float z) { alSource3f(alSource, AL_POSITION, x, y, z); }
239void Source::setVelocity(float x, float y, float z) { alSource3f(alSource, AL_VELOCITY, x, y, z); }
240void Source::setDirection(float x, float y, float z) { alSource3f(alSource, AL_DIRECTION, x, y, z); }
241void Source::setRelative(bool relative) {
242 alSourcei(alSource, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
243}
244void Source::setAttenuationDistances(float ref, float max) {
245 alSourcef(alSource, AL_REFERENCE_DISTANCE, ref);
246 alSourcef(alSource, AL_MAX_DISTANCE, max);
247 alSourcef(alSource, AL_ROLLOFF_FACTOR, 1.f);
248}
249
251 if (!streaming || !wantsData.load())
252 return;
253 {
254 std::lock_guard<std::mutex> lock(pendingMutex);
255 if (pending.size() >= kMaxPendingChunks)
256 return;
257 }
258
259 // Guard the whole decode step: `decoder` may be deleted concurrently by
260 // Source::~Source() running on another thread (e.g. main/script thread
261 // destroying this Source while the Audio worker is mid-decode here).
262 PcmChunk chunk;
263 {
264 std::lock_guard<std::mutex> decoderLock(decoderMutex);
265 if (!decoder)
266 return;
267 int n = decoder->decode();
268 if (n <= 0) {
269 if (looping && decoder->rewind()) {
270 n = decoder->decode();
271 } else {
272 wantsData = false;
273 return;
274 }
275 }
276 if (n <= 0)
277 return;
278 auto *buf = static_cast<const uint8_t *>(decoder->getBuffer());
279 chunk.bytes.assign(buf, buf + n);
280 }
281
282 std::lock_guard<std::mutex> lock(pendingMutex);
283 pending.push(std::move(chunk));
284}
285
287 if (!streaming || !alSource)
288 return;
289
290 ALint processed = 0;
291 alGetSourcei(alSource, AL_BUFFERS_PROCESSED, &processed);
292 while (processed-- > 0) {
293 ALuint b = 0;
294 alSourceUnqueueBuffers(alSource, 1, &b);
295 PcmChunk chunk;
296 {
297 std::lock_guard<std::mutex> lock(pendingMutex);
298 if (pending.empty()) {
299 // Recycle empty: leave buffer unused until data arrives
300 // Re-queue nothing; keep buffer id available by... we need free list.
301 // Simpler: if no pending, push buffer id back by queuing silence? Skip.
302 // Keep a free list via re-queue of zero later - for now re-store by
303 // temporarily using pending empty and regenerating: actually alGen keeps
304 // buffer; we must not lose buffer id. Queue a tiny silence.
305 std::vector<uint8_t> silence(256, 0);
306 alBufferData(b, alFormat(), silence.data(), static_cast<ALsizei>(silence.size()),
307 decoderSampleRateOr(44100));
308 alSourceQueueBuffers(alSource, 1, &b);
309 continue;
310 }
311 chunk = std::move(pending.front());
312 pending.pop();
313 }
314 alBufferData(b, alFormat(), chunk.bytes.data(), static_cast<ALsizei>(chunk.bytes.size()),
315 decoderSampleRateOr(44100));
316 alSourceQueueBuffers(alSource, 1, &b);
317 }
318
319 // Initial fill: queue unused buffers
320 ALint queued = 0;
321 alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued);
322 for (int i = queued; i < streamBufferCount; ++i) {
323 PcmChunk chunk;
324 {
325 std::lock_guard<std::mutex> lock(pendingMutex);
326 if (pending.empty())
327 break;
328 chunk = std::move(pending.front());
329 pending.pop();
330 }
331 ALuint b = streamBuffers[i];
332 // Find a buffer not currently queued — for initial, buffers 0..n unused
333 // Use streamBuffers[queued] style
334 ALuint buf = streamBuffers[queued];
335 alBufferData(buf, alFormat(), chunk.bytes.data(), static_cast<ALsizei>(chunk.bytes.size()),
336 decoderSampleRateOr(44100));
337 alSourceQueueBuffers(alSource, 1, &buf);
338 alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued);
339 }
340
341 if (playing && !isPlaying()) {
342 ALint q = 0;
343 alGetSourcei(alSource, AL_BUFFERS_QUEUED, &q);
344 if (q > 0)
345 alSourcePlay(alSource);
346 }
347
348 wantsData = true;
349 if (audio)
350 audio->notifyWorker();
351}
352
353} // namespace audio
354} // namespace eve
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
glm::vec3 n
Definition Grass.cpp:64
JobSystemThreadPool::State * state
uint32_t b
glm::vec4 p[6]
Light2D::Data * data
int v
OpenAL audio module: device management, master listener state, and Source factory....
Definition Audio.h:27
float getVolume() const
Definition Audio.cpp:167
void unregisterSource(Source *s)
Internal: removes a source from all module tracking.
Definition Audio.cpp:84
void registerStream(Source *s)
Internal: registers a streaming source with the decode worker.
Definition Audio.cpp:79
void notifyWorker()
Internal: wakes the decode worker thread.
Definition Audio.cpp:94
void fillPendingFromDecoder()
Called by the Audio worker thread. Internally synchronized against concurrent decoder teardown (see d...
Definition Source.cpp:250
void play()
Starts (or resumes) playback.
Definition Source.cpp:120
~Source() override
Definition Source.cpp:46
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
Source(Audio *audio, sound::SoundData *data)
Creates a static source from decoded audio (both arguments required).
Definition Source.cpp:18
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 pump()
Main thread: queues/unqueues AL buffers for a streaming source.
Definition Source.cpp:286
void setPitch(float p)
Sets playback pitch (clamped to >= 0).
Definition Source.cpp:187
ref is a smart pointer that automatically calls ref() and unref() on the object. Type T must be a sub...
Definition Object.h:54
Streaming audio decoder (medialoader-backed). Owns the raw encoded bytes and decodes incrementally vi...
Definition Decoder.h:20
void * getBuffer() const
Internal decoded buffer.
Definition Decoder.cpp:27
double getDuration()
Total duration in seconds.
Definition Decoder.cpp:35
int getSampleRate() const
Definition Decoder.cpp:34
bool isSeekable()
True when seeking is supported.
Definition Decoder.cpp:30
int getChannelCount() const
Format metadata.
Definition Decoder.cpp:32
bool rewind()
Rewinds to the start; false when unsupported.
Definition Decoder.cpp:29
bool seek(double seconds)
Seeks to a time in seconds; false when unsupported.
Definition Decoder.cpp:28
int getBitDepth() const
Definition Decoder.cpp:33
int decode()
Decodes the next chunk; returns bytes produced (0 = end/error).
Definition Decoder.cpp:25
Raw PCM audio buffer with format metadata (samples/rate/bit depth/channels).
Definition SoundData.h:13
double getDuration() const
Duration in seconds.
Definition SoundData.cpp:40
void * getData() const
Raw PCM access.
Definition SoundData.cpp:46
int getBitDepth() const
Definition SoundData.cpp:37
int getChannelCount() const
Definition SoundData.cpp:38
int getSampleRate() const
Definition SoundData.cpp:36
size_t getSize() const
Definition SoundData.cpp:47
constexpr int kStreamBufferCount
流式 Source 的 OpenAL 缓冲数量。
Definition AudioTypes.h:11
Definition Build.cpp:11
解码后待排队的一帧 PCM 数据。
Definition AudioTypes.h:16
std::vector< uint8_t > bytes
Definition AudioTypes.h:17