15constexpr size_t kMaxPendingChunks = 8;
19 : audio(audio), staticData(
data), streaming(false) {
22 alGenSources(1, &alSource);
25 alSourcei(alSource, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
29 : audio(audio), decoder(decoder), ownsDecoder(takeDecoderOwnership), streaming(streaming) {
30 if (!audio || !decoder)
32 alGenSources(1, &alSource);
35 alGenBuffers(streamBufferCount, streamBuffers);
41 throw eve::Exception(
"Non-stream Decoder Source should be created via SoundData");
55 alSourcei(alSource, AL_BUFFER, 0);
56 alDeleteSources(1, &alSource);
60 alDeleteBuffers(1, &alBuffer);
63 if (streamBufferCount > 0) {
64 alDeleteBuffers(streamBufferCount, streamBuffers);
65 streamBufferCount = 0;
73 std::lock_guard<std::mutex> lock(decoderMutex);
81ALenum Source::alFormat()
const {
87 std::lock_guard<std::mutex> lock(decoderMutex);
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;
100int Source::decoderSampleRateOr(
int fallback)
const {
101 std::lock_guard<std::mutex> lock(decoderMutex);
105void Source::ensureStaticBuffer() {
106 if (alBuffer || !staticData)
108 alGenBuffers(1, &alBuffer);
109 alBufferData(alBuffer, alFormat(), staticData->
getData(),
111 alSourcei(alSource, AL_BUFFER,
static_cast<ALint
>(alBuffer));
114void Source::applyGainPitch() {
115 float master = audio ? audio->
getVolume() : 1.f;
116 alSourcef(alSource, AL_GAIN, volume * master);
117 alSourcef(alSource, AL_PITCH, pitch);
126 alSourcePlay(alSource);
129 ensureStaticBuffer();
131 alSourcePlay(alSource);
136 alSourcePause(alSource);
143 alSourceStop(alSource);
148 alGetSourcei(alSource, AL_BUFFERS_PROCESSED, &processed);
149 while (processed-- > 0) {
151 alSourceUnqueueBuffers(alSource, 1, &
b);
154 alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued);
155 while (queued-- > 0) {
157 alSourceUnqueueBuffers(alSource, 1, &
b);
160 std::lock_guard<std::mutex> lock(pendingMutex);
161 while (!pending.empty())
165 std::lock_guard<std::mutex> lock(decoderMutex);
171 alSourceRewind(alSource);
177 alGetSourcei(alSource, AL_SOURCE_STATE, &
state);
178 return state == AL_PLAYING;
182 volume = std::max(0.f,
v);
188 pitch = std::max(0.001f,
p);
196 alSourcei(alSource, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
203 std::lock_guard<std::mutex> lock(decoderMutex);
210 std::lock_guard<std::mutex> lock(decoderMutex);
211 if (!decoder || !decoder->
seek(seconds))
219 alSourcef(alSource, AL_SEC_OFFSET,
static_cast<ALfloat
>(seconds));
225 alGetSourcef(alSource, AL_SEC_OFFSET, &sec);
226 return static_cast<double>(sec);
232 std::lock_guard<std::mutex> lock(decoderMutex);
242 alSourcei(alSource, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
245 alSourcef(alSource, AL_REFERENCE_DISTANCE,
ref);
246 alSourcef(alSource, AL_MAX_DISTANCE, max);
247 alSourcef(alSource, AL_ROLLOFF_FACTOR, 1.f);
251 if (!streaming || !wantsData.load())
254 std::lock_guard<std::mutex> lock(pendingMutex);
255 if (pending.size() >= kMaxPendingChunks)
264 std::lock_guard<std::mutex> decoderLock(decoderMutex);
269 if (looping && decoder->
rewind()) {
278 auto *buf =
static_cast<const uint8_t *
>(decoder->
getBuffer());
279 chunk.
bytes.assign(buf, buf +
n);
282 std::lock_guard<std::mutex> lock(pendingMutex);
283 pending.push(std::move(chunk));
287 if (!streaming || !alSource)
291 alGetSourcei(alSource, AL_BUFFERS_PROCESSED, &processed);
292 while (processed-- > 0) {
294 alSourceUnqueueBuffers(alSource, 1, &
b);
297 std::lock_guard<std::mutex> lock(pendingMutex);
298 if (pending.empty()) {
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);
311 chunk = std::move(pending.front());
314 alBufferData(
b, alFormat(), chunk.
bytes.data(),
static_cast<ALsizei
>(chunk.
bytes.size()),
315 decoderSampleRateOr(44100));
316 alSourceQueueBuffers(alSource, 1, &
b);
321 alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued);
322 for (
int i = queued; i < streamBufferCount; ++i) {
325 std::lock_guard<std::mutex> lock(pendingMutex);
328 chunk = std::move(pending.front());
331 ALuint
b = streamBuffers[i];
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);
343 alGetSourcei(alSource, AL_BUFFERS_QUEUED, &q);
345 alSourcePlay(alSource);
JobSystemThreadPool::State * state
OpenAL audio module: device management, master listener state, and Source factory....
void unregisterSource(Source *s)
Internal: removes a source from all module tracking.
void registerStream(Source *s)
Internal: registers a streaming source with the decode worker.
void notifyWorker()
Internal: wakes the decode worker thread.
void fillPendingFromDecoder()
Called by the Audio worker thread. Internally synchronized against concurrent decoder teardown (see d...
void play()
Starts (or resumes) playback.
void pause()
Pauses playback, keeping the play position.
void setVolume(float v)
Sets source gain (clamped to >= 0).
void setVelocity(float x, float y, float z)
Sets the source velocity (used by OpenAL doppler).
void setRelative(bool relative)
Makes the source ignore the listener position (head-relative).
void setPosition(float x, float y, float z)
Sets the source position in world units.
void stop()
Stops playback and rewinds the play position.
double getDuration() const
Total duration in seconds (0 for live/unbounded streams).
double tell() const
Current play time in seconds.
void setAttenuationDistances(float ref, float max)
Sets OpenAL reference and maximum attenuation distances.
bool seek(double seconds)
Seeks to a time in seconds; false when seeking is unsupported.
Source(Audio *audio, sound::SoundData *data)
Creates a static source from decoded audio (both arguments required).
void setLooping(bool l)
Enables/disables looping.
void setDirection(float x, float y, float z)
Sets the source directional cone orientation.
void pump()
Main thread: queues/unqueues AL buffers for a streaming source.
void setPitch(float p)
Sets playback pitch (clamped to >= 0).
ref is a smart pointer that automatically calls ref() and unref() on the object. Type T must be a sub...
Streaming audio decoder (medialoader-backed). Owns the raw encoded bytes and decodes incrementally vi...
void * getBuffer() const
Internal decoded buffer.
double getDuration()
Total duration in seconds.
int getSampleRate() const
bool isSeekable()
True when seeking is supported.
int getChannelCount() const
Format metadata.
bool rewind()
Rewinds to the start; false when unsupported.
bool seek(double seconds)
Seeks to a time in seconds; false when unsupported.
int decode()
Decodes the next chunk; returns bytes produced (0 = end/error).
Raw PCM audio buffer with format metadata (samples/rate/bit depth/channels).
double getDuration() const
Duration in seconds.
void * getData() const
Raw PCM access.
int getChannelCount() const
int getSampleRate() const
constexpr int kStreamBufferCount
流式 Source 的 OpenAL 缓冲数量。
std::vector< uint8_t > bytes