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);
37 audio->registerStream(
this);
38 audio->notifyWorker();
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);
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());
333 ALuint buf = streamBuffers[
queued];
334 alBufferData(buf, alFormat(), chunk.
bytes.data(),
static_cast<ALsizei
>(chunk.
bytes.size()),
335 decoderSampleRateOr(44100));
336 alSourceQueueBuffers(alSource, 1, &buf);
337 alGetSourcei(alSource, AL_BUFFERS_QUEUED, &
queued);
342 alGetSourcei(alSource, AL_BUFFERS_QUEUED, &
q);
344 alSourcePlay(alSource);
std::array< double, 10 > q
EVENGINE_API_FOUNDATION public API.
OpenAL audio module: device management, master listener state, and Source factory....
float getVolume() const
Returns the volume.
void unregisterSource(Source *s)
Internal: removes a source from all module tracking.
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).
float getVolume() const
Returns the volume.
void setVelocity(float x, float y, float z)
Sets the source velocity (used by OpenAL doppler).
bool isPlaying() const
True when playing.
float getPitch() const
Returns the pitch.
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.
bool isLooping() const
True when looping.
void pump()
Main thread: queues/unqueues AL buffers for a streaming source.
void setPitch(float p)
Sets playback pitch (clamped to >= 0).
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
Returns the sample rate.
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 getBitDepth() const
Returns the bit depth.
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 getBitDepth() const
Returns the bit depth.
int getChannelCount() const
Returns the channel count.
int getSampleRate() const
Returns the sample rate.
size_t getSize() const
Returns the size.
constexpr int kStreamBufferCount
流式 Source 的 OpenAL 缓冲数量。
Build metadata (engine git commit, build time, third-party version).
std::vector< uint8_t > bytes