Remove unused atomic_invflag
[openal-soft.git] / al / source.cpp
blob3009edd89b69a7d54a7d9d496cb656e9bd00fc82
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include "source.h"
25 #include <algorithm>
26 #include <array>
27 #include <atomic>
28 #include <cassert>
29 #include <chrono>
30 #include <climits>
31 #include <cmath>
32 #include <cstdint>
33 #include <functional>
34 #include <iterator>
35 #include <limits>
36 #include <memory>
37 #include <mutex>
38 #include <new>
39 #include <numeric>
40 #include <stdexcept>
41 #include <thread>
42 #include <utility>
44 #include "AL/al.h"
45 #include "AL/alc.h"
46 #include "AL/alext.h"
47 #include "AL/efx.h"
49 #include "albit.h"
50 #include "alc/alu.h"
51 #include "alc/backends/base.h"
52 #include "alc/context.h"
53 #include "alc/device.h"
54 #include "alc/inprogext.h"
55 #include "almalloc.h"
56 #include "alnumeric.h"
57 #include "aloptional.h"
58 #include "alspan.h"
59 #include "atomic.h"
60 #include "auxeffectslot.h"
61 #include "buffer.h"
62 #include "core/ambidefs.h"
63 #include "core/bformatdec.h"
64 #include "core/except.h"
65 #include "core/filters/nfc.h"
66 #include "core/filters/splitter.h"
67 #include "core/logging.h"
68 #include "core/voice_change.h"
69 #include "event.h"
70 #include "filter.h"
71 #include "opthelpers.h"
72 #include "ringbuffer.h"
73 #include "threads.h"
75 #ifdef ALSOFT_EAX
76 #include "eax_exception.h"
77 #endif // ALSOFT_EAX
79 namespace {
81 using namespace std::placeholders;
82 using std::chrono::nanoseconds;
84 Voice *GetSourceVoice(ALsource *source, ALCcontext *context)
86 auto voicelist = context->getVoicesSpan();
87 ALuint idx{source->VoiceIdx};
88 if(idx < voicelist.size())
90 ALuint sid{source->id};
91 Voice *voice = voicelist[idx];
92 if(voice->mSourceID.load(std::memory_order_acquire) == sid)
93 return voice;
95 source->VoiceIdx = INVALID_VOICE_IDX;
96 return nullptr;
100 void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context)
102 /* Get an unused property container, or allocate a new one as needed. */
103 VoicePropsItem *props{context->mFreeVoiceProps.load(std::memory_order_acquire)};
104 if(!props)
106 context->allocVoiceProps();
107 props = context->mFreeVoiceProps.load(std::memory_order_acquire);
109 VoicePropsItem *next;
110 do {
111 next = props->next.load(std::memory_order_relaxed);
112 } while(unlikely(context->mFreeVoiceProps.compare_exchange_weak(props, next,
113 std::memory_order_acq_rel, std::memory_order_acquire) == false));
115 props->Pitch = source->Pitch;
116 props->Gain = source->Gain;
117 props->OuterGain = source->OuterGain;
118 props->MinGain = source->MinGain;
119 props->MaxGain = source->MaxGain;
120 props->InnerAngle = source->InnerAngle;
121 props->OuterAngle = source->OuterAngle;
122 props->RefDistance = source->RefDistance;
123 props->MaxDistance = source->MaxDistance;
124 props->RolloffFactor = source->RolloffFactor;
125 props->Position = source->Position;
126 props->Velocity = source->Velocity;
127 props->Direction = source->Direction;
128 props->OrientAt = source->OrientAt;
129 props->OrientUp = source->OrientUp;
130 props->HeadRelative = source->HeadRelative;
131 props->mDistanceModel = source->mDistanceModel;
132 props->mResampler = source->mResampler;
133 props->DirectChannels = source->DirectChannels;
134 props->mSpatializeMode = source->mSpatialize;
136 props->DryGainHFAuto = source->DryGainHFAuto;
137 props->WetGainAuto = source->WetGainAuto;
138 props->WetGainHFAuto = source->WetGainHFAuto;
139 props->OuterGainHF = source->OuterGainHF;
141 props->AirAbsorptionFactor = source->AirAbsorptionFactor;
142 props->RoomRolloffFactor = source->RoomRolloffFactor;
143 props->DopplerFactor = source->DopplerFactor;
145 props->StereoPan = source->StereoPan;
147 props->Radius = source->Radius;
148 props->EnhWidth = source->EnhWidth;
150 props->Direct.Gain = source->Direct.Gain;
151 props->Direct.GainHF = source->Direct.GainHF;
152 props->Direct.HFReference = source->Direct.HFReference;
153 props->Direct.GainLF = source->Direct.GainLF;
154 props->Direct.LFReference = source->Direct.LFReference;
156 auto copy_send = [](const ALsource::SendData &srcsend) noexcept -> VoiceProps::SendData
158 VoiceProps::SendData ret{};
159 ret.Slot = srcsend.Slot ? &srcsend.Slot->mSlot : nullptr;
160 ret.Gain = srcsend.Gain;
161 ret.GainHF = srcsend.GainHF;
162 ret.HFReference = srcsend.HFReference;
163 ret.GainLF = srcsend.GainLF;
164 ret.LFReference = srcsend.LFReference;
165 return ret;
167 std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send);
168 if(!props->Send[0].Slot && context->mDefaultSlot)
169 props->Send[0].Slot = &context->mDefaultSlot->mSlot;
171 /* Set the new container for updating internal parameters. */
172 props = voice->mUpdate.exchange(props, std::memory_order_acq_rel);
173 if(props)
175 /* If there was an unused update container, put it back in the
176 * freelist.
178 AtomicReplaceHead(context->mFreeVoiceProps, props);
182 /* GetSourceSampleOffset
184 * Gets the current read offset for the given Source, in 32.32 fixed-point
185 * samples. The offset is relative to the start of the queue (not the start of
186 * the current buffer).
188 int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
190 ALCdevice *device{context->mALDevice.get()};
191 const VoiceBufferItem *Current{};
192 uint64_t readPos{};
193 ALuint refcount;
194 Voice *voice;
196 do {
197 refcount = device->waitForMix();
198 *clocktime = GetDeviceClockTime(device);
199 voice = GetSourceVoice(Source, context);
200 if(voice)
202 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
204 readPos = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << 32;
205 readPos |= uint64_t{voice->mPositionFrac.load(std::memory_order_relaxed)} <<
206 (32-MixerFracBits);
208 std::atomic_thread_fence(std::memory_order_acquire);
209 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
211 if(!voice)
212 return 0;
214 for(auto &item : Source->mQueue)
216 if(&item == Current) break;
217 readPos += uint64_t{item.mSampleLen} << 32;
219 return static_cast<int64_t>(minu64(readPos, 0x7fffffffffffffff_u64));
222 /* GetSourceSecOffset
224 * Gets the current read offset for the given Source, in seconds. The offset is
225 * relative to the start of the queue (not the start of the current buffer).
227 double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
229 ALCdevice *device{context->mALDevice.get()};
230 const VoiceBufferItem *Current{};
231 uint64_t readPos{};
232 ALuint refcount;
233 Voice *voice;
235 do {
236 refcount = device->waitForMix();
237 *clocktime = GetDeviceClockTime(device);
238 voice = GetSourceVoice(Source, context);
239 if(voice)
241 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
243 readPos = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << MixerFracBits;
244 readPos |= voice->mPositionFrac.load(std::memory_order_relaxed);
246 std::atomic_thread_fence(std::memory_order_acquire);
247 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
249 if(!voice)
250 return 0.0f;
252 const ALbuffer *BufferFmt{nullptr};
253 auto BufferList = Source->mQueue.cbegin();
254 while(BufferList != Source->mQueue.cend() && std::addressof(*BufferList) != Current)
256 if(!BufferFmt) BufferFmt = BufferList->mBuffer;
257 readPos += uint64_t{BufferList->mSampleLen} << MixerFracBits;
258 ++BufferList;
260 while(BufferList != Source->mQueue.cend() && !BufferFmt)
262 BufferFmt = BufferList->mBuffer;
263 ++BufferList;
265 assert(BufferFmt != nullptr);
267 return static_cast<double>(readPos) / double{MixerFracOne} / BufferFmt->mSampleRate;
270 /* GetSourceOffset
272 * Gets the current read offset for the given Source, in the appropriate format
273 * (Bytes, Samples or Seconds). The offset is relative to the start of the
274 * queue (not the start of the current buffer).
276 double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
278 ALCdevice *device{context->mALDevice.get()};
279 const VoiceBufferItem *Current{};
280 ALuint readPos{};
281 ALuint readPosFrac{};
282 ALuint refcount;
283 Voice *voice;
285 do {
286 refcount = device->waitForMix();
287 voice = GetSourceVoice(Source, context);
288 if(voice)
290 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
292 readPos = voice->mPosition.load(std::memory_order_relaxed);
293 readPosFrac = voice->mPositionFrac.load(std::memory_order_relaxed);
295 std::atomic_thread_fence(std::memory_order_acquire);
296 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
298 if(!voice)
299 return 0.0;
301 const ALbuffer *BufferFmt{nullptr};
302 auto BufferList = Source->mQueue.cbegin();
303 while(BufferList != Source->mQueue.cend() && std::addressof(*BufferList) != Current)
305 if(!BufferFmt) BufferFmt = BufferList->mBuffer;
306 readPos += BufferList->mSampleLen;
307 ++BufferList;
309 while(BufferList != Source->mQueue.cend() && !BufferFmt)
311 BufferFmt = BufferList->mBuffer;
312 ++BufferList;
314 assert(BufferFmt != nullptr);
316 double offset{};
317 switch(name)
319 case AL_SEC_OFFSET:
320 offset = (readPos + readPosFrac/double{MixerFracOne}) / BufferFmt->mSampleRate;
321 break;
323 case AL_SAMPLE_OFFSET:
324 offset = readPos + readPosFrac/double{MixerFracOne};
325 break;
327 case AL_BYTE_OFFSET:
328 if(BufferFmt->OriginalType == UserFmtIMA4)
330 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
331 ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
332 ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
334 /* Round down to nearest ADPCM block */
335 offset = static_cast<double>(readPos / FrameBlockSize * BlockSize);
337 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
339 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
340 ALuint align{(FrameBlockSize-2)/2 + 7};
341 ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
343 /* Round down to nearest ADPCM block */
344 offset = static_cast<double>(readPos / FrameBlockSize * BlockSize);
346 else
348 const ALuint FrameSize{BufferFmt->frameSizeFromFmt()};
349 offset = static_cast<double>(readPos * FrameSize);
351 break;
353 return offset;
356 /* GetSourceLength
358 * Gets the length of the given Source's buffer queue, in the appropriate
359 * format (Bytes, Samples or Seconds).
361 double GetSourceLength(const ALsource *source, ALenum name)
363 uint64_t length{0};
364 const ALbuffer *BufferFmt{nullptr};
365 for(auto &listitem : source->mQueue)
367 if(!BufferFmt)
368 BufferFmt = listitem.mBuffer;
369 length += listitem.mSampleLen;
371 if(length == 0)
372 return 0.0;
374 assert(BufferFmt != nullptr);
375 switch(name)
377 case AL_SEC_LENGTH_SOFT:
378 return static_cast<double>(length) / BufferFmt->mSampleRate;
380 case AL_SAMPLE_LENGTH_SOFT:
381 return static_cast<double>(length);
383 case AL_BYTE_LENGTH_SOFT:
384 if(BufferFmt->OriginalType == UserFmtIMA4)
386 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
387 ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
388 ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
390 /* Round down to nearest ADPCM block */
391 return static_cast<double>(length / FrameBlockSize) * BlockSize;
393 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
395 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
396 ALuint align{(FrameBlockSize-2)/2 + 7};
397 ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
399 /* Round down to nearest ADPCM block */
400 return static_cast<double>(length / FrameBlockSize) * BlockSize;
402 return static_cast<double>(length) * BufferFmt->frameSizeFromFmt();
404 return 0.0;
408 struct VoicePos {
409 ALuint pos, frac;
410 ALbufferQueueItem *bufferitem;
414 * GetSampleOffset
416 * Retrieves the voice position, fixed-point fraction, and bufferlist item
417 * using the givem offset type and offset. If the offset is out of range,
418 * returns an empty optional.
420 al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList, ALenum OffsetType,
421 double Offset)
423 /* Find the first valid Buffer in the Queue */
424 const ALbuffer *BufferFmt{nullptr};
425 for(auto &item : BufferList)
427 BufferFmt = item.mBuffer;
428 if(BufferFmt) break;
430 if(!BufferFmt || BufferFmt->mCallback)
431 return al::nullopt;
433 /* Get sample frame offset */
434 ALuint offset{0u}, frac{0u};
435 double dbloff, dblfrac;
436 switch(OffsetType)
438 case AL_SEC_OFFSET:
439 dblfrac = std::modf(Offset*BufferFmt->mSampleRate, &dbloff);
440 offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
441 frac = static_cast<ALuint>(mind(dblfrac*MixerFracOne, MixerFracOne-1.0));
442 break;
444 case AL_SAMPLE_OFFSET:
445 dblfrac = std::modf(Offset, &dbloff);
446 offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
447 frac = static_cast<ALuint>(mind(dblfrac*MixerFracOne, MixerFracOne-1.0));
448 break;
450 case AL_BYTE_OFFSET:
451 /* Determine the ByteOffset (and ensure it is block aligned) */
452 offset = static_cast<ALuint>(Offset);
453 if(BufferFmt->OriginalType == UserFmtIMA4)
455 const ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
456 offset /= align * BufferFmt->channelsFromFmt();
457 offset *= BufferFmt->OriginalAlign;
459 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
461 const ALuint align{(BufferFmt->OriginalAlign-2)/2 + 7};
462 offset /= align * BufferFmt->channelsFromFmt();
463 offset *= BufferFmt->OriginalAlign;
465 else
466 offset /= BufferFmt->frameSizeFromFmt();
467 frac = 0;
468 break;
471 /* Find the bufferlist item this offset belongs to. */
472 ALuint totalBufferLen{0u};
473 for(auto &item : BufferList)
475 if(totalBufferLen > offset)
476 break;
477 if(item.mSampleLen > offset-totalBufferLen)
479 /* Offset is in this buffer */
480 return VoicePos{offset-totalBufferLen, frac, &item};
482 totalBufferLen += item.mSampleLen;
485 /* Offset is out of range of the queue */
486 return al::nullopt;
490 void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, ALCcontext *context,
491 ALCdevice *device)
493 voice->mLoopBuffer.store(source->Looping ? &source->mQueue.front() : nullptr,
494 std::memory_order_relaxed);
496 ALbuffer *buffer{BufferList->mBuffer};
497 voice->mFrequency = buffer->mSampleRate;
498 voice->mFmtChannels =
499 (buffer->mChannels == FmtStereo && source->mStereoMode == SourceStereo::Enhanced) ?
500 FmtSuperStereo : buffer->mChannels;
501 voice->mFmtType = buffer->mType;
502 voice->mFrameStep = buffer->channelsFromFmt();
503 voice->mFrameSize = buffer->frameSizeFromFmt();
504 voice->mAmbiLayout = IsUHJ(voice->mFmtChannels) ? AmbiLayout::FuMa : buffer->mAmbiLayout;
505 voice->mAmbiScaling = IsUHJ(voice->mFmtChannels) ? AmbiScaling::UHJ : buffer->mAmbiScaling;
506 voice->mAmbiOrder = (voice->mFmtChannels == FmtSuperStereo) ? 1 : buffer->mAmbiOrder;
508 if(buffer->mCallback) voice->mFlags.set(VoiceIsCallback);
509 else if(source->SourceType == AL_STATIC) voice->mFlags.set(VoiceIsStatic);
510 voice->mNumCallbackSamples = 0;
512 voice->prepare(device);
514 source->mPropsDirty = false;
515 UpdateSourceProps(source, voice, context);
517 voice->mSourceID.store(source->id, std::memory_order_release);
521 VoiceChange *GetVoiceChanger(ALCcontext *ctx)
523 VoiceChange *vchg{ctx->mVoiceChangeTail};
524 if UNLIKELY(vchg == ctx->mCurrentVoiceChange.load(std::memory_order_acquire))
526 ctx->allocVoiceChanges();
527 vchg = ctx->mVoiceChangeTail;
530 ctx->mVoiceChangeTail = vchg->mNext.exchange(nullptr, std::memory_order_relaxed);
532 return vchg;
535 void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
537 ALCdevice *device{ctx->mALDevice.get()};
539 VoiceChange *oldhead{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
540 while(VoiceChange *next{oldhead->mNext.load(std::memory_order_relaxed)})
541 oldhead = next;
542 oldhead->mNext.store(tail, std::memory_order_release);
544 const bool connected{device->Connected.load(std::memory_order_acquire)};
545 device->waitForMix();
546 if UNLIKELY(!connected)
548 if(ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire))
550 /* If the device is disconnected and voices are stopped, just
551 * ignore all pending changes.
553 VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
554 while(VoiceChange *next{cur->mNext.load(std::memory_order_acquire)})
556 cur = next;
557 if(Voice *voice{cur->mVoice})
558 voice->mSourceID.store(0, std::memory_order_relaxed);
560 ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
566 bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALCcontext *context,
567 ALCdevice *device)
569 /* First, get a free voice to start at the new offset. */
570 auto voicelist = context->getVoicesSpan();
571 Voice *newvoice{};
572 ALuint vidx{0};
573 for(Voice *voice : voicelist)
575 if(voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
576 && voice->mSourceID.load(std::memory_order_relaxed) == 0u
577 && voice->mPendingChange.load(std::memory_order_relaxed) == false)
579 newvoice = voice;
580 break;
582 ++vidx;
584 if UNLIKELY(!newvoice)
586 auto &allvoices = *context->mVoices.load(std::memory_order_relaxed);
587 if(allvoices.size() == voicelist.size())
588 context->allocVoices(1);
589 context->mActiveVoiceCount.fetch_add(1, std::memory_order_release);
590 voicelist = context->getVoicesSpan();
592 vidx = 0;
593 for(Voice *voice : voicelist)
595 if(voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
596 && voice->mSourceID.load(std::memory_order_relaxed) == 0u
597 && voice->mPendingChange.load(std::memory_order_relaxed) == false)
599 newvoice = voice;
600 break;
602 ++vidx;
604 assert(newvoice != nullptr);
607 /* Initialize the new voice and set its starting offset.
608 * TODO: It might be better to have the VoiceChange processing copy the old
609 * voice's mixing parameters (and pending update) insead of initializing it
610 * all here. This would just need to set the minimum properties to link the
611 * voice to the source and its position-dependent properties (including the
612 * fading flag).
614 newvoice->mPlayState.store(Voice::Pending, std::memory_order_relaxed);
615 newvoice->mPosition.store(vpos.pos, std::memory_order_relaxed);
616 newvoice->mPositionFrac.store(vpos.frac, std::memory_order_relaxed);
617 newvoice->mCurrentBuffer.store(vpos.bufferitem, std::memory_order_relaxed);
618 newvoice->mFlags.reset();
619 if(vpos.pos > 0 || vpos.frac > 0 || vpos.bufferitem != &source->mQueue.front())
620 newvoice->mFlags.set(VoiceIsFading);
621 InitVoice(newvoice, source, vpos.bufferitem, context, device);
622 source->VoiceIdx = vidx;
624 /* Set the old voice as having a pending change, and send it off with the
625 * new one with a new offset voice change.
627 oldvoice->mPendingChange.store(true, std::memory_order_relaxed);
629 VoiceChange *vchg{GetVoiceChanger(context)};
630 vchg->mOldVoice = oldvoice;
631 vchg->mVoice = newvoice;
632 vchg->mSourceID = source->id;
633 vchg->mState = VChangeState::Restart;
634 SendVoiceChanges(context, vchg);
636 /* If the old voice still has a sourceID, it's still active and the change-
637 * over will work on the next update.
639 if LIKELY(oldvoice->mSourceID.load(std::memory_order_acquire) != 0u)
640 return true;
642 /* Otherwise, if the new voice's state is not pending, the change-over
643 * already happened.
645 if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending)
646 return true;
648 /* Otherwise, wait for any current mix to finish and check one last time. */
649 device->waitForMix();
650 if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending)
651 return true;
652 /* The change-over failed because the old voice stopped before the new
653 * voice could start at the new offset. Let go of the new voice and have
654 * the caller store the source offset since it's stopped.
656 newvoice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
657 newvoice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
658 newvoice->mSourceID.store(0u, std::memory_order_relaxed);
659 newvoice->mPlayState.store(Voice::Stopped, std::memory_order_relaxed);
660 return false;
665 * Returns if the last known state for the source was playing or paused. Does
666 * not sync with the mixer voice.
668 inline bool IsPlayingOrPaused(ALsource *source)
669 { return source->state == AL_PLAYING || source->state == AL_PAUSED; }
672 * Returns an updated source state using the matching voice's status (or lack
673 * thereof).
675 inline ALenum GetSourceState(ALsource *source, Voice *voice)
677 if(!voice && source->state == AL_PLAYING)
678 source->state = AL_STOPPED;
679 return source->state;
683 bool EnsureSources(ALCcontext *context, size_t needed)
685 size_t count{std::accumulate(context->mSourceList.cbegin(), context->mSourceList.cend(),
686 size_t{0},
687 [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
688 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
690 while(needed > count)
692 if UNLIKELY(context->mSourceList.size() >= 1<<25)
693 return false;
695 context->mSourceList.emplace_back();
696 auto sublist = context->mSourceList.end() - 1;
697 sublist->FreeMask = ~0_u64;
698 sublist->Sources = static_cast<ALsource*>(al_calloc(alignof(ALsource), sizeof(ALsource)*64));
699 if UNLIKELY(!sublist->Sources)
701 context->mSourceList.pop_back();
702 return false;
704 count += 64;
706 return true;
709 ALsource *AllocSource(ALCcontext *context)
711 auto sublist = std::find_if(context->mSourceList.begin(), context->mSourceList.end(),
712 [](const SourceSubList &entry) noexcept -> bool
713 { return entry.FreeMask != 0; });
714 auto lidx = static_cast<ALuint>(std::distance(context->mSourceList.begin(), sublist));
715 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
716 ASSUME(slidx < 64);
718 ALsource *source{al::construct_at(sublist->Sources + slidx)};
720 /* Add 1 to avoid source ID 0. */
721 source->id = ((lidx<<6) | slidx) + 1;
723 context->mNumSources += 1;
724 sublist->FreeMask &= ~(1_u64 << slidx);
726 return source;
729 void FreeSource(ALCcontext *context, ALsource *source)
731 const ALuint id{source->id - 1};
732 const size_t lidx{id >> 6};
733 const ALuint slidx{id & 0x3f};
735 if(Voice *voice{GetSourceVoice(source, context)})
737 VoiceChange *vchg{GetVoiceChanger(context)};
739 voice->mPendingChange.store(true, std::memory_order_relaxed);
740 vchg->mVoice = voice;
741 vchg->mSourceID = source->id;
742 vchg->mState = VChangeState::Stop;
744 SendVoiceChanges(context, vchg);
747 al::destroy_at(source);
749 context->mSourceList[lidx].FreeMask |= 1_u64 << slidx;
750 context->mNumSources--;
754 inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept
756 const size_t lidx{(id-1) >> 6};
757 const ALuint slidx{(id-1) & 0x3f};
759 if UNLIKELY(lidx >= context->mSourceList.size())
760 return nullptr;
761 SourceSubList &sublist{context->mSourceList[lidx]};
762 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
763 return nullptr;
764 return sublist.Sources + slidx;
767 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
769 const size_t lidx{(id-1) >> 6};
770 const ALuint slidx{(id-1) & 0x3f};
772 if UNLIKELY(lidx >= device->BufferList.size())
773 return nullptr;
774 BufferSubList &sublist = device->BufferList[lidx];
775 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
776 return nullptr;
777 return sublist.Buffers + slidx;
780 inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept
782 const size_t lidx{(id-1) >> 6};
783 const ALuint slidx{(id-1) & 0x3f};
785 if UNLIKELY(lidx >= device->FilterList.size())
786 return nullptr;
787 FilterSubList &sublist = device->FilterList[lidx];
788 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
789 return nullptr;
790 return sublist.Filters + slidx;
793 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
795 const size_t lidx{(id-1) >> 6};
796 const ALuint slidx{(id-1) & 0x3f};
798 if UNLIKELY(lidx >= context->mEffectSlotList.size())
799 return nullptr;
800 EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
801 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
802 return nullptr;
803 return sublist.EffectSlots + slidx;
807 al::optional<SourceStereo> StereoModeFromEnum(ALenum mode)
809 switch(mode)
811 case AL_NORMAL_SOFT: return al::make_optional(SourceStereo::Normal);
812 case AL_SUPER_STEREO_SOFT: return al::make_optional(SourceStereo::Enhanced);
814 WARN("Unsupported stereo mode: 0x%04x\n", mode);
815 return al::nullopt;
817 ALenum EnumFromStereoMode(SourceStereo mode)
819 switch(mode)
821 case SourceStereo::Normal: return AL_NORMAL_SOFT;
822 case SourceStereo::Enhanced: return AL_SUPER_STEREO_SOFT;
824 throw std::runtime_error{"Invalid SourceStereo: "+std::to_string(int(mode))};
827 al::optional<SpatializeMode> SpatializeModeFromEnum(ALenum mode)
829 switch(mode)
831 case AL_FALSE: return al::make_optional(SpatializeMode::Off);
832 case AL_TRUE: return al::make_optional(SpatializeMode::On);
833 case AL_AUTO_SOFT: return al::make_optional(SpatializeMode::Auto);
835 WARN("Unsupported spatialize mode: 0x%04x\n", mode);
836 return al::nullopt;
838 ALenum EnumFromSpatializeMode(SpatializeMode mode)
840 switch(mode)
842 case SpatializeMode::Off: return AL_FALSE;
843 case SpatializeMode::On: return AL_TRUE;
844 case SpatializeMode::Auto: return AL_AUTO_SOFT;
846 throw std::runtime_error{"Invalid SpatializeMode: "+std::to_string(int(mode))};
849 al::optional<DirectMode> DirectModeFromEnum(ALenum mode)
851 switch(mode)
853 case AL_FALSE: return al::make_optional(DirectMode::Off);
854 case AL_DROP_UNMATCHED_SOFT: return al::make_optional(DirectMode::DropMismatch);
855 case AL_REMIX_UNMATCHED_SOFT: return al::make_optional(DirectMode::RemixMismatch);
857 WARN("Unsupported direct mode: 0x%04x\n", mode);
858 return al::nullopt;
860 ALenum EnumFromDirectMode(DirectMode mode)
862 switch(mode)
864 case DirectMode::Off: return AL_FALSE;
865 case DirectMode::DropMismatch: return AL_DROP_UNMATCHED_SOFT;
866 case DirectMode::RemixMismatch: return AL_REMIX_UNMATCHED_SOFT;
868 throw std::runtime_error{"Invalid DirectMode: "+std::to_string(int(mode))};
871 al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
873 switch(model)
875 case AL_NONE: return al::make_optional(DistanceModel::Disable);
876 case AL_INVERSE_DISTANCE: return al::make_optional(DistanceModel::Inverse);
877 case AL_INVERSE_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::InverseClamped);
878 case AL_LINEAR_DISTANCE: return al::make_optional(DistanceModel::Linear);
879 case AL_LINEAR_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::LinearClamped);
880 case AL_EXPONENT_DISTANCE: return al::make_optional(DistanceModel::Exponent);
881 case AL_EXPONENT_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::ExponentClamped);
883 return al::nullopt;
885 ALenum ALenumFromDistanceModel(DistanceModel model)
887 switch(model)
889 case DistanceModel::Disable: return AL_NONE;
890 case DistanceModel::Inverse: return AL_INVERSE_DISTANCE;
891 case DistanceModel::InverseClamped: return AL_INVERSE_DISTANCE_CLAMPED;
892 case DistanceModel::Linear: return AL_LINEAR_DISTANCE;
893 case DistanceModel::LinearClamped: return AL_LINEAR_DISTANCE_CLAMPED;
894 case DistanceModel::Exponent: return AL_EXPONENT_DISTANCE;
895 case DistanceModel::ExponentClamped: return AL_EXPONENT_DISTANCE_CLAMPED;
897 throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast<int>(model))};
900 enum SourceProp : ALenum {
901 srcPitch = AL_PITCH,
902 srcGain = AL_GAIN,
903 srcMinGain = AL_MIN_GAIN,
904 srcMaxGain = AL_MAX_GAIN,
905 srcMaxDistance = AL_MAX_DISTANCE,
906 srcRolloffFactor = AL_ROLLOFF_FACTOR,
907 srcDopplerFactor = AL_DOPPLER_FACTOR,
908 srcConeOuterGain = AL_CONE_OUTER_GAIN,
909 srcSecOffset = AL_SEC_OFFSET,
910 srcSampleOffset = AL_SAMPLE_OFFSET,
911 srcByteOffset = AL_BYTE_OFFSET,
912 srcConeInnerAngle = AL_CONE_INNER_ANGLE,
913 srcConeOuterAngle = AL_CONE_OUTER_ANGLE,
914 srcRefDistance = AL_REFERENCE_DISTANCE,
916 srcPosition = AL_POSITION,
917 srcVelocity = AL_VELOCITY,
918 srcDirection = AL_DIRECTION,
920 srcSourceRelative = AL_SOURCE_RELATIVE,
921 srcLooping = AL_LOOPING,
922 srcBuffer = AL_BUFFER,
923 srcSourceState = AL_SOURCE_STATE,
924 srcBuffersQueued = AL_BUFFERS_QUEUED,
925 srcBuffersProcessed = AL_BUFFERS_PROCESSED,
926 srcSourceType = AL_SOURCE_TYPE,
928 /* ALC_EXT_EFX */
929 srcConeOuterGainHF = AL_CONE_OUTER_GAINHF,
930 srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR,
931 srcRoomRolloffFactor = AL_ROOM_ROLLOFF_FACTOR,
932 srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO,
933 srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO,
934 srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO,
935 srcDirectFilter = AL_DIRECT_FILTER,
936 srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER,
938 /* AL_SOFT_direct_channels */
939 srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT,
941 /* AL_EXT_source_distance_model */
942 srcDistanceModel = AL_DISTANCE_MODEL,
944 /* AL_SOFT_source_latency */
945 srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT,
946 srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT,
948 /* AL_EXT_STEREO_ANGLES */
949 srcAngles = AL_STEREO_ANGLES,
951 /* AL_EXT_SOURCE_RADIUS */
952 srcRadius = AL_SOURCE_RADIUS,
954 /* AL_EXT_BFORMAT */
955 srcOrientation = AL_ORIENTATION,
957 /* AL_SOFT_source_length */
958 srcByteLength = AL_BYTE_LENGTH_SOFT,
959 srcSampleLength = AL_SAMPLE_LENGTH_SOFT,
960 srcSecLength = AL_SEC_LENGTH_SOFT,
962 /* AL_SOFT_source_resampler */
963 srcResampler = AL_SOURCE_RESAMPLER_SOFT,
965 /* AL_SOFT_source_spatialize */
966 srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT,
968 /* ALC_SOFT_device_clock */
969 srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT,
970 srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT,
972 /* AL_SOFT_UHJ */
973 srcStereoMode = AL_STEREO_MODE_SOFT,
974 srcSuperStereoWidth = AL_SUPER_STEREO_WIDTH_SOFT,
978 constexpr size_t MaxValues{6u};
980 ALuint FloatValsByProp(ALenum prop)
982 switch(static_cast<SourceProp>(prop))
984 case AL_PITCH:
985 case AL_GAIN:
986 case AL_MIN_GAIN:
987 case AL_MAX_GAIN:
988 case AL_MAX_DISTANCE:
989 case AL_ROLLOFF_FACTOR:
990 case AL_DOPPLER_FACTOR:
991 case AL_CONE_OUTER_GAIN:
992 case AL_SEC_OFFSET:
993 case AL_SAMPLE_OFFSET:
994 case AL_BYTE_OFFSET:
995 case AL_CONE_INNER_ANGLE:
996 case AL_CONE_OUTER_ANGLE:
997 case AL_REFERENCE_DISTANCE:
998 case AL_CONE_OUTER_GAINHF:
999 case AL_AIR_ABSORPTION_FACTOR:
1000 case AL_ROOM_ROLLOFF_FACTOR:
1001 case AL_DIRECT_FILTER_GAINHF_AUTO:
1002 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1003 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1004 case AL_DIRECT_CHANNELS_SOFT:
1005 case AL_DISTANCE_MODEL:
1006 case AL_SOURCE_RELATIVE:
1007 case AL_LOOPING:
1008 case AL_SOURCE_STATE:
1009 case AL_BUFFERS_QUEUED:
1010 case AL_BUFFERS_PROCESSED:
1011 case AL_SOURCE_TYPE:
1012 case AL_SOURCE_RADIUS:
1013 case AL_SOURCE_RESAMPLER_SOFT:
1014 case AL_SOURCE_SPATIALIZE_SOFT:
1015 case AL_BYTE_LENGTH_SOFT:
1016 case AL_SAMPLE_LENGTH_SOFT:
1017 case AL_SEC_LENGTH_SOFT:
1018 case AL_STEREO_MODE_SOFT:
1019 case AL_SUPER_STEREO_WIDTH_SOFT:
1020 return 1;
1022 case AL_STEREO_ANGLES:
1023 return 2;
1025 case AL_POSITION:
1026 case AL_VELOCITY:
1027 case AL_DIRECTION:
1028 return 3;
1030 case AL_ORIENTATION:
1031 return 6;
1033 case AL_SEC_OFFSET_LATENCY_SOFT:
1034 case AL_SEC_OFFSET_CLOCK_SOFT:
1035 break; /* Double only */
1037 case AL_BUFFER:
1038 case AL_DIRECT_FILTER:
1039 case AL_AUXILIARY_SEND_FILTER:
1040 break; /* i/i64 only */
1041 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1042 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1043 break; /* i64 only */
1045 return 0;
1047 ALuint DoubleValsByProp(ALenum prop)
1049 switch(static_cast<SourceProp>(prop))
1051 case AL_PITCH:
1052 case AL_GAIN:
1053 case AL_MIN_GAIN:
1054 case AL_MAX_GAIN:
1055 case AL_MAX_DISTANCE:
1056 case AL_ROLLOFF_FACTOR:
1057 case AL_DOPPLER_FACTOR:
1058 case AL_CONE_OUTER_GAIN:
1059 case AL_SEC_OFFSET:
1060 case AL_SAMPLE_OFFSET:
1061 case AL_BYTE_OFFSET:
1062 case AL_CONE_INNER_ANGLE:
1063 case AL_CONE_OUTER_ANGLE:
1064 case AL_REFERENCE_DISTANCE:
1065 case AL_CONE_OUTER_GAINHF:
1066 case AL_AIR_ABSORPTION_FACTOR:
1067 case AL_ROOM_ROLLOFF_FACTOR:
1068 case AL_DIRECT_FILTER_GAINHF_AUTO:
1069 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1070 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1071 case AL_DIRECT_CHANNELS_SOFT:
1072 case AL_DISTANCE_MODEL:
1073 case AL_SOURCE_RELATIVE:
1074 case AL_LOOPING:
1075 case AL_SOURCE_STATE:
1076 case AL_BUFFERS_QUEUED:
1077 case AL_BUFFERS_PROCESSED:
1078 case AL_SOURCE_TYPE:
1079 case AL_SOURCE_RADIUS:
1080 case AL_SOURCE_RESAMPLER_SOFT:
1081 case AL_SOURCE_SPATIALIZE_SOFT:
1082 case AL_BYTE_LENGTH_SOFT:
1083 case AL_SAMPLE_LENGTH_SOFT:
1084 case AL_SEC_LENGTH_SOFT:
1085 case AL_STEREO_MODE_SOFT:
1086 case AL_SUPER_STEREO_WIDTH_SOFT:
1087 return 1;
1089 case AL_SEC_OFFSET_LATENCY_SOFT:
1090 case AL_SEC_OFFSET_CLOCK_SOFT:
1091 case AL_STEREO_ANGLES:
1092 return 2;
1094 case AL_POSITION:
1095 case AL_VELOCITY:
1096 case AL_DIRECTION:
1097 return 3;
1099 case AL_ORIENTATION:
1100 return 6;
1102 case AL_BUFFER:
1103 case AL_DIRECT_FILTER:
1104 case AL_AUXILIARY_SEND_FILTER:
1105 break; /* i/i64 only */
1106 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1107 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1108 break; /* i64 only */
1110 return 0;
1114 void SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const float> values);
1115 void SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int> values);
1116 void SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int64_t> values);
1118 #define CHECKSIZE(v, s) do { \
1119 if LIKELY((v).size() == (s) || (v).size() == MaxValues) break; \
1120 Context->setError(AL_INVALID_ENUM, \
1121 "Property 0x%04x expects %d value(s), got %zu", prop, (s), \
1122 (v).size()); \
1123 return; \
1124 } while(0)
1125 #define CHECKVAL(x) do { \
1126 if LIKELY(x) break; \
1127 Context->setError(AL_INVALID_VALUE, "Value out of range"); \
1128 return; \
1129 } while(0)
1131 void UpdateSourceProps(ALsource *source, ALCcontext *context)
1133 if(!context->mDeferUpdates)
1135 if(Voice *voice{GetSourceVoice(source, context)})
1137 UpdateSourceProps(source, voice, context);
1138 return;
1141 source->mPropsDirty = true;
1143 #ifdef ALSOFT_EAX
1144 void CommitAndUpdateSourceProps(ALsource *source, ALCcontext *context)
1146 if(!context->mDeferUpdates)
1148 if(source->eax_is_initialized())
1149 source->eax_commit();
1150 if(Voice *voice{GetSourceVoice(source, context)})
1152 UpdateSourceProps(source, voice, context);
1153 return;
1156 source->mPropsDirty = true;
1159 #else
1161 inline void CommitAndUpdateSourceProps(ALsource *source, ALCcontext *context)
1162 { UpdateSourceProps(source, context); }
1163 #endif
1166 void SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop,
1167 const al::span<const float> values)
1169 int ival;
1171 switch(prop)
1173 case AL_SEC_LENGTH_SOFT:
1174 case AL_SEC_OFFSET_LATENCY_SOFT:
1175 case AL_SEC_OFFSET_CLOCK_SOFT:
1176 /* Query only */
1177 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1178 "Setting read-only source property 0x%04x", prop);
1180 case AL_PITCH:
1181 CHECKSIZE(values, 1);
1182 CHECKVAL(values[0] >= 0.0f);
1184 Source->Pitch = values[0];
1185 return UpdateSourceProps(Source, Context);
1187 case AL_CONE_INNER_ANGLE:
1188 CHECKSIZE(values, 1);
1189 CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
1191 Source->InnerAngle = values[0];
1192 return CommitAndUpdateSourceProps(Source, Context);
1194 case AL_CONE_OUTER_ANGLE:
1195 CHECKSIZE(values, 1);
1196 CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
1198 Source->OuterAngle = values[0];
1199 return CommitAndUpdateSourceProps(Source, Context);
1201 case AL_GAIN:
1202 CHECKSIZE(values, 1);
1203 CHECKVAL(values[0] >= 0.0f);
1205 Source->Gain = values[0];
1206 return UpdateSourceProps(Source, Context);
1208 case AL_MAX_DISTANCE:
1209 CHECKSIZE(values, 1);
1210 CHECKVAL(values[0] >= 0.0f);
1212 Source->MaxDistance = values[0];
1213 return CommitAndUpdateSourceProps(Source, Context);
1215 case AL_ROLLOFF_FACTOR:
1216 CHECKSIZE(values, 1);
1217 CHECKVAL(values[0] >= 0.0f);
1219 Source->RolloffFactor = values[0];
1220 return CommitAndUpdateSourceProps(Source, Context);
1222 case AL_REFERENCE_DISTANCE:
1223 CHECKSIZE(values, 1);
1224 CHECKVAL(values[0] >= 0.0f);
1226 Source->RefDistance = values[0];
1227 return CommitAndUpdateSourceProps(Source, Context);
1229 case AL_MIN_GAIN:
1230 CHECKSIZE(values, 1);
1231 CHECKVAL(values[0] >= 0.0f);
1233 Source->MinGain = values[0];
1234 return UpdateSourceProps(Source, Context);
1236 case AL_MAX_GAIN:
1237 CHECKSIZE(values, 1);
1238 CHECKVAL(values[0] >= 0.0f);
1240 Source->MaxGain = values[0];
1241 return UpdateSourceProps(Source, Context);
1243 case AL_CONE_OUTER_GAIN:
1244 CHECKSIZE(values, 1);
1245 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1247 Source->OuterGain = values[0];
1248 return UpdateSourceProps(Source, Context);
1250 case AL_CONE_OUTER_GAINHF:
1251 CHECKSIZE(values, 1);
1252 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1254 Source->OuterGainHF = values[0];
1255 return UpdateSourceProps(Source, Context);
1257 case AL_AIR_ABSORPTION_FACTOR:
1258 CHECKSIZE(values, 1);
1259 CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
1261 Source->AirAbsorptionFactor = values[0];
1262 return UpdateSourceProps(Source, Context);
1264 case AL_ROOM_ROLLOFF_FACTOR:
1265 CHECKSIZE(values, 1);
1266 CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
1268 Source->RoomRolloffFactor = values[0];
1269 return UpdateSourceProps(Source, Context);
1271 case AL_DOPPLER_FACTOR:
1272 CHECKSIZE(values, 1);
1273 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1275 Source->DopplerFactor = values[0];
1276 return UpdateSourceProps(Source, Context);
1278 case AL_SEC_OFFSET:
1279 case AL_SAMPLE_OFFSET:
1280 case AL_BYTE_OFFSET:
1281 CHECKSIZE(values, 1);
1282 CHECKVAL(values[0] >= 0.0f);
1284 if(Voice *voice{GetSourceVoice(Source, Context)})
1286 auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
1287 if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid offset");
1289 if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mALDevice.get()))
1290 return;
1292 Source->OffsetType = prop;
1293 Source->Offset = values[0];
1294 return;
1296 case AL_SOURCE_RADIUS:
1297 CHECKSIZE(values, 1);
1298 CHECKVAL(values[0] >= 0.0f && std::isfinite(values[0]));
1300 Source->Radius = values[0];
1301 return UpdateSourceProps(Source, Context);
1303 case AL_SUPER_STEREO_WIDTH_SOFT:
1304 CHECKSIZE(values, 1);
1305 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1307 Source->EnhWidth = values[0];
1308 return UpdateSourceProps(Source, Context);
1310 case AL_STEREO_ANGLES:
1311 CHECKSIZE(values, 2);
1312 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]));
1314 Source->StereoPan[0] = values[0];
1315 Source->StereoPan[1] = values[1];
1316 return UpdateSourceProps(Source, Context);
1319 case AL_POSITION:
1320 CHECKSIZE(values, 3);
1321 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1323 Source->Position[0] = values[0];
1324 Source->Position[1] = values[1];
1325 Source->Position[2] = values[2];
1326 return CommitAndUpdateSourceProps(Source, Context);
1328 case AL_VELOCITY:
1329 CHECKSIZE(values, 3);
1330 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1332 Source->Velocity[0] = values[0];
1333 Source->Velocity[1] = values[1];
1334 Source->Velocity[2] = values[2];
1335 return CommitAndUpdateSourceProps(Source, Context);
1337 case AL_DIRECTION:
1338 CHECKSIZE(values, 3);
1339 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1341 Source->Direction[0] = values[0];
1342 Source->Direction[1] = values[1];
1343 Source->Direction[2] = values[2];
1344 return CommitAndUpdateSourceProps(Source, Context);
1346 case AL_ORIENTATION:
1347 CHECKSIZE(values, 6);
1348 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2])
1349 && std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]));
1351 Source->OrientAt[0] = values[0];
1352 Source->OrientAt[1] = values[1];
1353 Source->OrientAt[2] = values[2];
1354 Source->OrientUp[0] = values[3];
1355 Source->OrientUp[1] = values[4];
1356 Source->OrientUp[2] = values[5];
1357 return UpdateSourceProps(Source, Context);
1360 case AL_SOURCE_RELATIVE:
1361 case AL_LOOPING:
1362 case AL_SOURCE_STATE:
1363 case AL_SOURCE_TYPE:
1364 case AL_DISTANCE_MODEL:
1365 case AL_DIRECT_FILTER_GAINHF_AUTO:
1366 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1367 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1368 case AL_DIRECT_CHANNELS_SOFT:
1369 case AL_SOURCE_RESAMPLER_SOFT:
1370 case AL_SOURCE_SPATIALIZE_SOFT:
1371 case AL_BYTE_LENGTH_SOFT:
1372 case AL_SAMPLE_LENGTH_SOFT:
1373 case AL_STEREO_MODE_SOFT:
1374 CHECKSIZE(values, 1);
1375 ival = static_cast<int>(values[0]);
1376 return SetSourceiv(Source, Context, prop, {&ival, 1u});
1378 case AL_BUFFERS_QUEUED:
1379 case AL_BUFFERS_PROCESSED:
1380 CHECKSIZE(values, 1);
1381 ival = static_cast<int>(static_cast<ALuint>(values[0]));
1382 return SetSourceiv(Source, Context, prop, {&ival, 1u});
1384 case AL_BUFFER:
1385 case AL_DIRECT_FILTER:
1386 case AL_AUXILIARY_SEND_FILTER:
1387 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1388 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1389 break;
1392 ERR("Unexpected property: 0x%04x\n", prop);
1393 Context->setError(AL_INVALID_ENUM, "Invalid source float property 0x%04x", prop);
1394 return;
1397 void SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop,
1398 const al::span<const int> values)
1400 ALCdevice *device{Context->mALDevice.get()};
1401 ALeffectslot *slot{nullptr};
1402 al::deque<ALbufferQueueItem> oldlist;
1403 std::unique_lock<std::mutex> slotlock;
1404 float fvals[6];
1406 switch(prop)
1408 case AL_SOURCE_STATE:
1409 case AL_SOURCE_TYPE:
1410 case AL_BUFFERS_QUEUED:
1411 case AL_BUFFERS_PROCESSED:
1412 case AL_BYTE_LENGTH_SOFT:
1413 case AL_SAMPLE_LENGTH_SOFT:
1414 /* Query only */
1415 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1416 "Setting read-only source property 0x%04x", prop);
1418 case AL_SOURCE_RELATIVE:
1419 CHECKSIZE(values, 1);
1420 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1422 Source->HeadRelative = values[0] != AL_FALSE;
1423 return CommitAndUpdateSourceProps(Source, Context);
1425 case AL_LOOPING:
1426 CHECKSIZE(values, 1);
1427 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1429 Source->Looping = values[0] != AL_FALSE;
1430 if(Voice *voice{GetSourceVoice(Source, Context)})
1432 if(Source->Looping)
1433 voice->mLoopBuffer.store(&Source->mQueue.front(), std::memory_order_release);
1434 else
1435 voice->mLoopBuffer.store(nullptr, std::memory_order_release);
1437 /* If the source is playing, wait for the current mix to finish to
1438 * ensure it isn't currently looping back or reaching the end.
1440 device->waitForMix();
1442 return;
1444 case AL_BUFFER:
1445 CHECKSIZE(values, 1);
1447 const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
1448 if(state == AL_PLAYING || state == AL_PAUSED)
1449 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1450 "Setting buffer on playing or paused source %u", Source->id);
1452 if(values[0])
1454 std::lock_guard<std::mutex> _{device->BufferLock};
1455 ALbuffer *buffer{LookupBuffer(device, static_cast<ALuint>(values[0]))};
1456 if(!buffer)
1457 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid buffer ID %u",
1458 static_cast<ALuint>(values[0]));
1459 if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
1460 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1461 "Setting non-persistently mapped buffer %u", buffer->id);
1462 if(buffer->mCallback && ReadRef(buffer->ref) != 0)
1463 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1464 "Setting already-set callback buffer %u", buffer->id);
1466 /* Add the selected buffer to a one-item queue */
1467 al::deque<ALbufferQueueItem> newlist;
1468 newlist.emplace_back();
1469 newlist.back().mCallback = buffer->mCallback;
1470 newlist.back().mUserData = buffer->mUserData;
1471 newlist.back().mSampleLen = buffer->mSampleLen;
1472 newlist.back().mLoopStart = buffer->mLoopStart;
1473 newlist.back().mLoopEnd = buffer->mLoopEnd;
1474 newlist.back().mSamples = buffer->mData.data();
1475 newlist.back().mBuffer = buffer;
1476 IncrementRef(buffer->ref);
1478 /* Source is now Static */
1479 Source->SourceType = AL_STATIC;
1480 Source->mQueue.swap(oldlist);
1481 Source->mQueue.swap(newlist);
1483 else
1485 /* Source is now Undetermined */
1486 Source->SourceType = AL_UNDETERMINED;
1487 Source->mQueue.swap(oldlist);
1490 /* Delete all elements in the previous queue */
1491 for(auto &item : oldlist)
1493 if(ALbuffer *buffer{item.mBuffer})
1494 DecrementRef(buffer->ref);
1496 return;
1498 case AL_SEC_OFFSET:
1499 case AL_SAMPLE_OFFSET:
1500 case AL_BYTE_OFFSET:
1501 CHECKSIZE(values, 1);
1502 CHECKVAL(values[0] >= 0);
1504 if(Voice *voice{GetSourceVoice(Source, Context)})
1506 auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
1507 if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid source offset");
1509 if(SetVoiceOffset(voice, *vpos, Source, Context, device))
1510 return;
1512 Source->OffsetType = prop;
1513 Source->Offset = values[0];
1514 return;
1516 case AL_DIRECT_FILTER:
1517 CHECKSIZE(values, 1);
1518 if(values[0])
1520 std::lock_guard<std::mutex> _{device->FilterLock};
1521 ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[0]))};
1522 if(!filter)
1523 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid filter ID %u",
1524 static_cast<ALuint>(values[0]));
1525 Source->Direct.Gain = filter->Gain;
1526 Source->Direct.GainHF = filter->GainHF;
1527 Source->Direct.HFReference = filter->HFReference;
1528 Source->Direct.GainLF = filter->GainLF;
1529 Source->Direct.LFReference = filter->LFReference;
1531 else
1533 Source->Direct.Gain = 1.0f;
1534 Source->Direct.GainHF = 1.0f;
1535 Source->Direct.HFReference = LOWPASSFREQREF;
1536 Source->Direct.GainLF = 1.0f;
1537 Source->Direct.LFReference = HIGHPASSFREQREF;
1539 return UpdateSourceProps(Source, Context);
1541 case AL_DIRECT_FILTER_GAINHF_AUTO:
1542 CHECKSIZE(values, 1);
1543 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1545 Source->DryGainHFAuto = values[0] != AL_FALSE;
1546 return UpdateSourceProps(Source, Context);
1548 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1549 CHECKSIZE(values, 1);
1550 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1552 Source->WetGainAuto = values[0] != AL_FALSE;
1553 return UpdateSourceProps(Source, Context);
1555 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1556 CHECKSIZE(values, 1);
1557 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1559 Source->WetGainHFAuto = values[0] != AL_FALSE;
1560 return UpdateSourceProps(Source, Context);
1562 case AL_DIRECT_CHANNELS_SOFT:
1563 CHECKSIZE(values, 1);
1564 if(auto mode = DirectModeFromEnum(values[0]))
1566 Source->DirectChannels = *mode;
1567 return UpdateSourceProps(Source, Context);
1569 Context->setError(AL_INVALID_VALUE, "Unsupported AL_DIRECT_CHANNELS_SOFT: 0x%04x\n",
1570 values[0]);
1571 return;
1573 case AL_DISTANCE_MODEL:
1574 CHECKSIZE(values, 1);
1575 if(auto model = DistanceModelFromALenum(values[0]))
1577 Source->mDistanceModel = *model;
1578 if(Context->mSourceDistanceModel)
1579 UpdateSourceProps(Source, Context);
1580 return;
1582 Context->setError(AL_INVALID_VALUE, "Distance model out of range: 0x%04x", values[0]);
1583 return;
1585 case AL_SOURCE_RESAMPLER_SOFT:
1586 CHECKSIZE(values, 1);
1587 CHECKVAL(values[0] >= 0 && values[0] <= static_cast<int>(Resampler::Max));
1589 Source->mResampler = static_cast<Resampler>(values[0]);
1590 return UpdateSourceProps(Source, Context);
1592 case AL_SOURCE_SPATIALIZE_SOFT:
1593 CHECKSIZE(values, 1);
1594 if(auto mode = SpatializeModeFromEnum(values[0]))
1596 Source->mSpatialize = *mode;
1597 return UpdateSourceProps(Source, Context);
1599 Context->setError(AL_INVALID_VALUE, "Unsupported AL_SOURCE_SPATIALIZE_SOFT: 0x%04x\n",
1600 values[0]);
1601 return;
1603 case AL_STEREO_MODE_SOFT:
1604 CHECKSIZE(values, 1);
1606 const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
1607 if(state == AL_PLAYING || state == AL_PAUSED)
1608 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1609 "Modifying stereo mode on playing or paused source %u", Source->id);
1611 if(auto mode = StereoModeFromEnum(values[0]))
1613 Source->mStereoMode = *mode;
1614 return;
1616 Context->setError(AL_INVALID_VALUE, "Unsupported AL_STEREO_MODE_SOFT: 0x%04x\n",
1617 values[0]);
1618 return;
1620 case AL_AUXILIARY_SEND_FILTER:
1621 CHECKSIZE(values, 3);
1622 slotlock = std::unique_lock<std::mutex>{Context->mEffectSlotLock};
1623 if(values[0] && (slot=LookupEffectSlot(Context, static_cast<ALuint>(values[0]))) == nullptr)
1624 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid effect ID %u", values[0]);
1625 if(static_cast<ALuint>(values[1]) >= device->NumAuxSends)
1626 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid send %u", values[1]);
1628 if(values[2])
1630 std::lock_guard<std::mutex> _{device->FilterLock};
1631 ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[2]))};
1632 if(!filter)
1633 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid filter ID %u", values[2]);
1635 auto &send = Source->Send[static_cast<ALuint>(values[1])];
1636 send.Gain = filter->Gain;
1637 send.GainHF = filter->GainHF;
1638 send.HFReference = filter->HFReference;
1639 send.GainLF = filter->GainLF;
1640 send.LFReference = filter->LFReference;
1642 else
1644 /* Disable filter */
1645 auto &send = Source->Send[static_cast<ALuint>(values[1])];
1646 send.Gain = 1.0f;
1647 send.GainHF = 1.0f;
1648 send.HFReference = LOWPASSFREQREF;
1649 send.GainLF = 1.0f;
1650 send.LFReference = HIGHPASSFREQREF;
1653 if(slot != Source->Send[static_cast<ALuint>(values[1])].Slot && IsPlayingOrPaused(Source))
1655 /* Add refcount on the new slot, and release the previous slot */
1656 if(slot) IncrementRef(slot->ref);
1657 if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
1658 DecrementRef(oldslot->ref);
1659 Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
1661 /* We must force an update if the auxiliary slot changed on an
1662 * active source, in case the slot is about to be deleted.
1664 Voice *voice{GetSourceVoice(Source, Context)};
1665 if(voice) UpdateSourceProps(Source, voice, Context);
1666 else Source->mPropsDirty = true;
1668 else
1670 if(slot) IncrementRef(slot->ref);
1671 if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
1672 DecrementRef(oldslot->ref);
1673 Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
1674 UpdateSourceProps(Source, Context);
1676 return;
1679 /* 1x float */
1680 case AL_CONE_INNER_ANGLE:
1681 case AL_CONE_OUTER_ANGLE:
1682 case AL_PITCH:
1683 case AL_GAIN:
1684 case AL_MIN_GAIN:
1685 case AL_MAX_GAIN:
1686 case AL_REFERENCE_DISTANCE:
1687 case AL_ROLLOFF_FACTOR:
1688 case AL_CONE_OUTER_GAIN:
1689 case AL_MAX_DISTANCE:
1690 case AL_DOPPLER_FACTOR:
1691 case AL_CONE_OUTER_GAINHF:
1692 case AL_AIR_ABSORPTION_FACTOR:
1693 case AL_ROOM_ROLLOFF_FACTOR:
1694 case AL_SOURCE_RADIUS:
1695 case AL_SEC_LENGTH_SOFT:
1696 case AL_SUPER_STEREO_WIDTH_SOFT:
1697 CHECKSIZE(values, 1);
1698 fvals[0] = static_cast<float>(values[0]);
1699 return SetSourcefv(Source, Context, prop, {fvals, 1u});
1701 /* 3x float */
1702 case AL_POSITION:
1703 case AL_VELOCITY:
1704 case AL_DIRECTION:
1705 CHECKSIZE(values, 3);
1706 fvals[0] = static_cast<float>(values[0]);
1707 fvals[1] = static_cast<float>(values[1]);
1708 fvals[2] = static_cast<float>(values[2]);
1709 return SetSourcefv(Source, Context, prop, {fvals, 3u});
1711 /* 6x float */
1712 case AL_ORIENTATION:
1713 CHECKSIZE(values, 6);
1714 fvals[0] = static_cast<float>(values[0]);
1715 fvals[1] = static_cast<float>(values[1]);
1716 fvals[2] = static_cast<float>(values[2]);
1717 fvals[3] = static_cast<float>(values[3]);
1718 fvals[4] = static_cast<float>(values[4]);
1719 fvals[5] = static_cast<float>(values[5]);
1720 return SetSourcefv(Source, Context, prop, {fvals, 6u});
1722 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1723 case AL_SEC_OFFSET_LATENCY_SOFT:
1724 case AL_SEC_OFFSET_CLOCK_SOFT:
1725 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1726 case AL_STEREO_ANGLES:
1727 break;
1730 ERR("Unexpected property: 0x%04x\n", prop);
1731 Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
1732 return;
1735 void SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop,
1736 const al::span<const int64_t> values)
1738 float fvals[MaxValues];
1739 int ivals[MaxValues];
1741 switch(prop)
1743 case AL_SOURCE_TYPE:
1744 case AL_BUFFERS_QUEUED:
1745 case AL_BUFFERS_PROCESSED:
1746 case AL_SOURCE_STATE:
1747 case AL_BYTE_LENGTH_SOFT:
1748 case AL_SAMPLE_LENGTH_SOFT:
1749 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1750 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1751 /* Query only */
1752 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1753 "Setting read-only source property 0x%04x", prop);
1755 /* 1x int */
1756 case AL_SOURCE_RELATIVE:
1757 case AL_LOOPING:
1758 case AL_SEC_OFFSET:
1759 case AL_SAMPLE_OFFSET:
1760 case AL_BYTE_OFFSET:
1761 case AL_DIRECT_FILTER_GAINHF_AUTO:
1762 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1763 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1764 case AL_DIRECT_CHANNELS_SOFT:
1765 case AL_DISTANCE_MODEL:
1766 case AL_SOURCE_RESAMPLER_SOFT:
1767 case AL_SOURCE_SPATIALIZE_SOFT:
1768 case AL_STEREO_MODE_SOFT:
1769 CHECKSIZE(values, 1);
1770 CHECKVAL(values[0] <= INT_MAX && values[0] >= INT_MIN);
1772 ivals[0] = static_cast<int>(values[0]);
1773 return SetSourceiv(Source, Context, prop, {ivals, 1u});
1775 /* 1x uint */
1776 case AL_BUFFER:
1777 case AL_DIRECT_FILTER:
1778 CHECKSIZE(values, 1);
1779 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0);
1781 ivals[0] = static_cast<int>(values[0]);
1782 return SetSourceiv(Source, Context, prop, {ivals, 1u});
1784 /* 3x uint */
1785 case AL_AUXILIARY_SEND_FILTER:
1786 CHECKSIZE(values, 3);
1787 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 && values[1] <= UINT_MAX && values[1] >= 0
1788 && values[2] <= UINT_MAX && values[2] >= 0);
1790 ivals[0] = static_cast<int>(values[0]);
1791 ivals[1] = static_cast<int>(values[1]);
1792 ivals[2] = static_cast<int>(values[2]);
1793 return SetSourceiv(Source, Context, prop, {ivals, 3u});
1795 /* 1x float */
1796 case AL_CONE_INNER_ANGLE:
1797 case AL_CONE_OUTER_ANGLE:
1798 case AL_PITCH:
1799 case AL_GAIN:
1800 case AL_MIN_GAIN:
1801 case AL_MAX_GAIN:
1802 case AL_REFERENCE_DISTANCE:
1803 case AL_ROLLOFF_FACTOR:
1804 case AL_CONE_OUTER_GAIN:
1805 case AL_MAX_DISTANCE:
1806 case AL_DOPPLER_FACTOR:
1807 case AL_CONE_OUTER_GAINHF:
1808 case AL_AIR_ABSORPTION_FACTOR:
1809 case AL_ROOM_ROLLOFF_FACTOR:
1810 case AL_SOURCE_RADIUS:
1811 case AL_SEC_LENGTH_SOFT:
1812 case AL_SUPER_STEREO_WIDTH_SOFT:
1813 CHECKSIZE(values, 1);
1814 fvals[0] = static_cast<float>(values[0]);
1815 return SetSourcefv(Source, Context, prop, {fvals, 1u});
1817 /* 3x float */
1818 case AL_POSITION:
1819 case AL_VELOCITY:
1820 case AL_DIRECTION:
1821 CHECKSIZE(values, 3);
1822 fvals[0] = static_cast<float>(values[0]);
1823 fvals[1] = static_cast<float>(values[1]);
1824 fvals[2] = static_cast<float>(values[2]);
1825 return SetSourcefv(Source, Context, prop, {fvals, 3u});
1827 /* 6x float */
1828 case AL_ORIENTATION:
1829 CHECKSIZE(values, 6);
1830 fvals[0] = static_cast<float>(values[0]);
1831 fvals[1] = static_cast<float>(values[1]);
1832 fvals[2] = static_cast<float>(values[2]);
1833 fvals[3] = static_cast<float>(values[3]);
1834 fvals[4] = static_cast<float>(values[4]);
1835 fvals[5] = static_cast<float>(values[5]);
1836 return SetSourcefv(Source, Context, prop, {fvals, 6u});
1838 case AL_SEC_OFFSET_LATENCY_SOFT:
1839 case AL_SEC_OFFSET_CLOCK_SOFT:
1840 case AL_STEREO_ANGLES:
1841 break;
1844 ERR("Unexpected property: 0x%04x\n", prop);
1845 Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
1846 return;
1849 #undef CHECKVAL
1850 #undef CHECKSIZE
1852 #define CHECKSIZE(v, s) do { \
1853 if LIKELY((v).size() == (s) || (v).size() == MaxValues) break; \
1854 Context->setError(AL_INVALID_ENUM, \
1855 "Property 0x%04x expects %d value(s), got %zu", prop, (s), \
1856 (v).size()); \
1857 return false; \
1858 } while(0)
1860 bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values);
1861 bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int> values);
1862 bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values);
1864 bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values)
1866 ALCdevice *device{Context->mALDevice.get()};
1867 ClockLatency clocktime;
1868 nanoseconds srcclock;
1869 int ivals[MaxValues];
1870 bool err;
1872 switch(prop)
1874 case AL_GAIN:
1875 CHECKSIZE(values, 1);
1876 values[0] = Source->Gain;
1877 return true;
1879 case AL_PITCH:
1880 CHECKSIZE(values, 1);
1881 values[0] = Source->Pitch;
1882 return true;
1884 case AL_MAX_DISTANCE:
1885 CHECKSIZE(values, 1);
1886 values[0] = Source->MaxDistance;
1887 return true;
1889 case AL_ROLLOFF_FACTOR:
1890 CHECKSIZE(values, 1);
1891 values[0] = Source->RolloffFactor;
1892 return true;
1894 case AL_REFERENCE_DISTANCE:
1895 CHECKSIZE(values, 1);
1896 values[0] = Source->RefDistance;
1897 return true;
1899 case AL_CONE_INNER_ANGLE:
1900 CHECKSIZE(values, 1);
1901 values[0] = Source->InnerAngle;
1902 return true;
1904 case AL_CONE_OUTER_ANGLE:
1905 CHECKSIZE(values, 1);
1906 values[0] = Source->OuterAngle;
1907 return true;
1909 case AL_MIN_GAIN:
1910 CHECKSIZE(values, 1);
1911 values[0] = Source->MinGain;
1912 return true;
1914 case AL_MAX_GAIN:
1915 CHECKSIZE(values, 1);
1916 values[0] = Source->MaxGain;
1917 return true;
1919 case AL_CONE_OUTER_GAIN:
1920 CHECKSIZE(values, 1);
1921 values[0] = Source->OuterGain;
1922 return true;
1924 case AL_SEC_OFFSET:
1925 case AL_SAMPLE_OFFSET:
1926 case AL_BYTE_OFFSET:
1927 CHECKSIZE(values, 1);
1928 values[0] = GetSourceOffset(Source, prop, Context);
1929 return true;
1931 case AL_CONE_OUTER_GAINHF:
1932 CHECKSIZE(values, 1);
1933 values[0] = Source->OuterGainHF;
1934 return true;
1936 case AL_AIR_ABSORPTION_FACTOR:
1937 CHECKSIZE(values, 1);
1938 values[0] = Source->AirAbsorptionFactor;
1939 return true;
1941 case AL_ROOM_ROLLOFF_FACTOR:
1942 CHECKSIZE(values, 1);
1943 values[0] = Source->RoomRolloffFactor;
1944 return true;
1946 case AL_DOPPLER_FACTOR:
1947 CHECKSIZE(values, 1);
1948 values[0] = Source->DopplerFactor;
1949 return true;
1951 case AL_SOURCE_RADIUS:
1952 CHECKSIZE(values, 1);
1953 values[0] = Source->Radius;
1954 return true;
1956 case AL_SUPER_STEREO_WIDTH_SOFT:
1957 CHECKSIZE(values, 1);
1958 values[0] = Source->EnhWidth;
1959 return true;
1961 case AL_BYTE_LENGTH_SOFT:
1962 case AL_SAMPLE_LENGTH_SOFT:
1963 case AL_SEC_LENGTH_SOFT:
1964 CHECKSIZE(values, 1);
1965 values[0] = GetSourceLength(Source, prop);
1966 return true;
1968 case AL_STEREO_ANGLES:
1969 CHECKSIZE(values, 2);
1970 values[0] = Source->StereoPan[0];
1971 values[1] = Source->StereoPan[1];
1972 return true;
1974 case AL_SEC_OFFSET_LATENCY_SOFT:
1975 CHECKSIZE(values, 2);
1976 /* Get the source offset with the clock time first. Then get the clock
1977 * time with the device latency. Order is important.
1979 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1981 std::lock_guard<std::mutex> _{device->StateLock};
1982 clocktime = GetClockLatency(device, device->Backend.get());
1984 if(srcclock == clocktime.ClockTime)
1985 values[1] = static_cast<double>(clocktime.Latency.count()) / 1000000000.0;
1986 else
1988 /* If the clock time incremented, reduce the latency by that much
1989 * since it's that much closer to the source offset it got earlier.
1991 const nanoseconds diff{clocktime.ClockTime - srcclock};
1992 const nanoseconds latency{clocktime.Latency - std::min(clocktime.Latency, diff)};
1993 values[1] = static_cast<double>(latency.count()) / 1000000000.0;
1995 return true;
1997 case AL_SEC_OFFSET_CLOCK_SOFT:
1998 CHECKSIZE(values, 2);
1999 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
2000 values[1] = static_cast<double>(srcclock.count()) / 1000000000.0;
2001 return true;
2003 case AL_POSITION:
2004 CHECKSIZE(values, 3);
2005 values[0] = Source->Position[0];
2006 values[1] = Source->Position[1];
2007 values[2] = Source->Position[2];
2008 return true;
2010 case AL_VELOCITY:
2011 CHECKSIZE(values, 3);
2012 values[0] = Source->Velocity[0];
2013 values[1] = Source->Velocity[1];
2014 values[2] = Source->Velocity[2];
2015 return true;
2017 case AL_DIRECTION:
2018 CHECKSIZE(values, 3);
2019 values[0] = Source->Direction[0];
2020 values[1] = Source->Direction[1];
2021 values[2] = Source->Direction[2];
2022 return true;
2024 case AL_ORIENTATION:
2025 CHECKSIZE(values, 6);
2026 values[0] = Source->OrientAt[0];
2027 values[1] = Source->OrientAt[1];
2028 values[2] = Source->OrientAt[2];
2029 values[3] = Source->OrientUp[0];
2030 values[4] = Source->OrientUp[1];
2031 values[5] = Source->OrientUp[2];
2032 return true;
2034 /* 1x int */
2035 case AL_SOURCE_RELATIVE:
2036 case AL_LOOPING:
2037 case AL_SOURCE_STATE:
2038 case AL_BUFFERS_QUEUED:
2039 case AL_BUFFERS_PROCESSED:
2040 case AL_SOURCE_TYPE:
2041 case AL_DIRECT_FILTER_GAINHF_AUTO:
2042 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2043 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2044 case AL_DIRECT_CHANNELS_SOFT:
2045 case AL_DISTANCE_MODEL:
2046 case AL_SOURCE_RESAMPLER_SOFT:
2047 case AL_SOURCE_SPATIALIZE_SOFT:
2048 case AL_STEREO_MODE_SOFT:
2049 CHECKSIZE(values, 1);
2050 if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2051 values[0] = static_cast<double>(ivals[0]);
2052 return err;
2054 case AL_BUFFER:
2055 case AL_DIRECT_FILTER:
2056 case AL_AUXILIARY_SEND_FILTER:
2057 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
2058 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
2059 break;
2062 ERR("Unexpected property: 0x%04x\n", prop);
2063 Context->setError(AL_INVALID_ENUM, "Invalid source double property 0x%04x", prop);
2064 return false;
2067 bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int> values)
2069 double dvals[MaxValues];
2070 bool err;
2072 switch(prop)
2074 case AL_SOURCE_RELATIVE:
2075 CHECKSIZE(values, 1);
2076 values[0] = Source->HeadRelative;
2077 return true;
2079 case AL_LOOPING:
2080 CHECKSIZE(values, 1);
2081 values[0] = Source->Looping;
2082 return true;
2084 case AL_BUFFER:
2085 CHECKSIZE(values, 1);
2087 ALbufferQueueItem *BufferList{(Source->SourceType == AL_STATIC)
2088 ? &Source->mQueue.front() : nullptr};
2089 ALbuffer *buffer{BufferList ? BufferList->mBuffer : nullptr};
2090 values[0] = buffer ? static_cast<int>(buffer->id) : 0;
2092 return true;
2094 case AL_SOURCE_STATE:
2095 CHECKSIZE(values, 1);
2096 values[0] = GetSourceState(Source, GetSourceVoice(Source, Context));
2097 return true;
2099 case AL_BUFFERS_QUEUED:
2100 CHECKSIZE(values, 1);
2101 values[0] = static_cast<int>(Source->mQueue.size());
2102 return true;
2104 case AL_BUFFERS_PROCESSED:
2105 CHECKSIZE(values, 1);
2106 if(Source->Looping || Source->SourceType != AL_STREAMING)
2108 /* Buffers on a looping source are in a perpetual state of PENDING,
2109 * so don't report any as PROCESSED
2111 values[0] = 0;
2113 else
2115 int played{0};
2116 if(Source->state != AL_INITIAL)
2118 const VoiceBufferItem *Current{nullptr};
2119 if(Voice *voice{GetSourceVoice(Source, Context)})
2120 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
2121 for(auto &item : Source->mQueue)
2123 if(&item == Current)
2124 break;
2125 ++played;
2128 values[0] = played;
2130 return true;
2132 case AL_SOURCE_TYPE:
2133 CHECKSIZE(values, 1);
2134 values[0] = Source->SourceType;
2135 return true;
2137 case AL_DIRECT_FILTER_GAINHF_AUTO:
2138 CHECKSIZE(values, 1);
2139 values[0] = Source->DryGainHFAuto;
2140 return true;
2142 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2143 CHECKSIZE(values, 1);
2144 values[0] = Source->WetGainAuto;
2145 return true;
2147 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2148 CHECKSIZE(values, 1);
2149 values[0] = Source->WetGainHFAuto;
2150 return true;
2152 case AL_DIRECT_CHANNELS_SOFT:
2153 CHECKSIZE(values, 1);
2154 values[0] = EnumFromDirectMode(Source->DirectChannels);
2155 return true;
2157 case AL_DISTANCE_MODEL:
2158 CHECKSIZE(values, 1);
2159 values[0] = ALenumFromDistanceModel(Source->mDistanceModel);
2160 return true;
2162 case AL_BYTE_LENGTH_SOFT:
2163 case AL_SAMPLE_LENGTH_SOFT:
2164 case AL_SEC_LENGTH_SOFT:
2165 CHECKSIZE(values, 1);
2166 values[0] = static_cast<int>(mind(GetSourceLength(Source, prop),
2167 std::numeric_limits<int>::max()));
2168 return true;
2170 case AL_SOURCE_RESAMPLER_SOFT:
2171 CHECKSIZE(values, 1);
2172 values[0] = static_cast<int>(Source->mResampler);
2173 return true;
2175 case AL_SOURCE_SPATIALIZE_SOFT:
2176 CHECKSIZE(values, 1);
2177 values[0] = EnumFromSpatializeMode(Source->mSpatialize);
2178 return true;
2180 case AL_STEREO_MODE_SOFT:
2181 CHECKSIZE(values, 1);
2182 values[0] = EnumFromStereoMode(Source->mStereoMode);
2183 return true;
2185 /* 1x float/double */
2186 case AL_CONE_INNER_ANGLE:
2187 case AL_CONE_OUTER_ANGLE:
2188 case AL_PITCH:
2189 case AL_GAIN:
2190 case AL_MIN_GAIN:
2191 case AL_MAX_GAIN:
2192 case AL_REFERENCE_DISTANCE:
2193 case AL_ROLLOFF_FACTOR:
2194 case AL_CONE_OUTER_GAIN:
2195 case AL_MAX_DISTANCE:
2196 case AL_SEC_OFFSET:
2197 case AL_SAMPLE_OFFSET:
2198 case AL_BYTE_OFFSET:
2199 case AL_DOPPLER_FACTOR:
2200 case AL_AIR_ABSORPTION_FACTOR:
2201 case AL_ROOM_ROLLOFF_FACTOR:
2202 case AL_CONE_OUTER_GAINHF:
2203 case AL_SOURCE_RADIUS:
2204 case AL_SUPER_STEREO_WIDTH_SOFT:
2205 CHECKSIZE(values, 1);
2206 if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
2207 values[0] = static_cast<int>(dvals[0]);
2208 return err;
2210 /* 3x float/double */
2211 case AL_POSITION:
2212 case AL_VELOCITY:
2213 case AL_DIRECTION:
2214 CHECKSIZE(values, 3);
2215 if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
2217 values[0] = static_cast<int>(dvals[0]);
2218 values[1] = static_cast<int>(dvals[1]);
2219 values[2] = static_cast<int>(dvals[2]);
2221 return err;
2223 /* 6x float/double */
2224 case AL_ORIENTATION:
2225 CHECKSIZE(values, 6);
2226 if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
2228 values[0] = static_cast<int>(dvals[0]);
2229 values[1] = static_cast<int>(dvals[1]);
2230 values[2] = static_cast<int>(dvals[2]);
2231 values[3] = static_cast<int>(dvals[3]);
2232 values[4] = static_cast<int>(dvals[4]);
2233 values[5] = static_cast<int>(dvals[5]);
2235 return err;
2237 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
2238 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
2239 break; /* i64 only */
2240 case AL_SEC_OFFSET_LATENCY_SOFT:
2241 case AL_SEC_OFFSET_CLOCK_SOFT:
2242 break; /* Double only */
2243 case AL_STEREO_ANGLES:
2244 break; /* Float/double only */
2246 case AL_DIRECT_FILTER:
2247 case AL_AUXILIARY_SEND_FILTER:
2248 break; /* ??? */
2251 ERR("Unexpected property: 0x%04x\n", prop);
2252 Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
2253 return false;
2256 bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values)
2258 ALCdevice *device{Context->mALDevice.get()};
2259 ClockLatency clocktime;
2260 nanoseconds srcclock;
2261 double dvals[MaxValues];
2262 int ivals[MaxValues];
2263 bool err;
2265 switch(prop)
2267 case AL_BYTE_LENGTH_SOFT:
2268 case AL_SAMPLE_LENGTH_SOFT:
2269 case AL_SEC_LENGTH_SOFT:
2270 CHECKSIZE(values, 1);
2271 values[0] = static_cast<int64_t>(GetSourceLength(Source, prop));
2272 return true;
2274 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
2275 CHECKSIZE(values, 2);
2276 /* Get the source offset with the clock time first. Then get the clock
2277 * time with the device latency. Order is important.
2279 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
2281 std::lock_guard<std::mutex> _{device->StateLock};
2282 clocktime = GetClockLatency(device, device->Backend.get());
2284 if(srcclock == clocktime.ClockTime)
2285 values[1] = clocktime.Latency.count();
2286 else
2288 /* If the clock time incremented, reduce the latency by that much
2289 * since it's that much closer to the source offset it got earlier.
2291 const nanoseconds diff{clocktime.ClockTime - srcclock};
2292 values[1] = nanoseconds{clocktime.Latency - std::min(clocktime.Latency, diff)}.count();
2294 return true;
2296 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
2297 CHECKSIZE(values, 2);
2298 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
2299 values[1] = srcclock.count();
2300 return true;
2302 /* 1x float/double */
2303 case AL_CONE_INNER_ANGLE:
2304 case AL_CONE_OUTER_ANGLE:
2305 case AL_PITCH:
2306 case AL_GAIN:
2307 case AL_MIN_GAIN:
2308 case AL_MAX_GAIN:
2309 case AL_REFERENCE_DISTANCE:
2310 case AL_ROLLOFF_FACTOR:
2311 case AL_CONE_OUTER_GAIN:
2312 case AL_MAX_DISTANCE:
2313 case AL_SEC_OFFSET:
2314 case AL_SAMPLE_OFFSET:
2315 case AL_BYTE_OFFSET:
2316 case AL_DOPPLER_FACTOR:
2317 case AL_AIR_ABSORPTION_FACTOR:
2318 case AL_ROOM_ROLLOFF_FACTOR:
2319 case AL_CONE_OUTER_GAINHF:
2320 case AL_SOURCE_RADIUS:
2321 case AL_SUPER_STEREO_WIDTH_SOFT:
2322 CHECKSIZE(values, 1);
2323 if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
2324 values[0] = static_cast<int64_t>(dvals[0]);
2325 return err;
2327 /* 3x float/double */
2328 case AL_POSITION:
2329 case AL_VELOCITY:
2330 case AL_DIRECTION:
2331 CHECKSIZE(values, 3);
2332 if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
2334 values[0] = static_cast<int64_t>(dvals[0]);
2335 values[1] = static_cast<int64_t>(dvals[1]);
2336 values[2] = static_cast<int64_t>(dvals[2]);
2338 return err;
2340 /* 6x float/double */
2341 case AL_ORIENTATION:
2342 CHECKSIZE(values, 6);
2343 if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
2345 values[0] = static_cast<int64_t>(dvals[0]);
2346 values[1] = static_cast<int64_t>(dvals[1]);
2347 values[2] = static_cast<int64_t>(dvals[2]);
2348 values[3] = static_cast<int64_t>(dvals[3]);
2349 values[4] = static_cast<int64_t>(dvals[4]);
2350 values[5] = static_cast<int64_t>(dvals[5]);
2352 return err;
2354 /* 1x int */
2355 case AL_SOURCE_RELATIVE:
2356 case AL_LOOPING:
2357 case AL_SOURCE_STATE:
2358 case AL_BUFFERS_QUEUED:
2359 case AL_BUFFERS_PROCESSED:
2360 case AL_SOURCE_TYPE:
2361 case AL_DIRECT_FILTER_GAINHF_AUTO:
2362 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2363 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2364 case AL_DIRECT_CHANNELS_SOFT:
2365 case AL_DISTANCE_MODEL:
2366 case AL_SOURCE_RESAMPLER_SOFT:
2367 case AL_SOURCE_SPATIALIZE_SOFT:
2368 case AL_STEREO_MODE_SOFT:
2369 CHECKSIZE(values, 1);
2370 if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2371 values[0] = ivals[0];
2372 return err;
2374 /* 1x uint */
2375 case AL_BUFFER:
2376 case AL_DIRECT_FILTER:
2377 CHECKSIZE(values, 1);
2378 if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2379 values[0] = static_cast<ALuint>(ivals[0]);
2380 return err;
2382 /* 3x uint */
2383 case AL_AUXILIARY_SEND_FILTER:
2384 CHECKSIZE(values, 3);
2385 if((err=GetSourceiv(Source, Context, prop, {ivals, 3u})) != false)
2387 values[0] = static_cast<ALuint>(ivals[0]);
2388 values[1] = static_cast<ALuint>(ivals[1]);
2389 values[2] = static_cast<ALuint>(ivals[2]);
2391 return err;
2393 case AL_SEC_OFFSET_LATENCY_SOFT:
2394 case AL_SEC_OFFSET_CLOCK_SOFT:
2395 break; /* Double only */
2396 case AL_STEREO_ANGLES:
2397 break; /* Float/double only */
2400 ERR("Unexpected property: 0x%04x\n", prop);
2401 Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
2402 return false;
2405 } // namespace
2407 AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
2408 START_API_FUNC
2410 ContextRef context{GetContextRef()};
2411 if UNLIKELY(!context) return;
2413 if UNLIKELY(n < 0)
2414 context->setError(AL_INVALID_VALUE, "Generating %d sources", n);
2415 if UNLIKELY(n <= 0) return;
2417 #ifdef ALSOFT_EAX
2418 const bool has_eax{context->has_eax()};
2419 std::unique_lock<std::mutex> proplock{};
2420 if(has_eax)
2421 proplock = std::unique_lock<std::mutex>{context->mPropLock};
2422 #endif
2423 std::unique_lock<std::mutex> srclock{context->mSourceLock};
2424 ALCdevice *device{context->mALDevice.get()};
2425 if(static_cast<ALuint>(n) > device->SourcesMax-context->mNumSources)
2427 context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)",
2428 device->SourcesMax, context->mNumSources, n);
2429 return;
2431 if(!EnsureSources(context.get(), static_cast<ALuint>(n)))
2433 context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, (n==1)?"":"s");
2434 return;
2437 if(n == 1)
2439 ALsource *source{AllocSource(context.get())};
2440 sources[0] = source->id;
2442 #ifdef ALSOFT_EAX
2443 if(has_eax)
2444 source->eax_initialize(context.get());
2445 #endif // ALSOFT_EAX
2447 else
2449 #ifdef ALSOFT_EAX
2450 auto eax_sources = al::vector<ALsource*>{};
2451 if(has_eax)
2452 eax_sources.reserve(static_cast<ALuint>(n));
2453 #endif // ALSOFT_EAX
2455 al::vector<ALuint> ids;
2456 ids.reserve(static_cast<ALuint>(n));
2457 do {
2458 ALsource *source{AllocSource(context.get())};
2459 ids.emplace_back(source->id);
2461 #ifdef ALSOFT_EAX
2462 if(has_eax)
2463 eax_sources.emplace_back(source);
2464 #endif // ALSOFT_EAX
2465 } while(--n);
2466 std::copy(ids.cbegin(), ids.cend(), sources);
2468 #ifdef ALSOFT_EAX
2469 for(auto& eax_source : eax_sources)
2470 eax_source->eax_initialize(context.get());
2471 #endif // ALSOFT_EAX
2474 END_API_FUNC
2476 AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
2477 START_API_FUNC
2479 ContextRef context{GetContextRef()};
2480 if UNLIKELY(!context) return;
2482 if UNLIKELY(n < 0)
2483 SETERR_RETURN(context, AL_INVALID_VALUE,, "Deleting %d sources", n);
2485 std::lock_guard<std::mutex> _{context->mSourceLock};
2487 /* Check that all Sources are valid */
2488 auto validate_source = [&context](const ALuint sid) -> bool
2489 { return LookupSource(context.get(), sid) != nullptr; };
2491 const ALuint *sources_end = sources + n;
2492 auto invsrc = std::find_if_not(sources, sources_end, validate_source);
2493 if UNLIKELY(invsrc != sources_end)
2495 context->setError(AL_INVALID_NAME, "Invalid source ID %u", *invsrc);
2496 return;
2499 /* All good. Delete source IDs. */
2500 auto delete_source = [&context](const ALuint sid) -> void
2502 ALsource *src{LookupSource(context.get(), sid)};
2503 if(src) FreeSource(context.get(), src);
2505 std::for_each(sources, sources_end, delete_source);
2507 END_API_FUNC
2509 AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
2510 START_API_FUNC
2512 ContextRef context{GetContextRef()};
2513 if LIKELY(context)
2515 std::lock_guard<std::mutex> _{context->mSourceLock};
2516 if(LookupSource(context.get(), source) != nullptr)
2517 return AL_TRUE;
2519 return AL_FALSE;
2521 END_API_FUNC
2524 AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
2525 START_API_FUNC
2527 ContextRef context{GetContextRef()};
2528 if UNLIKELY(!context) return;
2530 std::lock_guard<std::mutex> _{context->mPropLock};
2531 std::lock_guard<std::mutex> __{context->mSourceLock};
2532 ALsource *Source = LookupSource(context.get(), source);
2533 if UNLIKELY(!Source)
2534 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2535 else
2536 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2538 END_API_FUNC
2540 AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
2541 START_API_FUNC
2543 ContextRef context{GetContextRef()};
2544 if UNLIKELY(!context) return;
2546 std::lock_guard<std::mutex> _{context->mPropLock};
2547 std::lock_guard<std::mutex> __{context->mSourceLock};
2548 ALsource *Source = LookupSource(context.get(), source);
2549 if UNLIKELY(!Source)
2550 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2551 else
2553 const float fvals[3]{ value1, value2, value3 };
2554 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2557 END_API_FUNC
2559 AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
2560 START_API_FUNC
2562 ContextRef context{GetContextRef()};
2563 if UNLIKELY(!context) return;
2565 std::lock_guard<std::mutex> _{context->mPropLock};
2566 std::lock_guard<std::mutex> __{context->mSourceLock};
2567 ALsource *Source = LookupSource(context.get(), source);
2568 if UNLIKELY(!Source)
2569 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2570 else if UNLIKELY(!values)
2571 context->setError(AL_INVALID_VALUE, "NULL pointer");
2572 else
2573 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2575 END_API_FUNC
2578 AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
2579 START_API_FUNC
2581 ContextRef context{GetContextRef()};
2582 if UNLIKELY(!context) return;
2584 std::lock_guard<std::mutex> _{context->mPropLock};
2585 std::lock_guard<std::mutex> __{context->mSourceLock};
2586 ALsource *Source = LookupSource(context.get(), source);
2587 if UNLIKELY(!Source)
2588 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2589 else
2591 const float fval[1]{static_cast<float>(value)};
2592 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fval);
2595 END_API_FUNC
2597 AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
2598 START_API_FUNC
2600 ContextRef context{GetContextRef()};
2601 if UNLIKELY(!context) return;
2603 std::lock_guard<std::mutex> _{context->mPropLock};
2604 std::lock_guard<std::mutex> __{context->mSourceLock};
2605 ALsource *Source = LookupSource(context.get(), source);
2606 if UNLIKELY(!Source)
2607 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2608 else
2610 const float fvals[3]{static_cast<float>(value1), static_cast<float>(value2),
2611 static_cast<float>(value3)};
2612 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2615 END_API_FUNC
2617 AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
2618 START_API_FUNC
2620 ContextRef context{GetContextRef()};
2621 if UNLIKELY(!context) return;
2623 std::lock_guard<std::mutex> _{context->mPropLock};
2624 std::lock_guard<std::mutex> __{context->mSourceLock};
2625 ALsource *Source = LookupSource(context.get(), source);
2626 if UNLIKELY(!Source)
2627 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2628 else if UNLIKELY(!values)
2629 context->setError(AL_INVALID_VALUE, "NULL pointer");
2630 else
2632 const ALuint count{DoubleValsByProp(param)};
2633 float fvals[MaxValues];
2634 for(ALuint i{0};i < count;i++)
2635 fvals[i] = static_cast<float>(values[i]);
2636 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {fvals, count});
2639 END_API_FUNC
2642 AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
2643 START_API_FUNC
2645 ContextRef context{GetContextRef()};
2646 if UNLIKELY(!context) return;
2648 std::lock_guard<std::mutex> _{context->mPropLock};
2649 std::lock_guard<std::mutex> __{context->mSourceLock};
2650 ALsource *Source = LookupSource(context.get(), source);
2651 if UNLIKELY(!Source)
2652 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2653 else
2654 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2656 END_API_FUNC
2658 AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
2659 START_API_FUNC
2661 ContextRef context{GetContextRef()};
2662 if UNLIKELY(!context) return;
2664 std::lock_guard<std::mutex> _{context->mPropLock};
2665 std::lock_guard<std::mutex> __{context->mSourceLock};
2666 ALsource *Source = LookupSource(context.get(), source);
2667 if UNLIKELY(!Source)
2668 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2669 else
2671 const int ivals[3]{ value1, value2, value3 };
2672 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals);
2675 END_API_FUNC
2677 AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
2678 START_API_FUNC
2680 ContextRef context{GetContextRef()};
2681 if UNLIKELY(!context) return;
2683 std::lock_guard<std::mutex> _{context->mPropLock};
2684 std::lock_guard<std::mutex> __{context->mSourceLock};
2685 ALsource *Source = LookupSource(context.get(), source);
2686 if UNLIKELY(!Source)
2687 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2688 else if UNLIKELY(!values)
2689 context->setError(AL_INVALID_VALUE, "NULL pointer");
2690 else
2691 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2693 END_API_FUNC
2696 AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
2697 START_API_FUNC
2699 ContextRef context{GetContextRef()};
2700 if UNLIKELY(!context) return;
2702 std::lock_guard<std::mutex> _{context->mPropLock};
2703 std::lock_guard<std::mutex> __{context->mSourceLock};
2704 ALsource *Source{LookupSource(context.get(), source)};
2705 if UNLIKELY(!Source)
2706 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2707 else
2708 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2710 END_API_FUNC
2712 AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
2713 START_API_FUNC
2715 ContextRef context{GetContextRef()};
2716 if UNLIKELY(!context) return;
2718 std::lock_guard<std::mutex> _{context->mPropLock};
2719 std::lock_guard<std::mutex> __{context->mSourceLock};
2720 ALsource *Source{LookupSource(context.get(), source)};
2721 if UNLIKELY(!Source)
2722 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2723 else
2725 const int64_t i64vals[3]{ value1, value2, value3 };
2726 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals);
2729 END_API_FUNC
2731 AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
2732 START_API_FUNC
2734 ContextRef context{GetContextRef()};
2735 if UNLIKELY(!context) return;
2737 std::lock_guard<std::mutex> _{context->mPropLock};
2738 std::lock_guard<std::mutex> __{context->mSourceLock};
2739 ALsource *Source{LookupSource(context.get(), source)};
2740 if UNLIKELY(!Source)
2741 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2742 else if UNLIKELY(!values)
2743 context->setError(AL_INVALID_VALUE, "NULL pointer");
2744 else
2745 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2747 END_API_FUNC
2750 AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
2751 START_API_FUNC
2753 ContextRef context{GetContextRef()};
2754 if UNLIKELY(!context) return;
2756 std::lock_guard<std::mutex> _{context->mSourceLock};
2757 ALsource *Source{LookupSource(context.get(), source)};
2758 if UNLIKELY(!Source)
2759 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2760 else if UNLIKELY(!value)
2761 context->setError(AL_INVALID_VALUE, "NULL pointer");
2762 else
2764 double dval[1];
2765 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dval))
2766 *value = static_cast<float>(dval[0]);
2769 END_API_FUNC
2771 AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
2772 START_API_FUNC
2774 ContextRef context{GetContextRef()};
2775 if UNLIKELY(!context) return;
2777 std::lock_guard<std::mutex> _{context->mSourceLock};
2778 ALsource *Source{LookupSource(context.get(), source)};
2779 if UNLIKELY(!Source)
2780 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2781 else if UNLIKELY(!(value1 && value2 && value3))
2782 context->setError(AL_INVALID_VALUE, "NULL pointer");
2783 else
2785 double dvals[3];
2786 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2788 *value1 = static_cast<float>(dvals[0]);
2789 *value2 = static_cast<float>(dvals[1]);
2790 *value3 = static_cast<float>(dvals[2]);
2794 END_API_FUNC
2796 AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
2797 START_API_FUNC
2799 ContextRef context{GetContextRef()};
2800 if UNLIKELY(!context) return;
2802 std::lock_guard<std::mutex> _{context->mSourceLock};
2803 ALsource *Source{LookupSource(context.get(), source)};
2804 if UNLIKELY(!Source)
2805 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2806 else if UNLIKELY(!values)
2807 context->setError(AL_INVALID_VALUE, "NULL pointer");
2808 else
2810 const ALuint count{FloatValsByProp(param)};
2811 double dvals[MaxValues];
2812 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {dvals, count}))
2814 for(ALuint i{0};i < count;i++)
2815 values[i] = static_cast<float>(dvals[i]);
2819 END_API_FUNC
2822 AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
2823 START_API_FUNC
2825 ContextRef context{GetContextRef()};
2826 if UNLIKELY(!context) return;
2828 std::lock_guard<std::mutex> _{context->mSourceLock};
2829 ALsource *Source{LookupSource(context.get(), source)};
2830 if UNLIKELY(!Source)
2831 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2832 else if UNLIKELY(!value)
2833 context->setError(AL_INVALID_VALUE, "NULL pointer");
2834 else
2835 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2837 END_API_FUNC
2839 AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
2840 START_API_FUNC
2842 ContextRef context{GetContextRef()};
2843 if UNLIKELY(!context) return;
2845 std::lock_guard<std::mutex> _{context->mSourceLock};
2846 ALsource *Source{LookupSource(context.get(), source)};
2847 if UNLIKELY(!Source)
2848 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2849 else if UNLIKELY(!(value1 && value2 && value3))
2850 context->setError(AL_INVALID_VALUE, "NULL pointer");
2851 else
2853 double dvals[3];
2854 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2856 *value1 = dvals[0];
2857 *value2 = dvals[1];
2858 *value3 = dvals[2];
2862 END_API_FUNC
2864 AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
2865 START_API_FUNC
2867 ContextRef context{GetContextRef()};
2868 if UNLIKELY(!context) return;
2870 std::lock_guard<std::mutex> _{context->mSourceLock};
2871 ALsource *Source{LookupSource(context.get(), source)};
2872 if UNLIKELY(!Source)
2873 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2874 else if UNLIKELY(!values)
2875 context->setError(AL_INVALID_VALUE, "NULL pointer");
2876 else
2877 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2879 END_API_FUNC
2882 AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
2883 START_API_FUNC
2885 ContextRef context{GetContextRef()};
2886 if UNLIKELY(!context) return;
2888 std::lock_guard<std::mutex> _{context->mSourceLock};
2889 ALsource *Source{LookupSource(context.get(), source)};
2890 if UNLIKELY(!Source)
2891 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2892 else if UNLIKELY(!value)
2893 context->setError(AL_INVALID_VALUE, "NULL pointer");
2894 else
2895 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2897 END_API_FUNC
2899 AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
2900 START_API_FUNC
2902 ContextRef context{GetContextRef()};
2903 if UNLIKELY(!context) return;
2905 std::lock_guard<std::mutex> _{context->mSourceLock};
2906 ALsource *Source{LookupSource(context.get(), source)};
2907 if UNLIKELY(!Source)
2908 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2909 else if UNLIKELY(!(value1 && value2 && value3))
2910 context->setError(AL_INVALID_VALUE, "NULL pointer");
2911 else
2913 int ivals[3];
2914 if(GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals))
2916 *value1 = ivals[0];
2917 *value2 = ivals[1];
2918 *value3 = ivals[2];
2922 END_API_FUNC
2924 AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
2925 START_API_FUNC
2927 ContextRef context{GetContextRef()};
2928 if UNLIKELY(!context) return;
2930 std::lock_guard<std::mutex> _{context->mSourceLock};
2931 ALsource *Source{LookupSource(context.get(), source)};
2932 if UNLIKELY(!Source)
2933 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2934 else if UNLIKELY(!values)
2935 context->setError(AL_INVALID_VALUE, "NULL pointer");
2936 else
2937 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2939 END_API_FUNC
2942 AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
2943 START_API_FUNC
2945 ContextRef context{GetContextRef()};
2946 if UNLIKELY(!context) return;
2948 std::lock_guard<std::mutex> _{context->mSourceLock};
2949 ALsource *Source{LookupSource(context.get(), source)};
2950 if UNLIKELY(!Source)
2951 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2952 else if UNLIKELY(!value)
2953 context->setError(AL_INVALID_VALUE, "NULL pointer");
2954 else
2955 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2957 END_API_FUNC
2959 AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
2960 START_API_FUNC
2962 ContextRef context{GetContextRef()};
2963 if UNLIKELY(!context) return;
2965 std::lock_guard<std::mutex> _{context->mSourceLock};
2966 ALsource *Source{LookupSource(context.get(), source)};
2967 if UNLIKELY(!Source)
2968 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2969 else if UNLIKELY(!(value1 && value2 && value3))
2970 context->setError(AL_INVALID_VALUE, "NULL pointer");
2971 else
2973 int64_t i64vals[3];
2974 if(GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals))
2976 *value1 = i64vals[0];
2977 *value2 = i64vals[1];
2978 *value3 = i64vals[2];
2982 END_API_FUNC
2984 AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
2985 START_API_FUNC
2987 ContextRef context{GetContextRef()};
2988 if UNLIKELY(!context) return;
2990 std::lock_guard<std::mutex> _{context->mSourceLock};
2991 ALsource *Source{LookupSource(context.get(), source)};
2992 if UNLIKELY(!Source)
2993 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2994 else if UNLIKELY(!values)
2995 context->setError(AL_INVALID_VALUE, "NULL pointer");
2996 else
2997 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2999 END_API_FUNC
3002 AL_API void AL_APIENTRY alSourcePlay(ALuint source)
3003 START_API_FUNC
3004 { alSourcePlayv(1, &source); }
3005 END_API_FUNC
3007 AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
3008 START_API_FUNC
3010 ContextRef context{GetContextRef()};
3011 if UNLIKELY(!context) return;
3013 if UNLIKELY(n < 0)
3014 context->setError(AL_INVALID_VALUE, "Playing %d sources", n);
3015 if UNLIKELY(n <= 0) return;
3017 al::vector<ALsource*> extra_sources;
3018 std::array<ALsource*,8> source_storage;
3019 al::span<ALsource*> srchandles;
3020 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3021 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3022 else
3024 extra_sources.resize(static_cast<ALuint>(n));
3025 srchandles = {extra_sources.data(), extra_sources.size()};
3028 std::lock_guard<std::mutex> _{context->mSourceLock};
3029 for(auto &srchdl : srchandles)
3031 srchdl = LookupSource(context.get(), *sources);
3032 if(!srchdl)
3033 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3034 ++sources;
3037 ALCdevice *device{context->mALDevice.get()};
3038 /* If the device is disconnected, and voices stop on disconnect, go right
3039 * to stopped.
3041 if UNLIKELY(!device->Connected.load(std::memory_order_acquire))
3043 if(context->mStopVoicesOnDisconnect.load(std::memory_order_acquire))
3045 for(ALsource *source : srchandles)
3047 /* TODO: Send state change event? */
3048 source->Offset = 0.0;
3049 source->OffsetType = AL_NONE;
3050 source->state = AL_STOPPED;
3052 return;
3056 /* Count the number of reusable voices. */
3057 auto voicelist = context->getVoicesSpan();
3058 size_t free_voices{0};
3059 for(const Voice *voice : voicelist)
3061 free_voices += (voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
3062 && voice->mSourceID.load(std::memory_order_relaxed) == 0u
3063 && voice->mPendingChange.load(std::memory_order_relaxed) == false);
3064 if(free_voices == srchandles.size())
3065 break;
3067 if UNLIKELY(srchandles.size() != free_voices)
3069 const size_t inc_amount{srchandles.size() - free_voices};
3070 auto &allvoices = *context->mVoices.load(std::memory_order_relaxed);
3071 if(inc_amount > allvoices.size() - voicelist.size())
3073 /* Increase the number of voices to handle the request. */
3074 context->allocVoices(inc_amount - (allvoices.size() - voicelist.size()));
3076 context->mActiveVoiceCount.fetch_add(inc_amount, std::memory_order_release);
3077 voicelist = context->getVoicesSpan();
3080 auto voiceiter = voicelist.begin();
3081 ALuint vidx{0};
3082 VoiceChange *tail{}, *cur{};
3083 for(ALsource *source : srchandles)
3085 /* Check that there is a queue containing at least one valid, non zero
3086 * length buffer.
3088 auto BufferList = source->mQueue.begin();
3089 for(;BufferList != source->mQueue.end();++BufferList)
3091 if(BufferList->mSampleLen != 0 || BufferList->mCallback)
3092 break;
3095 /* If there's nothing to play, go right to stopped. */
3096 if UNLIKELY(BufferList == source->mQueue.end())
3098 /* NOTE: A source without any playable buffers should not have a
3099 * Voice since it shouldn't be in a playing or paused state. So
3100 * there's no need to look up its voice and clear the source.
3102 source->Offset = 0.0;
3103 source->OffsetType = AL_NONE;
3104 source->state = AL_STOPPED;
3105 continue;
3108 if(!cur)
3109 cur = tail = GetVoiceChanger(context.get());
3110 else
3112 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3113 cur = cur->mNext.load(std::memory_order_relaxed);
3116 Voice *voice{GetSourceVoice(source, context.get())};
3117 switch(GetSourceState(source, voice))
3119 case AL_PAUSED:
3120 /* A source that's paused simply resumes. If there's no voice, it
3121 * was lost from a disconnect, so just start over with a new one.
3123 cur->mOldVoice = nullptr;
3124 if(!voice) break;
3125 cur->mVoice = voice;
3126 cur->mSourceID = source->id;
3127 cur->mState = VChangeState::Play;
3128 source->state = AL_PLAYING;
3129 #ifdef ALSOFT_EAX
3130 if(source->eax_is_initialized())
3131 source->eax_commit();
3132 #endif // ALSOFT_EAX
3133 continue;
3135 case AL_PLAYING:
3136 /* A source that's already playing is restarted from the beginning.
3137 * Stop the current voice and start a new one so it properly cross-
3138 * fades back to the beginning.
3140 if(voice)
3141 voice->mPendingChange.store(true, std::memory_order_relaxed);
3142 cur->mOldVoice = voice;
3143 voice = nullptr;
3144 break;
3146 default:
3147 assert(voice == nullptr);
3148 cur->mOldVoice = nullptr;
3149 #ifdef ALSOFT_EAX
3150 if(source->eax_is_initialized())
3151 source->eax_commit();
3152 #endif // ALSOFT_EAX
3153 break;
3156 /* Find the next unused voice to play this source with. */
3157 for(;voiceiter != voicelist.end();++voiceiter,++vidx)
3159 Voice *v{*voiceiter};
3160 if(v->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
3161 && v->mSourceID.load(std::memory_order_relaxed) == 0u
3162 && v->mPendingChange.load(std::memory_order_relaxed) == false)
3164 voice = v;
3165 break;
3168 assert(voice != nullptr);
3170 voice->mPosition.store(0u, std::memory_order_relaxed);
3171 voice->mPositionFrac.store(0, std::memory_order_relaxed);
3172 voice->mCurrentBuffer.store(&source->mQueue.front(), std::memory_order_relaxed);
3173 voice->mFlags.reset();
3174 /* A source that's not playing or paused has any offset applied when it
3175 * starts playing.
3177 if(const ALenum offsettype{source->OffsetType})
3179 const double offset{source->Offset};
3180 source->OffsetType = AL_NONE;
3181 source->Offset = 0.0;
3182 if(auto vpos = GetSampleOffset(source->mQueue, offsettype, offset))
3184 voice->mPosition.store(vpos->pos, std::memory_order_relaxed);
3185 voice->mPositionFrac.store(vpos->frac, std::memory_order_relaxed);
3186 voice->mCurrentBuffer.store(vpos->bufferitem, std::memory_order_relaxed);
3187 if(vpos->pos!=0 || vpos->frac!=0 || vpos->bufferitem!=&source->mQueue.front())
3188 voice->mFlags.set(VoiceIsFading);
3191 InitVoice(voice, source, std::addressof(*BufferList), context.get(), device);
3193 source->VoiceIdx = vidx;
3194 source->state = AL_PLAYING;
3196 cur->mVoice = voice;
3197 cur->mSourceID = source->id;
3198 cur->mState = VChangeState::Play;
3200 if LIKELY(tail)
3201 SendVoiceChanges(context.get(), tail);
3203 END_API_FUNC
3206 AL_API void AL_APIENTRY alSourcePause(ALuint source)
3207 START_API_FUNC
3208 { alSourcePausev(1, &source); }
3209 END_API_FUNC
3211 AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
3212 START_API_FUNC
3214 ContextRef context{GetContextRef()};
3215 if UNLIKELY(!context) return;
3217 if UNLIKELY(n < 0)
3218 context->setError(AL_INVALID_VALUE, "Pausing %d sources", n);
3219 if UNLIKELY(n <= 0) return;
3221 al::vector<ALsource*> extra_sources;
3222 std::array<ALsource*,8> source_storage;
3223 al::span<ALsource*> srchandles;
3224 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3225 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3226 else
3228 extra_sources.resize(static_cast<ALuint>(n));
3229 srchandles = {extra_sources.data(), extra_sources.size()};
3232 std::lock_guard<std::mutex> _{context->mSourceLock};
3233 for(auto &srchdl : srchandles)
3235 srchdl = LookupSource(context.get(), *sources);
3236 if(!srchdl)
3237 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3238 ++sources;
3241 /* Pausing has to be done in two steps. First, for each source that's
3242 * detected to be playing, chamge the voice (asynchronously) to
3243 * stopping/paused.
3245 VoiceChange *tail{}, *cur{};
3246 for(ALsource *source : srchandles)
3248 Voice *voice{GetSourceVoice(source, context.get())};
3249 if(GetSourceState(source, voice) == AL_PLAYING)
3251 if(!cur)
3252 cur = tail = GetVoiceChanger(context.get());
3253 else
3255 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3256 cur = cur->mNext.load(std::memory_order_relaxed);
3258 cur->mVoice = voice;
3259 cur->mSourceID = source->id;
3260 cur->mState = VChangeState::Pause;
3263 if LIKELY(tail)
3265 SendVoiceChanges(context.get(), tail);
3266 /* Second, now that the voice changes have been sent, because it's
3267 * possible that the voice stopped after it was detected playing and
3268 * before the voice got paused, recheck that the source is still
3269 * considered playing and set it to paused if so.
3271 for(ALsource *source : srchandles)
3273 Voice *voice{GetSourceVoice(source, context.get())};
3274 if(GetSourceState(source, voice) == AL_PLAYING)
3275 source->state = AL_PAUSED;
3279 END_API_FUNC
3282 AL_API void AL_APIENTRY alSourceStop(ALuint source)
3283 START_API_FUNC
3284 { alSourceStopv(1, &source); }
3285 END_API_FUNC
3287 AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
3288 START_API_FUNC
3290 ContextRef context{GetContextRef()};
3291 if UNLIKELY(!context) return;
3293 if UNLIKELY(n < 0)
3294 context->setError(AL_INVALID_VALUE, "Stopping %d sources", n);
3295 if UNLIKELY(n <= 0) return;
3297 al::vector<ALsource*> extra_sources;
3298 std::array<ALsource*,8> source_storage;
3299 al::span<ALsource*> srchandles;
3300 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3301 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3302 else
3304 extra_sources.resize(static_cast<ALuint>(n));
3305 srchandles = {extra_sources.data(), extra_sources.size()};
3308 std::lock_guard<std::mutex> _{context->mSourceLock};
3309 for(auto &srchdl : srchandles)
3311 srchdl = LookupSource(context.get(), *sources);
3312 if(!srchdl)
3313 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3314 ++sources;
3317 VoiceChange *tail{}, *cur{};
3318 for(ALsource *source : srchandles)
3320 if(Voice *voice{GetSourceVoice(source, context.get())})
3322 if(!cur)
3323 cur = tail = GetVoiceChanger(context.get());
3324 else
3326 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3327 cur = cur->mNext.load(std::memory_order_relaxed);
3329 voice->mPendingChange.store(true, std::memory_order_relaxed);
3330 cur->mVoice = voice;
3331 cur->mSourceID = source->id;
3332 cur->mState = VChangeState::Stop;
3333 source->state = AL_STOPPED;
3335 source->Offset = 0.0;
3336 source->OffsetType = AL_NONE;
3337 source->VoiceIdx = INVALID_VOICE_IDX;
3339 if LIKELY(tail)
3340 SendVoiceChanges(context.get(), tail);
3342 END_API_FUNC
3345 AL_API void AL_APIENTRY alSourceRewind(ALuint source)
3346 START_API_FUNC
3347 { alSourceRewindv(1, &source); }
3348 END_API_FUNC
3350 AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
3351 START_API_FUNC
3353 ContextRef context{GetContextRef()};
3354 if UNLIKELY(!context) return;
3356 if UNLIKELY(n < 0)
3357 context->setError(AL_INVALID_VALUE, "Rewinding %d sources", n);
3358 if UNLIKELY(n <= 0) return;
3360 al::vector<ALsource*> extra_sources;
3361 std::array<ALsource*,8> source_storage;
3362 al::span<ALsource*> srchandles;
3363 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3364 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3365 else
3367 extra_sources.resize(static_cast<ALuint>(n));
3368 srchandles = {extra_sources.data(), extra_sources.size()};
3371 std::lock_guard<std::mutex> _{context->mSourceLock};
3372 for(auto &srchdl : srchandles)
3374 srchdl = LookupSource(context.get(), *sources);
3375 if(!srchdl)
3376 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3377 ++sources;
3380 VoiceChange *tail{}, *cur{};
3381 for(ALsource *source : srchandles)
3383 Voice *voice{GetSourceVoice(source, context.get())};
3384 if(source->state != AL_INITIAL)
3386 if(!cur)
3387 cur = tail = GetVoiceChanger(context.get());
3388 else
3390 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3391 cur = cur->mNext.load(std::memory_order_relaxed);
3393 if(voice)
3394 voice->mPendingChange.store(true, std::memory_order_relaxed);
3395 cur->mVoice = voice;
3396 cur->mSourceID = source->id;
3397 cur->mState = VChangeState::Reset;
3398 source->state = AL_INITIAL;
3400 source->Offset = 0.0;
3401 source->OffsetType = AL_NONE;
3402 source->VoiceIdx = INVALID_VOICE_IDX;
3404 if LIKELY(tail)
3405 SendVoiceChanges(context.get(), tail);
3407 END_API_FUNC
3410 AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
3411 START_API_FUNC
3413 ContextRef context{GetContextRef()};
3414 if UNLIKELY(!context) return;
3416 if UNLIKELY(nb < 0)
3417 context->setError(AL_INVALID_VALUE, "Queueing %d buffers", nb);
3418 if UNLIKELY(nb <= 0) return;
3420 std::lock_guard<std::mutex> _{context->mSourceLock};
3421 ALsource *source{LookupSource(context.get(),src)};
3422 if UNLIKELY(!source)
3423 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
3425 /* Can't queue on a Static Source */
3426 if UNLIKELY(source->SourceType == AL_STATIC)
3427 SETERR_RETURN(context, AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
3429 /* Check for a valid Buffer, for its frequency and format */
3430 ALCdevice *device{context->mALDevice.get()};
3431 ALbuffer *BufferFmt{nullptr};
3432 for(auto &item : source->mQueue)
3434 BufferFmt = item.mBuffer;
3435 if(BufferFmt) break;
3438 std::unique_lock<std::mutex> buflock{device->BufferLock};
3439 const size_t NewListStart{source->mQueue.size()};
3440 ALbufferQueueItem *BufferList{nullptr};
3441 for(ALsizei i{0};i < nb;i++)
3443 bool fmt_mismatch{false};
3444 ALbuffer *buffer{nullptr};
3445 if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr)
3447 context->setError(AL_INVALID_NAME, "Queueing invalid buffer ID %u", buffers[i]);
3448 goto buffer_error;
3450 if(buffer && buffer->mCallback)
3452 context->setError(AL_INVALID_OPERATION, "Queueing callback buffer %u", buffers[i]);
3453 goto buffer_error;
3456 source->mQueue.emplace_back();
3457 if(!BufferList)
3458 BufferList = &source->mQueue.back();
3459 else
3461 auto &item = source->mQueue.back();
3462 BufferList->mNext.store(&item, std::memory_order_relaxed);
3463 BufferList = &item;
3465 if(!buffer) continue;
3466 BufferList->mSampleLen = buffer->mSampleLen;
3467 BufferList->mLoopEnd = buffer->mSampleLen;
3468 BufferList->mSamples = buffer->mData.data();
3469 BufferList->mBuffer = buffer;
3470 IncrementRef(buffer->ref);
3472 if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
3474 context->setError(AL_INVALID_OPERATION, "Queueing non-persistently mapped buffer %u",
3475 buffer->id);
3476 goto buffer_error;
3479 if(BufferFmt == nullptr)
3480 BufferFmt = buffer;
3481 else
3483 fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate;
3484 fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels;
3485 if(BufferFmt->isBFormat())
3487 fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout;
3488 fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling;
3490 fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder;
3491 fmt_mismatch |= BufferFmt->OriginalType != buffer->OriginalType;
3493 if UNLIKELY(fmt_mismatch)
3495 context->setError(AL_INVALID_OPERATION, "Queueing buffer with mismatched format");
3497 buffer_error:
3498 /* A buffer failed (invalid ID or format), so unlock and release
3499 * each buffer we had.
3501 auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart);
3502 for(;iter != source->mQueue.end();++iter)
3504 if(ALbuffer *buf{iter->mBuffer})
3505 DecrementRef(buf->ref);
3507 source->mQueue.resize(NewListStart);
3508 return;
3511 /* All buffers good. */
3512 buflock.unlock();
3514 /* Source is now streaming */
3515 source->SourceType = AL_STREAMING;
3517 if(NewListStart != 0)
3519 auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart);
3520 (iter-1)->mNext.store(std::addressof(*iter), std::memory_order_release);
3523 END_API_FUNC
3525 AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
3526 START_API_FUNC
3528 ContextRef context{GetContextRef()};
3529 if UNLIKELY(!context) return;
3531 if UNLIKELY(nb < 0)
3532 context->setError(AL_INVALID_VALUE, "Unqueueing %d buffers", nb);
3533 if UNLIKELY(nb <= 0) return;
3535 std::lock_guard<std::mutex> _{context->mSourceLock};
3536 ALsource *source{LookupSource(context.get(),src)};
3537 if UNLIKELY(!source)
3538 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
3540 if UNLIKELY(source->SourceType != AL_STREAMING)
3541 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from a non-streaming source %u",
3542 src);
3543 if UNLIKELY(source->Looping)
3544 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from looping source %u", src);
3546 /* Make sure enough buffers have been processed to unqueue. */
3547 uint processed{0u};
3548 if LIKELY(source->state != AL_INITIAL)
3550 VoiceBufferItem *Current{nullptr};
3551 if(Voice *voice{GetSourceVoice(source, context.get())})
3552 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
3553 for(auto &item : source->mQueue)
3555 if(&item == Current)
3556 break;
3557 ++processed;
3560 if UNLIKELY(processed < static_cast<ALuint>(nb))
3561 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing %d buffer%s (only %u processed)",
3562 nb, (nb==1)?"":"s", processed);
3564 do {
3565 auto &head = source->mQueue.front();
3566 if(ALbuffer *buffer{head.mBuffer})
3568 *(buffers++) = buffer->id;
3569 DecrementRef(buffer->ref);
3571 else
3572 *(buffers++) = 0;
3573 source->mQueue.pop_front();
3574 } while(--nb);
3576 END_API_FUNC
3579 extern "C" AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint, ALsizei, const ALuint*)
3580 START_API_FUNC
3582 ContextRef context{GetContextRef()};
3583 if UNLIKELY(!context) return;
3585 context->setError(AL_INVALID_OPERATION, "alSourceQueueBufferLayersSOFT not supported");
3587 END_API_FUNC
3590 ALsource::ALsource()
3592 Direct.Gain = 1.0f;
3593 Direct.GainHF = 1.0f;
3594 Direct.HFReference = LOWPASSFREQREF;
3595 Direct.GainLF = 1.0f;
3596 Direct.LFReference = HIGHPASSFREQREF;
3597 for(auto &send : Send)
3599 send.Slot = nullptr;
3600 send.Gain = 1.0f;
3601 send.GainHF = 1.0f;
3602 send.HFReference = LOWPASSFREQREF;
3603 send.GainLF = 1.0f;
3604 send.LFReference = HIGHPASSFREQREF;
3608 ALsource::~ALsource()
3610 for(auto &item : mQueue)
3612 if(ALbuffer *buffer{item.mBuffer})
3613 DecrementRef(buffer->ref);
3616 auto clear_send = [](ALsource::SendData &send) -> void
3617 { if(send.Slot) DecrementRef(send.Slot->ref); };
3618 std::for_each(Send.begin(), Send.end(), clear_send);
3621 void UpdateAllSourceProps(ALCcontext *context)
3623 std::lock_guard<std::mutex> _{context->mSourceLock};
3624 #ifdef ALSOFT_EAX
3625 if(context->has_eax())
3627 /* If EAX is enabled, we need to go through and commit all sources' EAX
3628 * changes, along with updating its voice, if any.
3630 for(auto &sublist : context->mSourceList)
3632 uint64_t usemask{~sublist.FreeMask};
3633 while(usemask)
3635 const int idx{al::countr_zero(usemask)};
3636 usemask &= ~(1_u64 << idx);
3638 ALsource *source{sublist.Sources + idx};
3639 source->eax_commit();
3641 if(Voice *voice{GetSourceVoice(source, context)})
3643 if(std::exchange(source->mPropsDirty, false))
3644 UpdateSourceProps(source, voice, context);
3649 else
3650 #endif
3652 auto voicelist = context->getVoicesSpan();
3653 ALuint vidx{0u};
3654 for(Voice *voice : voicelist)
3656 ALuint sid{voice->mSourceID.load(std::memory_order_acquire)};
3657 ALsource *source = sid ? LookupSource(context, sid) : nullptr;
3658 if(source && source->VoiceIdx == vidx)
3660 if(std::exchange(source->mPropsDirty, false))
3661 UpdateSourceProps(source, voice, context);
3663 ++vidx;
3668 SourceSubList::~SourceSubList()
3670 uint64_t usemask{~FreeMask};
3671 while(usemask)
3673 const int idx{al::countr_zero(usemask)};
3674 usemask &= ~(1_u64 << idx);
3675 al::destroy_at(Sources+idx);
3677 FreeMask = ~usemask;
3678 al_free(Sources);
3679 Sources = nullptr;
3683 #ifdef ALSOFT_EAX
3684 class EaxSourceException :
3685 public EaxException
3687 public:
3688 explicit EaxSourceException(
3689 const char* message)
3691 EaxException{"EAX_SOURCE", message}
3694 }; // EaxSourceException
3697 class EaxSourceActiveFxSlotsException :
3698 public EaxException
3700 public:
3701 explicit EaxSourceActiveFxSlotsException(
3702 const char* message)
3704 EaxException{"EAX_SOURCE_ACTIVE_FX_SLOTS", message}
3707 }; // EaxSourceActiveFxSlotsException
3710 class EaxSourceSendException :
3711 public EaxException
3713 public:
3714 explicit EaxSourceSendException(
3715 const char* message)
3717 EaxException{"EAX_SOURCE_SEND", message}
3720 }; // EaxSourceSendException
3723 void EaxUpdateSourceVoice(ALsource *source, ALCcontext *context)
3725 if(Voice *voice{GetSourceVoice(source, context)})
3727 if(std::exchange(source->mPropsDirty, false))
3728 UpdateSourceProps(source, voice, context);
3733 void ALsource::eax_initialize(ALCcontext *context) noexcept
3735 assert(context);
3736 eax_al_context_ = context;
3737 eax_set_defaults();
3738 eax_initialize_fx_slots();
3740 eax_d_ = eax_;
3743 void ALsource::eax_dispatch(
3744 const EaxEaxCall& eax_call)
3746 if (eax_call.is_get())
3748 eax_get(eax_call);
3750 else
3752 eax_set(eax_call);
3756 void ALsource::eax_update_filters()
3758 eax_update_filters_internal();
3761 void ALsource::eax_update(EaxContextSharedDirtyFlags)
3763 /* NOTE: EaxContextSharedDirtyFlags only has one flag (primary_fx_slot_id),
3764 * which must be true for this to be called.
3766 if(eax_uses_primary_id_)
3767 eax_update_primary_fx_slot_id();
3770 void ALsource::eax_commit_and_update()
3772 eax_apply_deferred();
3773 EaxUpdateSourceVoice(this, eax_al_context_);
3776 ALsource* ALsource::eax_lookup_source(
3777 ALCcontext& al_context,
3778 ALuint source_id) noexcept
3780 return LookupSource(&al_context, source_id);
3783 [[noreturn]]
3784 void ALsource::eax_fail(
3785 const char* message)
3787 throw EaxSourceException{message};
3790 void ALsource::eax_set_source_defaults() noexcept
3792 eax1_.fMix = EAX_REVERBMIX_USEDISTANCE;
3794 eax_.source.lDirect = EAXSOURCE_DEFAULTDIRECT;
3795 eax_.source.lDirectHF = EAXSOURCE_DEFAULTDIRECTHF;
3796 eax_.source.lRoom = EAXSOURCE_DEFAULTROOM;
3797 eax_.source.lRoomHF = EAXSOURCE_DEFAULTROOMHF;
3798 eax_.source.lObstruction = EAXSOURCE_DEFAULTOBSTRUCTION;
3799 eax_.source.flObstructionLFRatio = EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO;
3800 eax_.source.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION;
3801 eax_.source.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO;
3802 eax_.source.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO;
3803 eax_.source.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO;
3804 eax_.source.lExclusion = EAXSOURCE_DEFAULTEXCLUSION;
3805 eax_.source.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO;
3806 eax_.source.lOutsideVolumeHF = EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF;
3807 eax_.source.flDopplerFactor = EAXSOURCE_DEFAULTDOPPLERFACTOR;
3808 eax_.source.flRolloffFactor = EAXSOURCE_DEFAULTROLLOFFFACTOR;
3809 eax_.source.flRoomRolloffFactor = EAXSOURCE_DEFAULTROOMROLLOFFFACTOR;
3810 eax_.source.flAirAbsorptionFactor = EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR;
3811 eax_.source.ulFlags = EAXSOURCE_DEFAULTFLAGS;
3814 void ALsource::eax_set_active_fx_slots_defaults() noexcept
3816 eax_.active_fx_slots = EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID;
3819 void ALsource::eax_set_send_defaults(EAXSOURCEALLSENDPROPERTIES& eax_send) noexcept
3821 eax_send.guidReceivingFXSlotID = EAX_NULL_GUID;
3822 eax_send.lSend = EAXSOURCE_DEFAULTSEND;
3823 eax_send.lSendHF = EAXSOURCE_DEFAULTSENDHF;
3824 eax_send.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION;
3825 eax_send.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO;
3826 eax_send.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO;
3827 eax_send.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO;
3828 eax_send.lExclusion = EAXSOURCE_DEFAULTEXCLUSION;
3829 eax_send.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO;
3832 void ALsource::eax_set_sends_defaults() noexcept
3834 for (auto& eax_send : eax_.sends)
3836 eax_set_send_defaults(eax_send);
3840 void ALsource::eax_set_speaker_levels_defaults() noexcept
3842 std::fill(eax_.speaker_levels.begin(), eax_.speaker_levels.end(), EAXSOURCE_DEFAULTSPEAKERLEVEL);
3845 void ALsource::eax_set_defaults() noexcept
3847 eax_set_source_defaults();
3848 eax_set_active_fx_slots_defaults();
3849 eax_set_sends_defaults();
3850 eax_set_speaker_levels_defaults();
3853 float ALsource::eax_calculate_dst_occlusion_mb(
3854 long src_occlusion_mb,
3855 float path_ratio,
3856 float lf_ratio) noexcept
3858 const auto ratio_1 = path_ratio + lf_ratio - 1.0F;
3859 const auto ratio_2 = path_ratio * lf_ratio;
3860 const auto ratio = (ratio_2 > ratio_1) ? ratio_2 : ratio_1;
3861 const auto dst_occlustion_mb = static_cast<float>(src_occlusion_mb) * ratio;
3863 return dst_occlustion_mb;
3866 EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept
3868 auto gain_mb =
3869 static_cast<float>(eax_.source.lDirect) +
3871 (static_cast<float>(eax_.source.lObstruction) * eax_.source.flObstructionLFRatio) +
3873 eax_calculate_dst_occlusion_mb(
3874 eax_.source.lOcclusion,
3875 eax_.source.flOcclusionDirectRatio,
3876 eax_.source.flOcclusionLFRatio);
3878 auto gain_hf_mb =
3879 static_cast<float>(eax_.source.lDirectHF) +
3881 static_cast<float>(eax_.source.lObstruction) +
3883 (static_cast<float>(eax_.source.lOcclusion) * eax_.source.flOcclusionDirectRatio);
3885 for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i)
3887 if (eax_active_fx_slots_[i])
3889 const auto& send = eax_.sends[i];
3891 gain_mb += eax_calculate_dst_occlusion_mb(
3892 send.lOcclusion,
3893 send.flOcclusionDirectRatio,
3894 send.flOcclusionLFRatio);
3896 gain_hf_mb += static_cast<float>(send.lOcclusion) * send.flOcclusionDirectRatio;
3900 const auto al_low_pass_param = EaxAlLowPassParam
3902 level_mb_to_gain(gain_mb),
3903 minf(level_mb_to_gain(gain_hf_mb), 1.0f)
3906 return al_low_pass_param;
3909 EaxAlLowPassParam ALsource::eax_create_room_filter_param(
3910 const ALeffectslot& fx_slot,
3911 const EAXSOURCEALLSENDPROPERTIES& send) const noexcept
3913 const auto& fx_slot_eax = fx_slot.eax_get_eax_fx_slot();
3915 const auto gain_mb =
3916 static_cast<float>(
3917 eax_.source.lRoom +
3918 send.lSend) +
3920 eax_calculate_dst_occlusion_mb(
3921 eax_.source.lOcclusion,
3922 eax_.source.flOcclusionRoomRatio,
3923 eax_.source.flOcclusionLFRatio
3926 eax_calculate_dst_occlusion_mb(
3927 send.lOcclusion,
3928 send.flOcclusionRoomRatio,
3929 send.flOcclusionLFRatio
3932 (static_cast<float>(eax_.source.lExclusion) * eax_.source.flExclusionLFRatio) +
3933 (static_cast<float>(send.lExclusion) * send.flExclusionLFRatio) +
3935 0.0F;
3937 const auto gain_hf_mb =
3938 static_cast<float>(
3939 eax_.source.lRoomHF +
3940 send.lSendHF) +
3942 (static_cast<float>(fx_slot_eax.lOcclusion + eax_.source.lOcclusion) * eax_.source.flOcclusionRoomRatio) +
3943 (static_cast<float>(send.lOcclusion) * send.flOcclusionRoomRatio) +
3945 static_cast<float>(
3946 eax_.source.lExclusion +
3947 send.lExclusion) +
3949 0.0F;
3951 const auto al_low_pass_param = EaxAlLowPassParam
3953 level_mb_to_gain(gain_mb),
3954 minf(level_mb_to_gain(gain_hf_mb), 1.0f)
3957 return al_low_pass_param;
3960 void ALsource::eax_set_fx_slots()
3962 eax_uses_primary_id_ = false;
3963 eax_has_active_fx_slots_ = false;
3965 for (auto i = 0; i < EAX_MAX_FXSLOTS; ++i)
3967 const auto& eax_active_fx_slot_id = eax_.active_fx_slots.guidActiveFXSlots[i];
3969 auto fx_slot_index = EaxFxSlotIndex{};
3971 if (eax_active_fx_slot_id == EAX_PrimaryFXSlotID)
3973 eax_uses_primary_id_ = true;
3974 fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index();
3976 else
3978 fx_slot_index = eax_active_fx_slot_id;
3981 if (fx_slot_index.has_value())
3983 eax_has_active_fx_slots_ = true;
3984 eax_active_fx_slots_[*fx_slot_index] = true;
3988 for (auto i = 0u; i < EAX_MAX_FXSLOTS; ++i)
3990 if (!eax_active_fx_slots_[i])
3992 eax_set_al_source_send(nullptr, i, EaxAlLowPassParam{1.0f, 1.0f});
3997 void ALsource::eax_initialize_fx_slots()
3999 eax_set_fx_slots();
4000 eax_update_filters_internal();
4003 void ALsource::eax_update_direct_filter_internal()
4005 const auto& direct_param = eax_create_direct_filter_param();
4007 Direct.Gain = direct_param.gain;
4008 Direct.GainHF = direct_param.gain_hf;
4009 Direct.HFReference = LOWPASSFREQREF;
4010 Direct.GainLF = 1.0f;
4011 Direct.LFReference = HIGHPASSFREQREF;
4012 mPropsDirty = true;
4015 void ALsource::eax_update_room_filters_internal()
4017 if (!eax_has_active_fx_slots_)
4019 return;
4022 for (auto i = 0u; i < EAX_MAX_FXSLOTS; ++i)
4024 if (eax_active_fx_slots_[i])
4026 auto& fx_slot = eax_al_context_->eax_get_fx_slot(static_cast<std::size_t>(i));
4027 const auto& send = eax_.sends[i];
4028 const auto& room_param = eax_create_room_filter_param(fx_slot, send);
4030 eax_set_al_source_send(&fx_slot, i, room_param);
4035 void ALsource::eax_update_filters_internal()
4037 eax_update_direct_filter_internal();
4038 eax_update_room_filters_internal();
4041 void ALsource::eax_update_primary_fx_slot_id()
4043 const auto& previous_primary_fx_slot_index = eax_al_context_->eax_get_previous_primary_fx_slot_index();
4044 const auto& primary_fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index();
4046 if (previous_primary_fx_slot_index == primary_fx_slot_index)
4048 return;
4051 if (previous_primary_fx_slot_index.has_value())
4053 const auto fx_slot_index = previous_primary_fx_slot_index.value();
4054 eax_active_fx_slots_[fx_slot_index] = false;
4056 eax_set_al_source_send(nullptr, fx_slot_index, EaxAlLowPassParam{1.0f, 1.0f});
4059 if (primary_fx_slot_index.has_value())
4061 const auto fx_slot_index = primary_fx_slot_index.value();
4062 eax_active_fx_slots_[fx_slot_index] = true;
4064 auto& fx_slot = eax_al_context_->eax_get_fx_slot(fx_slot_index);
4065 const auto& send = eax_.sends[fx_slot_index];
4066 const auto& room_param = eax_create_room_filter_param(fx_slot, send);
4068 eax_set_al_source_send(&fx_slot, fx_slot_index, room_param);
4071 eax_has_active_fx_slots_ = std::any_of(
4072 eax_active_fx_slots_.cbegin(),
4073 eax_active_fx_slots_.cend(),
4074 [](const auto& item)
4076 return item;
4081 void ALsource::eax_defer_active_fx_slots(
4082 const EaxEaxCall& eax_call)
4084 const auto active_fx_slots_span =
4085 eax_call.get_values<EaxSourceActiveFxSlotsException, const GUID>();
4087 const auto fx_slot_count = active_fx_slots_span.size();
4089 if (fx_slot_count <= 0 || fx_slot_count > EAX_MAX_FXSLOTS)
4091 throw EaxSourceActiveFxSlotsException{"Count out of range."};
4094 for (auto i = std::size_t{}; i < fx_slot_count; ++i)
4096 const auto& fx_slot_guid = active_fx_slots_span[i];
4098 if (fx_slot_guid != EAX_NULL_GUID &&
4099 fx_slot_guid != EAX_PrimaryFXSlotID &&
4100 fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot0 &&
4101 fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot0 &&
4102 fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot1 &&
4103 fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot1 &&
4104 fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot2 &&
4105 fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot2 &&
4106 fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot3 &&
4107 fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot3)
4109 throw EaxSourceActiveFxSlotsException{"Unsupported GUID."};
4113 for (auto i = std::size_t{}; i < fx_slot_count; ++i)
4115 eax_d_.active_fx_slots.guidActiveFXSlots[i] = active_fx_slots_span[i];
4118 for (auto i = fx_slot_count; i < EAX_MAX_FXSLOTS; ++i)
4120 eax_d_.active_fx_slots.guidActiveFXSlots[i] = EAX_NULL_GUID;
4123 eax_are_active_fx_slots_dirty_ = (eax_d_.active_fx_slots != eax_.active_fx_slots);
4127 const char* ALsource::eax_get_exclusion_name() noexcept
4129 return "Exclusion";
4132 const char* ALsource::eax_get_exclusion_lf_ratio_name() noexcept
4134 return "Exclusion LF Ratio";
4137 const char* ALsource::eax_get_occlusion_name() noexcept
4139 return "Occlusion";
4142 const char* ALsource::eax_get_occlusion_lf_ratio_name() noexcept
4144 return "Occlusion LF Ratio";
4147 const char* ALsource::eax_get_occlusion_direct_ratio_name() noexcept
4149 return "Occlusion Direct Ratio";
4152 const char* ALsource::eax_get_occlusion_room_ratio_name() noexcept
4154 return "Occlusion Room Ratio";
4157 void ALsource::eax1_validate_reverb_mix(float reverb_mix)
4159 if (reverb_mix == EAX_REVERBMIX_USEDISTANCE)
4160 return;
4162 eax_validate_range<EaxSourceSendException>("Reverb Mix", reverb_mix, EAX_BUFFER_MINREVERBMIX, EAX_BUFFER_MAXREVERBMIX);
4165 void ALsource::eax_validate_send_receiving_fx_slot_guid(
4166 const GUID& guidReceivingFXSlotID)
4168 if (guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 &&
4169 guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 &&
4170 guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 &&
4171 guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 &&
4172 guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 &&
4173 guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 &&
4174 guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot3 &&
4175 guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot3)
4177 throw EaxSourceSendException{"Unsupported receiving FX slot GUID."};
4181 void ALsource::eax_validate_send_send(
4182 long lSend)
4184 eax_validate_range<EaxSourceSendException>(
4185 "Send",
4186 lSend,
4187 EAXSOURCE_MINSEND,
4188 EAXSOURCE_MAXSEND);
4191 void ALsource::eax_validate_send_send_hf(
4192 long lSendHF)
4194 eax_validate_range<EaxSourceSendException>(
4195 "Send HF",
4196 lSendHF,
4197 EAXSOURCE_MINSENDHF,
4198 EAXSOURCE_MAXSENDHF);
4201 void ALsource::eax_validate_send_occlusion(
4202 long lOcclusion)
4204 eax_validate_range<EaxSourceSendException>(
4205 eax_get_occlusion_name(),
4206 lOcclusion,
4207 EAXSOURCE_MINOCCLUSION,
4208 EAXSOURCE_MAXOCCLUSION);
4211 void ALsource::eax_validate_send_occlusion_lf_ratio(
4212 float flOcclusionLFRatio)
4214 eax_validate_range<EaxSourceSendException>(
4215 eax_get_occlusion_lf_ratio_name(),
4216 flOcclusionLFRatio,
4217 EAXSOURCE_MINOCCLUSIONLFRATIO,
4218 EAXSOURCE_MAXOCCLUSIONLFRATIO);
4221 void ALsource::eax_validate_send_occlusion_room_ratio(
4222 float flOcclusionRoomRatio)
4224 eax_validate_range<EaxSourceSendException>(
4225 eax_get_occlusion_room_ratio_name(),
4226 flOcclusionRoomRatio,
4227 EAXSOURCE_MINOCCLUSIONROOMRATIO,
4228 EAXSOURCE_MAXOCCLUSIONROOMRATIO);
4231 void ALsource::eax_validate_send_occlusion_direct_ratio(
4232 float flOcclusionDirectRatio)
4234 eax_validate_range<EaxSourceSendException>(
4235 eax_get_occlusion_direct_ratio_name(),
4236 flOcclusionDirectRatio,
4237 EAXSOURCE_MINOCCLUSIONDIRECTRATIO,
4238 EAXSOURCE_MAXOCCLUSIONDIRECTRATIO);
4241 void ALsource::eax_validate_send_exclusion(
4242 long lExclusion)
4244 eax_validate_range<EaxSourceSendException>(
4245 eax_get_exclusion_name(),
4246 lExclusion,
4247 EAXSOURCE_MINEXCLUSION,
4248 EAXSOURCE_MAXEXCLUSION);
4251 void ALsource::eax_validate_send_exclusion_lf_ratio(
4252 float flExclusionLFRatio)
4254 eax_validate_range<EaxSourceSendException>(
4255 eax_get_exclusion_lf_ratio_name(),
4256 flExclusionLFRatio,
4257 EAXSOURCE_MINEXCLUSIONLFRATIO,
4258 EAXSOURCE_MAXEXCLUSIONLFRATIO);
4261 void ALsource::eax_validate_send(
4262 const EAXSOURCESENDPROPERTIES& all)
4264 eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID);
4265 eax_validate_send_send(all.lSend);
4266 eax_validate_send_send_hf(all.lSendHF);
4269 void ALsource::eax_validate_send_exclusion_all(
4270 const EAXSOURCEEXCLUSIONSENDPROPERTIES& all)
4272 eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID);
4273 eax_validate_send_exclusion(all.lExclusion);
4274 eax_validate_send_exclusion_lf_ratio(all.flExclusionLFRatio);
4277 void ALsource::eax_validate_send_occlusion_all(
4278 const EAXSOURCEOCCLUSIONSENDPROPERTIES& all)
4280 eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID);
4281 eax_validate_send_occlusion(all.lOcclusion);
4282 eax_validate_send_occlusion_lf_ratio(all.flOcclusionLFRatio);
4283 eax_validate_send_occlusion_room_ratio(all.flOcclusionRoomRatio);
4284 eax_validate_send_occlusion_direct_ratio(all.flOcclusionDirectRatio);
4287 void ALsource::eax_validate_send_all(
4288 const EAXSOURCEALLSENDPROPERTIES& all)
4290 eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID);
4291 eax_validate_send_send(all.lSend);
4292 eax_validate_send_send_hf(all.lSendHF);
4293 eax_validate_send_occlusion(all.lOcclusion);
4294 eax_validate_send_occlusion_lf_ratio(all.flOcclusionLFRatio);
4295 eax_validate_send_occlusion_room_ratio(all.flOcclusionRoomRatio);
4296 eax_validate_send_occlusion_direct_ratio(all.flOcclusionDirectRatio);
4297 eax_validate_send_exclusion(all.lExclusion);
4298 eax_validate_send_exclusion_lf_ratio(all.flExclusionLFRatio);
4301 EaxFxSlotIndexValue ALsource::eax_get_send_index(
4302 const GUID& send_guid)
4304 if (false)
4307 else if (send_guid == EAXPROPERTYID_EAX40_FXSlot0 || send_guid == EAXPROPERTYID_EAX50_FXSlot0)
4309 return 0;
4311 else if (send_guid == EAXPROPERTYID_EAX40_FXSlot1 || send_guid == EAXPROPERTYID_EAX50_FXSlot1)
4313 return 1;
4315 else if (send_guid == EAXPROPERTYID_EAX40_FXSlot2 || send_guid == EAXPROPERTYID_EAX50_FXSlot2)
4317 return 2;
4319 else if (send_guid == EAXPROPERTYID_EAX40_FXSlot3 || send_guid == EAXPROPERTYID_EAX50_FXSlot3)
4321 return 3;
4323 else
4325 throw EaxSourceSendException{"Unsupported receiving FX slot GUID."};
4329 void ALsource::eax_defer_send_send(
4330 long lSend,
4331 EaxFxSlotIndexValue index)
4333 eax_d_.sends[index].lSend = lSend;
4335 eax_sends_dirty_flags_.sends[index].lSend =
4336 (eax_.sends[index].lSend != eax_d_.sends[index].lSend);
4339 void ALsource::eax_defer_send_send_hf(
4340 long lSendHF,
4341 EaxFxSlotIndexValue index)
4343 eax_d_.sends[index].lSendHF = lSendHF;
4345 eax_sends_dirty_flags_.sends[index].lSendHF =
4346 (eax_.sends[index].lSendHF != eax_d_.sends[index].lSendHF);
4349 void ALsource::eax_defer_send_occlusion(
4350 long lOcclusion,
4351 EaxFxSlotIndexValue index)
4353 eax_d_.sends[index].lOcclusion = lOcclusion;
4355 eax_sends_dirty_flags_.sends[index].lOcclusion =
4356 (eax_.sends[index].lOcclusion != eax_d_.sends[index].lOcclusion);
4359 void ALsource::eax_defer_send_occlusion_lf_ratio(
4360 float flOcclusionLFRatio,
4361 EaxFxSlotIndexValue index)
4363 eax_d_.sends[index].flOcclusionLFRatio = flOcclusionLFRatio;
4365 eax_sends_dirty_flags_.sends[index].flOcclusionLFRatio =
4366 (eax_.sends[index].flOcclusionLFRatio != eax_d_.sends[index].flOcclusionLFRatio);
4369 void ALsource::eax_defer_send_occlusion_room_ratio(
4370 float flOcclusionRoomRatio,
4371 EaxFxSlotIndexValue index)
4373 eax_d_.sends[index].flOcclusionRoomRatio = flOcclusionRoomRatio;
4375 eax_sends_dirty_flags_.sends[index].flOcclusionRoomRatio =
4376 (eax_.sends[index].flOcclusionRoomRatio != eax_d_.sends[index].flOcclusionRoomRatio);
4379 void ALsource::eax_defer_send_occlusion_direct_ratio(
4380 float flOcclusionDirectRatio,
4381 EaxFxSlotIndexValue index)
4383 eax_d_.sends[index].flOcclusionDirectRatio = flOcclusionDirectRatio;
4385 eax_sends_dirty_flags_.sends[index].flOcclusionDirectRatio =
4386 (eax_.sends[index].flOcclusionDirectRatio != eax_d_.sends[index].flOcclusionDirectRatio);
4389 void ALsource::eax_defer_send_exclusion(
4390 long lExclusion,
4391 EaxFxSlotIndexValue index)
4393 eax_d_.sends[index].lExclusion = lExclusion;
4395 eax_sends_dirty_flags_.sends[index].lExclusion =
4396 (eax_.sends[index].lExclusion != eax_d_.sends[index].lExclusion);
4399 void ALsource::eax_defer_send_exclusion_lf_ratio(
4400 float flExclusionLFRatio,
4401 EaxFxSlotIndexValue index)
4403 eax_d_.sends[index].flExclusionLFRatio = flExclusionLFRatio;
4405 eax_sends_dirty_flags_.sends[index].flExclusionLFRatio =
4406 (eax_.sends[index].flExclusionLFRatio != eax_d_.sends[index].flExclusionLFRatio);
4409 void ALsource::eax_defer_send(
4410 const EAXSOURCESENDPROPERTIES& all,
4411 EaxFxSlotIndexValue index)
4413 eax_defer_send_send(all.lSend, index);
4414 eax_defer_send_send_hf(all.lSendHF, index);
4417 void ALsource::eax_defer_send_exclusion_all(
4418 const EAXSOURCEEXCLUSIONSENDPROPERTIES& all,
4419 EaxFxSlotIndexValue index)
4421 eax_defer_send_exclusion(all.lExclusion, index);
4422 eax_defer_send_exclusion_lf_ratio(all.flExclusionLFRatio, index);
4425 void ALsource::eax_defer_send_occlusion_all(
4426 const EAXSOURCEOCCLUSIONSENDPROPERTIES& all,
4427 EaxFxSlotIndexValue index)
4429 eax_defer_send_occlusion(all.lOcclusion, index);
4430 eax_defer_send_occlusion_lf_ratio(all.flOcclusionLFRatio, index);
4431 eax_defer_send_occlusion_room_ratio(all.flOcclusionRoomRatio, index);
4432 eax_defer_send_occlusion_direct_ratio(all.flOcclusionDirectRatio, index);
4435 void ALsource::eax_defer_send_all(
4436 const EAXSOURCEALLSENDPROPERTIES& all,
4437 EaxFxSlotIndexValue index)
4439 eax_defer_send_send(all.lSend, index);
4440 eax_defer_send_send_hf(all.lSendHF, index);
4441 eax_defer_send_occlusion(all.lOcclusion, index);
4442 eax_defer_send_occlusion_lf_ratio(all.flOcclusionLFRatio, index);
4443 eax_defer_send_occlusion_room_ratio(all.flOcclusionRoomRatio, index);
4444 eax_defer_send_occlusion_direct_ratio(all.flOcclusionDirectRatio, index);
4445 eax_defer_send_exclusion(all.lExclusion, index);
4446 eax_defer_send_exclusion_lf_ratio(all.flExclusionLFRatio, index);
4449 void ALsource::eax_defer_send(
4450 const EaxEaxCall& eax_call)
4452 const auto eax_all_span =
4453 eax_call.get_values<EaxSourceException, const EAXSOURCESENDPROPERTIES>();
4455 const auto count = eax_all_span.size();
4457 if (count <= 0 || count > EAX_MAX_FXSLOTS)
4459 throw EaxSourceSendException{"Send count out of range."};
4462 for (auto i = std::size_t{}; i < count; ++i)
4464 const auto& all = eax_all_span[i];
4465 eax_validate_send(all);
4468 for (auto i = std::size_t{}; i < count; ++i)
4470 const auto& all = eax_all_span[i];
4471 const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID);
4472 eax_defer_send(all, send_index);
4476 void ALsource::eax_defer_send_exclusion_all(
4477 const EaxEaxCall& eax_call)
4479 const auto eax_all_span =
4480 eax_call.get_values<EaxSourceException, const EAXSOURCEEXCLUSIONSENDPROPERTIES>();
4482 const auto count = eax_all_span.size();
4484 if (count <= 0 || count > EAX_MAX_FXSLOTS)
4486 throw EaxSourceSendException{"Send exclusion all count out of range."};
4489 for (auto i = std::size_t{}; i < count; ++i)
4491 const auto& all = eax_all_span[i];
4492 eax_validate_send_exclusion_all(all);
4495 for (auto i = std::size_t{}; i < count; ++i)
4497 const auto& all = eax_all_span[i];
4498 const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID);
4499 eax_defer_send_exclusion_all(all, send_index);
4503 void ALsource::eax_defer_send_occlusion_all(
4504 const EaxEaxCall& eax_call)
4506 const auto eax_all_span =
4507 eax_call.get_values<EaxSourceException, const EAXSOURCEOCCLUSIONSENDPROPERTIES>();
4509 const auto count = eax_all_span.size();
4511 if (count <= 0 || count > EAX_MAX_FXSLOTS)
4513 throw EaxSourceSendException{"Send occlusion all count out of range."};
4516 for (auto i = std::size_t{}; i < count; ++i)
4518 const auto& all = eax_all_span[i];
4519 eax_validate_send_occlusion_all(all);
4522 for (auto i = std::size_t{}; i < count; ++i)
4524 const auto& all = eax_all_span[i];
4525 const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID);
4526 eax_defer_send_occlusion_all(all, send_index);
4530 void ALsource::eax_defer_send_all(
4531 const EaxEaxCall& eax_call)
4533 const auto eax_all_span =
4534 eax_call.get_values<EaxSourceException, const EAXSOURCEALLSENDPROPERTIES>();
4536 const auto count = eax_all_span.size();
4538 if (count <= 0 || count > EAX_MAX_FXSLOTS)
4540 throw EaxSourceSendException{"Send all count out of range."};
4543 for (auto i = std::size_t{}; i < count; ++i)
4545 const auto& all = eax_all_span[i];
4546 eax_validate_send_all(all);
4549 for (auto i = std::size_t{}; i < count; ++i)
4551 const auto& all = eax_all_span[i];
4552 const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID);
4553 eax_defer_send_all(all, send_index);
4558 void ALsource::eax_validate_source_direct(
4559 long direct)
4561 eax_validate_range<EaxSourceException>(
4562 "Direct",
4563 direct,
4564 EAXSOURCE_MINDIRECT,
4565 EAXSOURCE_MAXDIRECT);
4568 void ALsource::eax_validate_source_direct_hf(
4569 long direct_hf)
4571 eax_validate_range<EaxSourceException>(
4572 "Direct HF",
4573 direct_hf,
4574 EAXSOURCE_MINDIRECTHF,
4575 EAXSOURCE_MAXDIRECTHF);
4578 void ALsource::eax_validate_source_room(
4579 long room)
4581 eax_validate_range<EaxSourceException>(
4582 "Room",
4583 room,
4584 EAXSOURCE_MINROOM,
4585 EAXSOURCE_MAXROOM);
4588 void ALsource::eax_validate_source_room_hf(
4589 long room_hf)
4591 eax_validate_range<EaxSourceException>(
4592 "Room HF",
4593 room_hf,
4594 EAXSOURCE_MINROOMHF,
4595 EAXSOURCE_MAXROOMHF);
4598 void ALsource::eax_validate_source_obstruction(
4599 long obstruction)
4601 eax_validate_range<EaxSourceException>(
4602 "Obstruction",
4603 obstruction,
4604 EAXSOURCE_MINOBSTRUCTION,
4605 EAXSOURCE_MAXOBSTRUCTION);
4608 void ALsource::eax_validate_source_obstruction_lf_ratio(
4609 float obstruction_lf_ratio)
4611 eax_validate_range<EaxSourceException>(
4612 "Obstruction LF Ratio",
4613 obstruction_lf_ratio,
4614 EAXSOURCE_MINOBSTRUCTIONLFRATIO,
4615 EAXSOURCE_MAXOBSTRUCTIONLFRATIO);
4618 void ALsource::eax_validate_source_occlusion(
4619 long occlusion)
4621 eax_validate_range<EaxSourceException>(
4622 eax_get_occlusion_name(),
4623 occlusion,
4624 EAXSOURCE_MINOCCLUSION,
4625 EAXSOURCE_MAXOCCLUSION);
4628 void ALsource::eax_validate_source_occlusion_lf_ratio(
4629 float occlusion_lf_ratio)
4631 eax_validate_range<EaxSourceException>(
4632 eax_get_occlusion_lf_ratio_name(),
4633 occlusion_lf_ratio,
4634 EAXSOURCE_MINOCCLUSIONLFRATIO,
4635 EAXSOURCE_MAXOCCLUSIONLFRATIO);
4638 void ALsource::eax_validate_source_occlusion_room_ratio(
4639 float occlusion_room_ratio)
4641 eax_validate_range<EaxSourceException>(
4642 eax_get_occlusion_room_ratio_name(),
4643 occlusion_room_ratio,
4644 EAXSOURCE_MINOCCLUSIONROOMRATIO,
4645 EAXSOURCE_MAXOCCLUSIONROOMRATIO);
4648 void ALsource::eax_validate_source_occlusion_direct_ratio(
4649 float occlusion_direct_ratio)
4651 eax_validate_range<EaxSourceException>(
4652 eax_get_occlusion_direct_ratio_name(),
4653 occlusion_direct_ratio,
4654 EAXSOURCE_MINOCCLUSIONDIRECTRATIO,
4655 EAXSOURCE_MAXOCCLUSIONDIRECTRATIO);
4658 void ALsource::eax_validate_source_exclusion(
4659 long exclusion)
4661 eax_validate_range<EaxSourceException>(
4662 eax_get_exclusion_name(),
4663 exclusion,
4664 EAXSOURCE_MINEXCLUSION,
4665 EAXSOURCE_MAXEXCLUSION);
4668 void ALsource::eax_validate_source_exclusion_lf_ratio(
4669 float exclusion_lf_ratio)
4671 eax_validate_range<EaxSourceException>(
4672 eax_get_exclusion_lf_ratio_name(),
4673 exclusion_lf_ratio,
4674 EAXSOURCE_MINEXCLUSIONLFRATIO,
4675 EAXSOURCE_MAXEXCLUSIONLFRATIO);
4678 void ALsource::eax_validate_source_outside_volume_hf(
4679 long outside_volume_hf)
4681 eax_validate_range<EaxSourceException>(
4682 "Outside Volume HF",
4683 outside_volume_hf,
4684 EAXSOURCE_MINOUTSIDEVOLUMEHF,
4685 EAXSOURCE_MAXOUTSIDEVOLUMEHF);
4688 void ALsource::eax_validate_source_doppler_factor(
4689 float doppler_factor)
4691 eax_validate_range<EaxSourceException>(
4692 "Doppler Factor",
4693 doppler_factor,
4694 EAXSOURCE_MINDOPPLERFACTOR,
4695 EAXSOURCE_MAXDOPPLERFACTOR);
4698 void ALsource::eax_validate_source_rolloff_factor(
4699 float rolloff_factor)
4701 eax_validate_range<EaxSourceException>(
4702 "Rolloff Factor",
4703 rolloff_factor,
4704 EAXSOURCE_MINROLLOFFFACTOR,
4705 EAXSOURCE_MAXROLLOFFFACTOR);
4708 void ALsource::eax_validate_source_room_rolloff_factor(
4709 float room_rolloff_factor)
4711 eax_validate_range<EaxSourceException>(
4712 "Room Rolloff Factor",
4713 room_rolloff_factor,
4714 EAXSOURCE_MINROOMROLLOFFFACTOR,
4715 EAXSOURCE_MAXROOMROLLOFFFACTOR);
4718 void ALsource::eax_validate_source_air_absorption_factor(
4719 float air_absorption_factor)
4721 eax_validate_range<EaxSourceException>(
4722 "Air Absorption Factor",
4723 air_absorption_factor,
4724 EAXSOURCE_MINAIRABSORPTIONFACTOR,
4725 EAXSOURCE_MAXAIRABSORPTIONFACTOR);
4728 void ALsource::eax_validate_source_flags(
4729 unsigned long flags,
4730 int eax_version)
4732 eax_validate_range<EaxSourceException>(
4733 "Flags",
4734 flags,
4735 0UL,
4736 ~((eax_version == 5) ? EAX50SOURCEFLAGS_RESERVED : EAX20SOURCEFLAGS_RESERVED));
4739 void ALsource::eax_validate_source_macro_fx_factor(
4740 float macro_fx_factor)
4742 eax_validate_range<EaxSourceException>(
4743 "Macro FX Factor",
4744 macro_fx_factor,
4745 EAXSOURCE_MINMACROFXFACTOR,
4746 EAXSOURCE_MAXMACROFXFACTOR);
4749 void ALsource::eax_validate_source_2d_all(
4750 const EAXSOURCE2DPROPERTIES& all,
4751 int eax_version)
4753 eax_validate_source_direct(all.lDirect);
4754 eax_validate_source_direct_hf(all.lDirectHF);
4755 eax_validate_source_room(all.lRoom);
4756 eax_validate_source_room_hf(all.lRoomHF);
4757 eax_validate_source_flags(all.ulFlags, eax_version);
4760 void ALsource::eax_validate_source_obstruction_all(
4761 const EAXOBSTRUCTIONPROPERTIES& all)
4763 eax_validate_source_obstruction(all.lObstruction);
4764 eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio);
4767 void ALsource::eax_validate_source_exclusion_all(
4768 const EAXEXCLUSIONPROPERTIES& all)
4770 eax_validate_source_exclusion(all.lExclusion);
4771 eax_validate_source_exclusion_lf_ratio(all.flExclusionLFRatio);
4774 void ALsource::eax_validate_source_occlusion_all(
4775 const EAXOCCLUSIONPROPERTIES& all)
4777 eax_validate_source_occlusion(all.lOcclusion);
4778 eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
4779 eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
4780 eax_validate_source_occlusion_direct_ratio(all.flOcclusionDirectRatio);
4783 void ALsource::eax_validate_source_all(
4784 const EAX20BUFFERPROPERTIES& all,
4785 int eax_version)
4787 eax_validate_source_direct(all.lDirect);
4788 eax_validate_source_direct_hf(all.lDirectHF);
4789 eax_validate_source_room(all.lRoom);
4790 eax_validate_source_room_hf(all.lRoomHF);
4791 eax_validate_source_obstruction(all.lObstruction);
4792 eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio);
4793 eax_validate_source_occlusion(all.lOcclusion);
4794 eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
4795 eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
4796 eax_validate_source_outside_volume_hf(all.lOutsideVolumeHF);
4797 eax_validate_source_room_rolloff_factor(all.flRoomRolloffFactor);
4798 eax_validate_source_air_absorption_factor(all.flAirAbsorptionFactor);
4799 eax_validate_source_flags(all.dwFlags, eax_version);
4802 void ALsource::eax_validate_source_all(
4803 const EAX30SOURCEPROPERTIES& all,
4804 int eax_version)
4806 eax_validate_source_direct(all.lDirect);
4807 eax_validate_source_direct_hf(all.lDirectHF);
4808 eax_validate_source_room(all.lRoom);
4809 eax_validate_source_room_hf(all.lRoomHF);
4810 eax_validate_source_obstruction(all.lObstruction);
4811 eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio);
4812 eax_validate_source_occlusion(all.lOcclusion);
4813 eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
4814 eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
4815 eax_validate_source_occlusion_direct_ratio(all.flOcclusionDirectRatio);
4816 eax_validate_source_exclusion(all.lExclusion);
4817 eax_validate_source_exclusion_lf_ratio(all.flExclusionLFRatio);
4818 eax_validate_source_outside_volume_hf(all.lOutsideVolumeHF);
4819 eax_validate_source_doppler_factor(all.flDopplerFactor);
4820 eax_validate_source_rolloff_factor(all.flRolloffFactor);
4821 eax_validate_source_room_rolloff_factor(all.flRoomRolloffFactor);
4822 eax_validate_source_air_absorption_factor(all.flAirAbsorptionFactor);
4823 eax_validate_source_flags(all.ulFlags, eax_version);
4826 void ALsource::eax_validate_source_all(
4827 const EAX50SOURCEPROPERTIES& all,
4828 int eax_version)
4830 eax_validate_source_all(static_cast<EAX30SOURCEPROPERTIES>(all), eax_version);
4831 eax_validate_source_macro_fx_factor(all.flMacroFXFactor);
4834 void ALsource::eax_validate_source_speaker_id(
4835 long speaker_id)
4837 eax_validate_range<EaxSourceException>(
4838 "Speaker Id",
4839 speaker_id,
4840 static_cast<long>(EAXSPEAKER_FRONT_LEFT),
4841 static_cast<long>(EAXSPEAKER_LOW_FREQUENCY));
4844 void ALsource::eax_validate_source_speaker_level(
4845 long speaker_level)
4847 eax_validate_range<EaxSourceException>(
4848 "Speaker Level",
4849 speaker_level,
4850 EAXSOURCE_MINSPEAKERLEVEL,
4851 EAXSOURCE_MAXSPEAKERLEVEL);
4854 void ALsource::eax_validate_source_speaker_level_all(
4855 const EAXSPEAKERLEVELPROPERTIES& all)
4857 eax_validate_source_speaker_id(all.lSpeakerID);
4858 eax_validate_source_speaker_level(all.lLevel);
4861 void ALsource::eax_defer_source_direct(
4862 long lDirect)
4864 eax_d_.source.lDirect = lDirect;
4865 eax_source_dirty_filter_flags_.lDirect = (eax_.source.lDirect != eax_d_.source.lDirect);
4868 void ALsource::eax_defer_source_direct_hf(
4869 long lDirectHF)
4871 eax_d_.source.lDirectHF = lDirectHF;
4872 eax_source_dirty_filter_flags_.lDirectHF = (eax_.source.lDirectHF != eax_d_.source.lDirectHF);
4875 void ALsource::eax_defer_source_room(
4876 long lRoom)
4878 eax_d_.source.lRoom = lRoom;
4879 eax_source_dirty_filter_flags_.lRoom = (eax_.source.lRoom != eax_d_.source.lRoom);
4882 void ALsource::eax_defer_source_room_hf(
4883 long lRoomHF)
4885 eax_d_.source.lRoomHF = lRoomHF;
4886 eax_source_dirty_filter_flags_.lRoomHF = (eax_.source.lRoomHF != eax_d_.source.lRoomHF);
4889 void ALsource::eax_defer_source_obstruction(
4890 long lObstruction)
4892 eax_d_.source.lObstruction = lObstruction;
4893 eax_source_dirty_filter_flags_.lObstruction = (eax_.source.lObstruction != eax_d_.source.lObstruction);
4896 void ALsource::eax_defer_source_obstruction_lf_ratio(
4897 float flObstructionLFRatio)
4899 eax_d_.source.flObstructionLFRatio = flObstructionLFRatio;
4900 eax_source_dirty_filter_flags_.flObstructionLFRatio = (eax_.source.flObstructionLFRatio != eax_d_.source.flObstructionLFRatio);
4903 void ALsource::eax_defer_source_occlusion(
4904 long lOcclusion)
4906 eax_d_.source.lOcclusion = lOcclusion;
4907 eax_source_dirty_filter_flags_.lOcclusion = (eax_.source.lOcclusion != eax_d_.source.lOcclusion);
4910 void ALsource::eax_defer_source_occlusion_lf_ratio(
4911 float flOcclusionLFRatio)
4913 eax_d_.source.flOcclusionLFRatio = flOcclusionLFRatio;
4914 eax_source_dirty_filter_flags_.flOcclusionLFRatio = (eax_.source.flOcclusionLFRatio != eax_d_.source.flOcclusionLFRatio);
4917 void ALsource::eax_defer_source_occlusion_room_ratio(
4918 float flOcclusionRoomRatio)
4920 eax_d_.source.flOcclusionRoomRatio = flOcclusionRoomRatio;
4921 eax_source_dirty_filter_flags_.flOcclusionRoomRatio = (eax_.source.flOcclusionRoomRatio != eax_d_.source.flOcclusionRoomRatio);
4924 void ALsource::eax_defer_source_occlusion_direct_ratio(
4925 float flOcclusionDirectRatio)
4927 eax_d_.source.flOcclusionDirectRatio = flOcclusionDirectRatio;
4928 eax_source_dirty_filter_flags_.flOcclusionDirectRatio = (eax_.source.flOcclusionDirectRatio != eax_d_.source.flOcclusionDirectRatio);
4931 void ALsource::eax_defer_source_exclusion(
4932 long lExclusion)
4934 eax_d_.source.lExclusion = lExclusion;
4935 eax_source_dirty_filter_flags_.lExclusion = (eax_.source.lExclusion != eax_d_.source.lExclusion);
4938 void ALsource::eax_defer_source_exclusion_lf_ratio(
4939 float flExclusionLFRatio)
4941 eax_d_.source.flExclusionLFRatio = flExclusionLFRatio;
4942 eax_source_dirty_filter_flags_.flExclusionLFRatio = (eax_.source.flExclusionLFRatio != eax_d_.source.flExclusionLFRatio);
4945 void ALsource::eax_defer_source_outside_volume_hf(
4946 long lOutsideVolumeHF)
4948 eax_d_.source.lOutsideVolumeHF = lOutsideVolumeHF;
4949 eax_source_dirty_misc_flags_.lOutsideVolumeHF = (eax_.source.lOutsideVolumeHF != eax_d_.source.lOutsideVolumeHF);
4952 void ALsource::eax_defer_source_doppler_factor(
4953 float flDopplerFactor)
4955 eax_d_.source.flDopplerFactor = flDopplerFactor;
4956 eax_source_dirty_misc_flags_.flDopplerFactor = (eax_.source.flDopplerFactor != eax_d_.source.flDopplerFactor);
4959 void ALsource::eax_defer_source_rolloff_factor(
4960 float flRolloffFactor)
4962 eax_d_.source.flRolloffFactor = flRolloffFactor;
4963 eax_source_dirty_misc_flags_.flRolloffFactor = (eax_.source.flRolloffFactor != eax_d_.source.flRolloffFactor);
4966 void ALsource::eax_defer_source_room_rolloff_factor(
4967 float flRoomRolloffFactor)
4969 eax_d_.source.flRoomRolloffFactor = flRoomRolloffFactor;
4970 eax_source_dirty_misc_flags_.flRoomRolloffFactor = (eax_.source.flRoomRolloffFactor != eax_d_.source.flRoomRolloffFactor);
4973 void ALsource::eax_defer_source_air_absorption_factor(
4974 float flAirAbsorptionFactor)
4976 eax_d_.source.flAirAbsorptionFactor = flAirAbsorptionFactor;
4977 eax_source_dirty_misc_flags_.flAirAbsorptionFactor = (eax_.source.flAirAbsorptionFactor != eax_d_.source.flAirAbsorptionFactor);
4980 void ALsource::eax_defer_source_flags(
4981 unsigned long ulFlags)
4983 eax_d_.source.ulFlags = ulFlags;
4984 eax_source_dirty_misc_flags_.ulFlags = (eax_.source.ulFlags != eax_d_.source.ulFlags);
4987 void ALsource::eax_defer_source_macro_fx_factor(
4988 float flMacroFXFactor)
4990 eax_d_.source.flMacroFXFactor = flMacroFXFactor;
4991 eax_source_dirty_misc_flags_.flMacroFXFactor = (eax_.source.flMacroFXFactor != eax_d_.source.flMacroFXFactor);
4994 void ALsource::eax_defer_source_2d_all(
4995 const EAXSOURCE2DPROPERTIES& all)
4997 eax_defer_source_direct(all.lDirect);
4998 eax_defer_source_direct_hf(all.lDirectHF);
4999 eax_defer_source_room(all.lRoom);
5000 eax_defer_source_room_hf(all.lRoomHF);
5001 eax_defer_source_flags(all.ulFlags);
5004 void ALsource::eax_defer_source_obstruction_all(
5005 const EAXOBSTRUCTIONPROPERTIES& all)
5007 eax_defer_source_obstruction(all.lObstruction);
5008 eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio);
5011 void ALsource::eax_defer_source_exclusion_all(
5012 const EAXEXCLUSIONPROPERTIES& all)
5014 eax_defer_source_exclusion(all.lExclusion);
5015 eax_defer_source_exclusion_lf_ratio(all.flExclusionLFRatio);
5018 void ALsource::eax_defer_source_occlusion_all(
5019 const EAXOCCLUSIONPROPERTIES& all)
5021 eax_defer_source_occlusion(all.lOcclusion);
5022 eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
5023 eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
5024 eax_defer_source_occlusion_direct_ratio(all.flOcclusionDirectRatio);
5027 void ALsource::eax_defer_source_all(
5028 const EAX20BUFFERPROPERTIES& all)
5030 eax_defer_source_direct(all.lDirect);
5031 eax_defer_source_direct_hf(all.lDirectHF);
5032 eax_defer_source_room(all.lRoom);
5033 eax_defer_source_room_hf(all.lRoomHF);
5034 eax_defer_source_obstruction(all.lObstruction);
5035 eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio);
5036 eax_defer_source_occlusion(all.lOcclusion);
5037 eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
5038 eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
5039 eax_defer_source_outside_volume_hf(all.lOutsideVolumeHF);
5040 eax_defer_source_room_rolloff_factor(all.flRoomRolloffFactor);
5041 eax_defer_source_air_absorption_factor(all.flAirAbsorptionFactor);
5042 eax_defer_source_flags(all.dwFlags);
5045 void ALsource::eax_defer_source_all(
5046 const EAX30SOURCEPROPERTIES& all)
5048 eax_defer_source_direct(all.lDirect);
5049 eax_defer_source_direct_hf(all.lDirectHF);
5050 eax_defer_source_room(all.lRoom);
5051 eax_defer_source_room_hf(all.lRoomHF);
5052 eax_defer_source_obstruction(all.lObstruction);
5053 eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio);
5054 eax_defer_source_occlusion(all.lOcclusion);
5055 eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
5056 eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
5057 eax_defer_source_occlusion_direct_ratio(all.flOcclusionDirectRatio);
5058 eax_defer_source_exclusion(all.lExclusion);
5059 eax_defer_source_exclusion_lf_ratio(all.flExclusionLFRatio);
5060 eax_defer_source_outside_volume_hf(all.lOutsideVolumeHF);
5061 eax_defer_source_doppler_factor(all.flDopplerFactor);
5062 eax_defer_source_rolloff_factor(all.flRolloffFactor);
5063 eax_defer_source_room_rolloff_factor(all.flRoomRolloffFactor);
5064 eax_defer_source_air_absorption_factor(all.flAirAbsorptionFactor);
5065 eax_defer_source_flags(all.ulFlags);
5068 void ALsource::eax_defer_source_all(
5069 const EAX50SOURCEPROPERTIES& all)
5071 eax_defer_source_all(static_cast<const EAX30SOURCEPROPERTIES&>(all));
5072 eax_defer_source_macro_fx_factor(all.flMacroFXFactor);
5075 void ALsource::eax_defer_source_speaker_level_all(
5076 const EAXSPEAKERLEVELPROPERTIES& all)
5078 const auto speaker_index = static_cast<std::size_t>(all.lSpeakerID - 1);
5079 auto& speaker_level_d = eax_d_.speaker_levels[speaker_index];
5080 const auto& speaker_level = eax_.speaker_levels[speaker_index];
5082 if (speaker_level != speaker_level_d)
5084 eax_source_dirty_misc_flags_.speaker_levels = true;
5088 void ALsource::eax1_set_efx()
5090 const auto primary_fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index();
5092 if (!primary_fx_slot_index.has_value())
5093 return;
5095 WetGainAuto = (eax1_.fMix == EAX_REVERBMIX_USEDISTANCE);
5096 const auto filter_gain = (WetGainAuto ? 1.0F : eax1_.fMix);
5097 auto& fx_slot = eax_al_context_->eax_get_fx_slot(*primary_fx_slot_index);
5098 eax_set_al_source_send(&fx_slot, *primary_fx_slot_index, EaxAlLowPassParam{filter_gain, 1.0F});
5099 mPropsDirty = true;
5102 void ALsource::eax1_set_reverb_mix(const EaxEaxCall& eax_call)
5104 const auto reverb_mix = eax_call.get_value<EaxSourceException, const decltype(EAXBUFFER_REVERBPROPERTIES::fMix)>();
5105 eax1_validate_reverb_mix(reverb_mix);
5107 if (eax1_.fMix == reverb_mix)
5108 return;
5110 eax1_.fMix = reverb_mix;
5111 eax1_set_efx();
5114 void ALsource::eax_defer_source_direct(
5115 const EaxEaxCall& eax_call)
5117 const auto direct =
5118 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lDirect)>();
5120 eax_validate_source_direct(direct);
5121 eax_defer_source_direct(direct);
5124 void ALsource::eax_defer_source_direct_hf(
5125 const EaxEaxCall& eax_call)
5127 const auto direct_hf =
5128 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lDirectHF)>();
5130 eax_validate_source_direct_hf(direct_hf);
5131 eax_defer_source_direct_hf(direct_hf);
5134 void ALsource::eax_defer_source_room(
5135 const EaxEaxCall& eax_call)
5137 const auto room =
5138 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lRoom)>();
5140 eax_validate_source_room(room);
5141 eax_defer_source_room(room);
5144 void ALsource::eax_defer_source_room_hf(
5145 const EaxEaxCall& eax_call)
5147 const auto room_hf =
5148 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lRoomHF)>();
5150 eax_validate_source_room_hf(room_hf);
5151 eax_defer_source_room_hf(room_hf);
5154 void ALsource::eax_defer_source_obstruction(
5155 const EaxEaxCall& eax_call)
5157 const auto obstruction =
5158 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lObstruction)>();
5160 eax_validate_source_obstruction(obstruction);
5161 eax_defer_source_obstruction(obstruction);
5164 void ALsource::eax_defer_source_obstruction_lf_ratio(
5165 const EaxEaxCall& eax_call)
5167 const auto obstruction_lf_ratio =
5168 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flObstructionLFRatio)>();
5170 eax_validate_source_obstruction_lf_ratio(obstruction_lf_ratio);
5171 eax_defer_source_obstruction_lf_ratio(obstruction_lf_ratio);
5174 void ALsource::eax_defer_source_occlusion(
5175 const EaxEaxCall& eax_call)
5177 const auto occlusion =
5178 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lOcclusion)>();
5180 eax_validate_source_occlusion(occlusion);
5181 eax_defer_source_occlusion(occlusion);
5184 void ALsource::eax_defer_source_occlusion_lf_ratio(
5185 const EaxEaxCall& eax_call)
5187 const auto occlusion_lf_ratio =
5188 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flOcclusionLFRatio)>();
5190 eax_validate_source_occlusion_lf_ratio(occlusion_lf_ratio);
5191 eax_defer_source_occlusion_lf_ratio(occlusion_lf_ratio);
5194 void ALsource::eax_defer_source_occlusion_room_ratio(
5195 const EaxEaxCall& eax_call)
5197 const auto occlusion_room_ratio =
5198 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flOcclusionRoomRatio)>();
5200 eax_validate_source_occlusion_room_ratio(occlusion_room_ratio);
5201 eax_defer_source_occlusion_room_ratio(occlusion_room_ratio);
5204 void ALsource::eax_defer_source_occlusion_direct_ratio(
5205 const EaxEaxCall& eax_call)
5207 const auto occlusion_direct_ratio =
5208 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flOcclusionDirectRatio)>();
5210 eax_validate_source_occlusion_direct_ratio(occlusion_direct_ratio);
5211 eax_defer_source_occlusion_direct_ratio(occlusion_direct_ratio);
5214 void ALsource::eax_defer_source_exclusion(
5215 const EaxEaxCall& eax_call)
5217 const auto exclusion =
5218 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lExclusion)>();
5220 eax_validate_source_exclusion(exclusion);
5221 eax_defer_source_exclusion(exclusion);
5224 void ALsource::eax_defer_source_exclusion_lf_ratio(
5225 const EaxEaxCall& eax_call)
5227 const auto exclusion_lf_ratio =
5228 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flExclusionLFRatio)>();
5230 eax_validate_source_exclusion_lf_ratio(exclusion_lf_ratio);
5231 eax_defer_source_exclusion_lf_ratio(exclusion_lf_ratio);
5234 void ALsource::eax_defer_source_outside_volume_hf(
5235 const EaxEaxCall& eax_call)
5237 const auto outside_volume_hf =
5238 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lOutsideVolumeHF)>();
5240 eax_validate_source_outside_volume_hf(outside_volume_hf);
5241 eax_defer_source_outside_volume_hf(outside_volume_hf);
5244 void ALsource::eax_defer_source_doppler_factor(
5245 const EaxEaxCall& eax_call)
5247 const auto doppler_factor =
5248 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flDopplerFactor)>();
5250 eax_validate_source_doppler_factor(doppler_factor);
5251 eax_defer_source_doppler_factor(doppler_factor);
5254 void ALsource::eax_defer_source_rolloff_factor(
5255 const EaxEaxCall& eax_call)
5257 const auto rolloff_factor =
5258 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flRolloffFactor)>();
5260 eax_validate_source_rolloff_factor(rolloff_factor);
5261 eax_defer_source_rolloff_factor(rolloff_factor);
5264 void ALsource::eax_defer_source_room_rolloff_factor(
5265 const EaxEaxCall& eax_call)
5267 const auto room_rolloff_factor =
5268 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flRoomRolloffFactor)>();
5270 eax_validate_source_room_rolloff_factor(room_rolloff_factor);
5271 eax_defer_source_room_rolloff_factor(room_rolloff_factor);
5274 void ALsource::eax_defer_source_air_absorption_factor(
5275 const EaxEaxCall& eax_call)
5277 const auto air_absorption_factor =
5278 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flAirAbsorptionFactor)>();
5280 eax_validate_source_air_absorption_factor(air_absorption_factor);
5281 eax_defer_source_air_absorption_factor(air_absorption_factor);
5284 void ALsource::eax_defer_source_flags(
5285 const EaxEaxCall& eax_call)
5287 const auto flags =
5288 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::ulFlags)>();
5290 eax_validate_source_flags(flags, eax_call.get_version());
5291 eax_defer_source_flags(flags);
5294 void ALsource::eax_defer_source_macro_fx_factor(
5295 const EaxEaxCall& eax_call)
5297 const auto macro_fx_factor =
5298 eax_call.get_value<EaxSourceException, const decltype(EAX50SOURCEPROPERTIES::flMacroFXFactor)>();
5300 eax_validate_source_macro_fx_factor(macro_fx_factor);
5301 eax_defer_source_macro_fx_factor(macro_fx_factor);
5304 void ALsource::eax_defer_source_2d_all(
5305 const EaxEaxCall& eax_call)
5307 const auto all = eax_call.get_value<EaxSourceException, const EAXSOURCE2DPROPERTIES>();
5309 eax_validate_source_2d_all(all, eax_call.get_version());
5310 eax_defer_source_2d_all(all);
5313 void ALsource::eax_defer_source_obstruction_all(
5314 const EaxEaxCall& eax_call)
5316 const auto all = eax_call.get_value<EaxSourceException, const EAXOBSTRUCTIONPROPERTIES>();
5318 eax_validate_source_obstruction_all(all);
5319 eax_defer_source_obstruction_all(all);
5322 void ALsource::eax_defer_source_exclusion_all(
5323 const EaxEaxCall& eax_call)
5325 const auto all = eax_call.get_value<EaxSourceException, const EAXEXCLUSIONPROPERTIES>();
5327 eax_validate_source_exclusion_all(all);
5328 eax_defer_source_exclusion_all(all);
5331 void ALsource::eax_defer_source_occlusion_all(
5332 const EaxEaxCall& eax_call)
5334 const auto all = eax_call.get_value<EaxSourceException, const EAXOCCLUSIONPROPERTIES>();
5336 eax_validate_source_occlusion_all(all);
5337 eax_defer_source_occlusion_all(all);
5340 void ALsource::eax_defer_source_all(
5341 const EaxEaxCall& eax_call)
5343 const auto eax_version = eax_call.get_version();
5345 if (eax_version == 2)
5347 const auto all = eax_call.get_value<EaxSourceException, const EAX20BUFFERPROPERTIES>();
5349 eax_validate_source_all(all, eax_version);
5350 eax_defer_source_all(all);
5352 else if (eax_version < 5)
5354 const auto all = eax_call.get_value<EaxSourceException, const EAX30SOURCEPROPERTIES>();
5356 eax_validate_source_all(all, eax_version);
5357 eax_defer_source_all(all);
5359 else
5361 const auto all = eax_call.get_value<EaxSourceException, const EAX50SOURCEPROPERTIES>();
5363 eax_validate_source_all(all, eax_version);
5364 eax_defer_source_all(all);
5368 void ALsource::eax_defer_source_speaker_level_all(
5369 const EaxEaxCall& eax_call)
5371 const auto speaker_level_properties = eax_call.get_value<EaxSourceException, const EAXSPEAKERLEVELPROPERTIES>();
5373 eax_validate_source_speaker_level_all(speaker_level_properties);
5374 eax_defer_source_speaker_level_all(speaker_level_properties);
5377 void ALsource::eax_set_outside_volume_hf()
5379 const auto efx_gain_hf = clamp(
5380 level_mb_to_gain(static_cast<float>(eax_.source.lOutsideVolumeHF)),
5381 AL_MIN_CONE_OUTER_GAINHF,
5382 AL_MAX_CONE_OUTER_GAINHF
5385 OuterGainHF = efx_gain_hf;
5388 void ALsource::eax_set_doppler_factor()
5390 DopplerFactor = eax_.source.flDopplerFactor;
5393 void ALsource::eax_set_rolloff_factor()
5395 RolloffFactor = eax_.source.flRolloffFactor;
5398 void ALsource::eax_set_room_rolloff_factor()
5400 RoomRolloffFactor = eax_.source.flRoomRolloffFactor;
5403 void ALsource::eax_set_air_absorption_factor()
5405 AirAbsorptionFactor = eax_.source.flAirAbsorptionFactor;
5408 void ALsource::eax_set_direct_hf_auto_flag()
5410 const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_DIRECTHFAUTO) != 0;
5412 DryGainHFAuto = is_enable;
5415 void ALsource::eax_set_room_auto_flag()
5417 const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMAUTO) != 0;
5419 WetGainAuto = is_enable;
5422 void ALsource::eax_set_room_hf_auto_flag()
5424 const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMHFAUTO) != 0;
5426 WetGainHFAuto = is_enable;
5429 void ALsource::eax_set_flags()
5431 eax_set_direct_hf_auto_flag();
5432 eax_set_room_auto_flag();
5433 eax_set_room_hf_auto_flag();
5434 eax_set_speaker_levels();
5437 void ALsource::eax_set_macro_fx_factor()
5439 // TODO
5442 void ALsource::eax_set_speaker_levels()
5444 // TODO
5447 void ALsource::eax1_set(const EaxEaxCall& eax_call)
5449 switch (eax_call.get_property_id())
5451 case DSPROPERTY_EAXBUFFER_ALL:
5452 case DSPROPERTY_EAXBUFFER_REVERBMIX:
5453 eax1_set_reverb_mix(eax_call);
5454 break;
5456 default:
5457 eax_fail("Unsupported property id.");
5461 void ALsource::eax_apply_deferred()
5463 if (!eax_are_active_fx_slots_dirty_ &&
5464 eax_sends_dirty_flags_ == EaxSourceSendsDirtyFlags{} &&
5465 eax_source_dirty_filter_flags_ == EaxSourceSourceFilterDirtyFlags{} &&
5466 eax_source_dirty_misc_flags_ == EaxSourceSourceMiscDirtyFlags{})
5468 return;
5471 eax_ = eax_d_;
5473 if (eax_are_active_fx_slots_dirty_)
5475 eax_are_active_fx_slots_dirty_ = false;
5476 eax_set_fx_slots();
5477 eax_update_filters_internal();
5479 else if (eax_has_active_fx_slots_)
5481 if (eax_source_dirty_filter_flags_ != EaxSourceSourceFilterDirtyFlags{})
5483 eax_update_filters_internal();
5485 else if (eax_sends_dirty_flags_ != EaxSourceSendsDirtyFlags{})
5487 for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i)
5489 if (eax_active_fx_slots_[i])
5491 if (eax_sends_dirty_flags_.sends[i] != EaxSourceSendDirtyFlags{})
5493 eax_update_filters_internal();
5494 break;
5501 if (eax_source_dirty_misc_flags_ != EaxSourceSourceMiscDirtyFlags{})
5503 if (eax_source_dirty_misc_flags_.lOutsideVolumeHF)
5505 eax_set_outside_volume_hf();
5508 if (eax_source_dirty_misc_flags_.flDopplerFactor)
5510 eax_set_doppler_factor();
5513 if (eax_source_dirty_misc_flags_.flRolloffFactor)
5515 eax_set_rolloff_factor();
5518 if (eax_source_dirty_misc_flags_.flRoomRolloffFactor)
5520 eax_set_room_rolloff_factor();
5523 if (eax_source_dirty_misc_flags_.flAirAbsorptionFactor)
5525 eax_set_air_absorption_factor();
5528 if (eax_source_dirty_misc_flags_.ulFlags)
5530 eax_set_flags();
5533 if (eax_source_dirty_misc_flags_.flMacroFXFactor)
5535 eax_set_macro_fx_factor();
5538 mPropsDirty = true;
5540 eax_source_dirty_misc_flags_ = EaxSourceSourceMiscDirtyFlags{};
5543 eax_sends_dirty_flags_ = EaxSourceSendsDirtyFlags{};
5544 eax_source_dirty_filter_flags_ = EaxSourceSourceFilterDirtyFlags{};
5547 void ALsource::eax_set(
5548 const EaxEaxCall& eax_call)
5550 if (eax_call.get_version() == 1)
5552 eax1_set(eax_call);
5553 return;
5556 switch (eax_call.get_property_id())
5558 case EAXSOURCE_NONE:
5559 break;
5561 case EAXSOURCE_ALLPARAMETERS:
5562 eax_defer_source_all(eax_call);
5563 break;
5565 case EAXSOURCE_OBSTRUCTIONPARAMETERS:
5566 eax_defer_source_obstruction_all(eax_call);
5567 break;
5569 case EAXSOURCE_OCCLUSIONPARAMETERS:
5570 eax_defer_source_occlusion_all(eax_call);
5571 break;
5573 case EAXSOURCE_EXCLUSIONPARAMETERS:
5574 eax_defer_source_exclusion_all(eax_call);
5575 break;
5577 case EAXSOURCE_DIRECT:
5578 eax_defer_source_direct(eax_call);
5579 break;
5581 case EAXSOURCE_DIRECTHF:
5582 eax_defer_source_direct_hf(eax_call);
5583 break;
5585 case EAXSOURCE_ROOM:
5586 eax_defer_source_room(eax_call);
5587 break;
5589 case EAXSOURCE_ROOMHF:
5590 eax_defer_source_room_hf(eax_call);
5591 break;
5593 case EAXSOURCE_OBSTRUCTION:
5594 eax_defer_source_obstruction(eax_call);
5595 break;
5597 case EAXSOURCE_OBSTRUCTIONLFRATIO:
5598 eax_defer_source_obstruction_lf_ratio(eax_call);
5599 break;
5601 case EAXSOURCE_OCCLUSION:
5602 eax_defer_source_occlusion(eax_call);
5603 break;
5605 case EAXSOURCE_OCCLUSIONLFRATIO:
5606 eax_defer_source_occlusion_lf_ratio(eax_call);
5607 break;
5609 case EAXSOURCE_OCCLUSIONROOMRATIO:
5610 eax_defer_source_occlusion_room_ratio(eax_call);
5611 break;
5613 case EAXSOURCE_OCCLUSIONDIRECTRATIO:
5614 eax_defer_source_occlusion_direct_ratio(eax_call);
5615 break;
5617 case EAXSOURCE_EXCLUSION:
5618 eax_defer_source_exclusion(eax_call);
5619 break;
5621 case EAXSOURCE_EXCLUSIONLFRATIO:
5622 eax_defer_source_exclusion_lf_ratio(eax_call);
5623 break;
5625 case EAXSOURCE_OUTSIDEVOLUMEHF:
5626 eax_defer_source_outside_volume_hf(eax_call);
5627 break;
5629 case EAXSOURCE_DOPPLERFACTOR:
5630 eax_defer_source_doppler_factor(eax_call);
5631 break;
5633 case EAXSOURCE_ROLLOFFFACTOR:
5634 eax_defer_source_rolloff_factor(eax_call);
5635 break;
5637 case EAXSOURCE_ROOMROLLOFFFACTOR:
5638 eax_defer_source_room_rolloff_factor(eax_call);
5639 break;
5641 case EAXSOURCE_AIRABSORPTIONFACTOR:
5642 eax_defer_source_air_absorption_factor(eax_call);
5643 break;
5645 case EAXSOURCE_FLAGS:
5646 eax_defer_source_flags(eax_call);
5647 break;
5649 case EAXSOURCE_SENDPARAMETERS:
5650 eax_defer_send(eax_call);
5651 break;
5653 case EAXSOURCE_ALLSENDPARAMETERS:
5654 eax_defer_send_all(eax_call);
5655 break;
5657 case EAXSOURCE_OCCLUSIONSENDPARAMETERS:
5658 eax_defer_send_occlusion_all(eax_call);
5659 break;
5661 case EAXSOURCE_EXCLUSIONSENDPARAMETERS:
5662 eax_defer_send_exclusion_all(eax_call);
5663 break;
5665 case EAXSOURCE_ACTIVEFXSLOTID:
5666 eax_defer_active_fx_slots(eax_call);
5667 break;
5669 case EAXSOURCE_MACROFXFACTOR:
5670 eax_defer_source_macro_fx_factor(eax_call);
5671 break;
5673 case EAXSOURCE_SPEAKERLEVELS:
5674 eax_defer_source_speaker_level_all(eax_call);
5675 break;
5677 case EAXSOURCE_ALL2DPARAMETERS:
5678 eax_defer_source_2d_all(eax_call);
5679 break;
5681 default:
5682 eax_fail("Unsupported property id.");
5685 if(!eax_call.is_deferred())
5687 eax_apply_deferred();
5688 EaxUpdateSourceVoice(this, eax_al_context_);
5692 const GUID& ALsource::eax_get_send_fx_slot_guid(
5693 int eax_version,
5694 EaxFxSlotIndexValue fx_slot_index)
5696 switch (eax_version)
5698 case 4:
5699 switch (fx_slot_index)
5701 case 0:
5702 return EAXPROPERTYID_EAX40_FXSlot0;
5704 case 1:
5705 return EAXPROPERTYID_EAX40_FXSlot1;
5707 case 2:
5708 return EAXPROPERTYID_EAX40_FXSlot2;
5710 case 3:
5711 return EAXPROPERTYID_EAX40_FXSlot3;
5713 default:
5714 eax_fail("FX slot index out of range.");
5717 case 5:
5718 switch (fx_slot_index)
5720 case 0:
5721 return EAXPROPERTYID_EAX50_FXSlot0;
5723 case 1:
5724 return EAXPROPERTYID_EAX50_FXSlot1;
5726 case 2:
5727 return EAXPROPERTYID_EAX50_FXSlot2;
5729 case 3:
5730 return EAXPROPERTYID_EAX50_FXSlot3;
5732 default:
5733 eax_fail("FX slot index out of range.");
5736 default:
5737 eax_fail("Unsupported EAX version.");
5741 void ALsource::eax_copy_send(
5742 const EAXSOURCEALLSENDPROPERTIES& src_send,
5743 EAXSOURCESENDPROPERTIES& dst_send)
5745 dst_send.lSend = src_send.lSend;
5746 dst_send.lSendHF = src_send.lSendHF;
5749 void ALsource::eax_copy_send(
5750 const EAXSOURCEALLSENDPROPERTIES& src_send,
5751 EAXSOURCEALLSENDPROPERTIES& dst_send)
5753 dst_send = src_send;
5756 void ALsource::eax_copy_send(
5757 const EAXSOURCEALLSENDPROPERTIES& src_send,
5758 EAXSOURCEOCCLUSIONSENDPROPERTIES& dst_send)
5760 dst_send.lOcclusion = src_send.lOcclusion;
5761 dst_send.flOcclusionLFRatio = src_send.flOcclusionLFRatio;
5762 dst_send.flOcclusionRoomRatio = src_send.flOcclusionRoomRatio;
5763 dst_send.flOcclusionDirectRatio = src_send.flOcclusionDirectRatio;
5766 void ALsource::eax_copy_send(
5767 const EAXSOURCEALLSENDPROPERTIES& src_send,
5768 EAXSOURCEEXCLUSIONSENDPROPERTIES& dst_send)
5770 dst_send.lExclusion = src_send.lExclusion;
5771 dst_send.flExclusionLFRatio = src_send.flExclusionLFRatio;
5774 void ALsource::eax1_get(const EaxEaxCall& eax_call)
5776 switch (eax_call.get_property_id())
5778 case DSPROPERTY_EAXBUFFER_ALL:
5779 case DSPROPERTY_EAXBUFFER_REVERBMIX:
5780 eax_call.set_value<EaxSourceException>(eax1_);
5781 break;
5783 default:
5784 eax_fail("Unsupported property id.");
5788 void ALsource::eax_api_get_source_all_v2(
5789 const EaxEaxCall& eax_call)
5791 auto eax_2_all = EAX20BUFFERPROPERTIES{};
5792 eax_2_all.lDirect = eax_.source.lDirect;
5793 eax_2_all.lDirectHF = eax_.source.lDirectHF;
5794 eax_2_all.lRoom = eax_.source.lRoom;
5795 eax_2_all.lRoomHF = eax_.source.lRoomHF;
5796 eax_2_all.flRoomRolloffFactor = eax_.source.flRoomRolloffFactor;
5797 eax_2_all.lObstruction = eax_.source.lObstruction;
5798 eax_2_all.flObstructionLFRatio = eax_.source.flObstructionLFRatio;
5799 eax_2_all.lOcclusion = eax_.source.lOcclusion;
5800 eax_2_all.flOcclusionLFRatio = eax_.source.flOcclusionLFRatio;
5801 eax_2_all.flOcclusionRoomRatio = eax_.source.flOcclusionRoomRatio;
5802 eax_2_all.lOutsideVolumeHF = eax_.source.lOutsideVolumeHF;
5803 eax_2_all.flAirAbsorptionFactor = eax_.source.flAirAbsorptionFactor;
5804 eax_2_all.dwFlags = eax_.source.ulFlags;
5806 eax_call.set_value<EaxSourceException>(eax_2_all);
5809 void ALsource::eax_api_get_source_all_v3(
5810 const EaxEaxCall& eax_call)
5812 eax_call.set_value<EaxSourceException>(static_cast<const EAX30SOURCEPROPERTIES&>(eax_.source));
5815 void ALsource::eax_api_get_source_all_v5(
5816 const EaxEaxCall& eax_call)
5818 eax_call.set_value<EaxSourceException>(eax_.source);
5821 void ALsource::eax_api_get_source_all(
5822 const EaxEaxCall& eax_call)
5824 switch (eax_call.get_version())
5826 case 2:
5827 eax_api_get_source_all_v2(eax_call);
5828 break;
5830 case 3:
5831 case 4:
5832 eax_api_get_source_all_v3(eax_call);
5833 break;
5835 case 5:
5836 eax_api_get_source_all_v5(eax_call);
5837 break;
5839 default:
5840 eax_fail("Unsupported EAX version.");
5844 void ALsource::eax_api_get_source_all_obstruction(
5845 const EaxEaxCall& eax_call)
5847 auto eax_obstruction_all = EAXOBSTRUCTIONPROPERTIES{};
5848 eax_obstruction_all.lObstruction = eax_.source.lObstruction;
5849 eax_obstruction_all.flObstructionLFRatio = eax_.source.flObstructionLFRatio;
5851 eax_call.set_value<EaxSourceException>(eax_obstruction_all);
5854 void ALsource::eax_api_get_source_all_occlusion(
5855 const EaxEaxCall& eax_call)
5857 auto eax_occlusion_all = EAXOCCLUSIONPROPERTIES{};
5858 eax_occlusion_all.lOcclusion = eax_.source.lOcclusion;
5859 eax_occlusion_all.flOcclusionLFRatio = eax_.source.flOcclusionLFRatio;
5860 eax_occlusion_all.flOcclusionRoomRatio = eax_.source.flOcclusionRoomRatio;
5861 eax_occlusion_all.flOcclusionDirectRatio = eax_.source.flOcclusionDirectRatio;
5863 eax_call.set_value<EaxSourceException>(eax_occlusion_all);
5866 void ALsource::eax_api_get_source_all_exclusion(
5867 const EaxEaxCall& eax_call)
5869 auto eax_exclusion_all = EAXEXCLUSIONPROPERTIES{};
5870 eax_exclusion_all.lExclusion = eax_.source.lExclusion;
5871 eax_exclusion_all.flExclusionLFRatio = eax_.source.flExclusionLFRatio;
5873 eax_call.set_value<EaxSourceException>(eax_exclusion_all);
5876 void ALsource::eax_api_get_source_active_fx_slot_id(
5877 const EaxEaxCall& eax_call)
5879 switch (eax_call.get_version())
5881 case 4:
5883 const auto& active_fx_slots = reinterpret_cast<const EAX40ACTIVEFXSLOTS&>(eax_.active_fx_slots);
5884 eax_call.set_value<EaxSourceException>(active_fx_slots);
5886 break;
5888 case 5:
5890 const auto& active_fx_slots = reinterpret_cast<const EAX50ACTIVEFXSLOTS&>(eax_.active_fx_slots);
5891 eax_call.set_value<EaxSourceException>(active_fx_slots);
5893 break;
5895 default:
5896 eax_fail("Unsupported EAX version.");
5900 void ALsource::eax_api_get_source_all_2d(
5901 const EaxEaxCall& eax_call)
5903 auto eax_2d_all = EAXSOURCE2DPROPERTIES{};
5904 eax_2d_all.lDirect = eax_.source.lDirect;
5905 eax_2d_all.lDirectHF = eax_.source.lDirectHF;
5906 eax_2d_all.lRoom = eax_.source.lRoom;
5907 eax_2d_all.lRoomHF = eax_.source.lRoomHF;
5908 eax_2d_all.ulFlags = eax_.source.ulFlags;
5910 eax_call.set_value<EaxSourceException>(eax_2d_all);
5913 void ALsource::eax_api_get_source_speaker_level_all(
5914 const EaxEaxCall& eax_call)
5916 auto& all = eax_call.get_value<EaxSourceException, EAXSPEAKERLEVELPROPERTIES>();
5918 eax_validate_source_speaker_id(all.lSpeakerID);
5919 const auto speaker_index = static_cast<std::size_t>(all.lSpeakerID - 1);
5920 all.lLevel = eax_.speaker_levels[speaker_index];
5923 void ALsource::eax_get(
5924 const EaxEaxCall& eax_call)
5926 if (eax_call.get_version() == 1)
5928 eax1_get(eax_call);
5929 return;
5932 switch (eax_call.get_property_id())
5934 case EAXSOURCE_NONE:
5935 break;
5937 case EAXSOURCE_ALLPARAMETERS:
5938 eax_api_get_source_all(eax_call);
5939 break;
5941 case EAXSOURCE_OBSTRUCTIONPARAMETERS:
5942 eax_api_get_source_all_obstruction(eax_call);
5943 break;
5945 case EAXSOURCE_OCCLUSIONPARAMETERS:
5946 eax_api_get_source_all_occlusion(eax_call);
5947 break;
5949 case EAXSOURCE_EXCLUSIONPARAMETERS:
5950 eax_api_get_source_all_exclusion(eax_call);
5951 break;
5953 case EAXSOURCE_DIRECT:
5954 eax_call.set_value<EaxSourceException>(eax_.source.lDirect);
5955 break;
5957 case EAXSOURCE_DIRECTHF:
5958 eax_call.set_value<EaxSourceException>(eax_.source.lDirectHF);
5959 break;
5961 case EAXSOURCE_ROOM:
5962 eax_call.set_value<EaxSourceException>(eax_.source.lRoom);
5963 break;
5965 case EAXSOURCE_ROOMHF:
5966 eax_call.set_value<EaxSourceException>(eax_.source.lRoomHF);
5967 break;
5969 case EAXSOURCE_OBSTRUCTION:
5970 eax_call.set_value<EaxSourceException>(eax_.source.lObstruction);
5971 break;
5973 case EAXSOURCE_OBSTRUCTIONLFRATIO:
5974 eax_call.set_value<EaxSourceException>(eax_.source.flObstructionLFRatio);
5975 break;
5977 case EAXSOURCE_OCCLUSION:
5978 eax_call.set_value<EaxSourceException>(eax_.source.lOcclusion);
5979 break;
5981 case EAXSOURCE_OCCLUSIONLFRATIO:
5982 eax_call.set_value<EaxSourceException>(eax_.source.flOcclusionLFRatio);
5983 break;
5985 case EAXSOURCE_OCCLUSIONROOMRATIO:
5986 eax_call.set_value<EaxSourceException>(eax_.source.flOcclusionRoomRatio);
5987 break;
5989 case EAXSOURCE_OCCLUSIONDIRECTRATIO:
5990 eax_call.set_value<EaxSourceException>(eax_.source.flOcclusionDirectRatio);
5991 break;
5993 case EAXSOURCE_EXCLUSION:
5994 eax_call.set_value<EaxSourceException>(eax_.source.lExclusion);
5995 break;
5997 case EAXSOURCE_EXCLUSIONLFRATIO:
5998 eax_call.set_value<EaxSourceException>(eax_.source.flExclusionLFRatio);
5999 break;
6001 case EAXSOURCE_OUTSIDEVOLUMEHF:
6002 eax_call.set_value<EaxSourceException>(eax_.source.lOutsideVolumeHF);
6003 break;
6005 case EAXSOURCE_DOPPLERFACTOR:
6006 eax_call.set_value<EaxSourceException>(eax_.source.flDopplerFactor);
6007 break;
6009 case EAXSOURCE_ROLLOFFFACTOR:
6010 eax_call.set_value<EaxSourceException>(eax_.source.flRolloffFactor);
6011 break;
6013 case EAXSOURCE_ROOMROLLOFFFACTOR:
6014 eax_call.set_value<EaxSourceException>(eax_.source.flRoomRolloffFactor);
6015 break;
6017 case EAXSOURCE_AIRABSORPTIONFACTOR:
6018 eax_call.set_value<EaxSourceException>(eax_.source.flAirAbsorptionFactor);
6019 break;
6021 case EAXSOURCE_FLAGS:
6022 eax_call.set_value<EaxSourceException>(eax_.source.ulFlags);
6023 break;
6025 case EAXSOURCE_SENDPARAMETERS:
6026 eax_api_get_send_properties<EaxSourceException, EAXSOURCESENDPROPERTIES>(eax_call);
6027 break;
6029 case EAXSOURCE_ALLSENDPARAMETERS:
6030 eax_api_get_send_properties<EaxSourceException, EAXSOURCEALLSENDPROPERTIES>(eax_call);
6031 break;
6033 case EAXSOURCE_OCCLUSIONSENDPARAMETERS:
6034 eax_api_get_send_properties<EaxSourceException, EAXSOURCEOCCLUSIONSENDPROPERTIES>(eax_call);
6035 break;
6037 case EAXSOURCE_EXCLUSIONSENDPARAMETERS:
6038 eax_api_get_send_properties<EaxSourceException, EAXSOURCEEXCLUSIONSENDPROPERTIES>(eax_call);
6039 break;
6041 case EAXSOURCE_ACTIVEFXSLOTID:
6042 eax_api_get_source_active_fx_slot_id(eax_call);
6043 break;
6045 case EAXSOURCE_MACROFXFACTOR:
6046 eax_call.set_value<EaxSourceException>(eax_.source.flMacroFXFactor);
6047 break;
6049 case EAXSOURCE_SPEAKERLEVELS:
6050 eax_api_get_source_speaker_level_all(eax_call);
6051 break;
6053 case EAXSOURCE_ALL2DPARAMETERS:
6054 eax_api_get_source_all_2d(eax_call);
6055 break;
6057 default:
6058 eax_fail("Unsupported property id.");
6062 void ALsource::eax_set_al_source_send(
6063 ALeffectslot *slot,
6064 size_t sendidx,
6065 const EaxAlLowPassParam &filter)
6067 if(sendidx >= EAX_MAX_FXSLOTS)
6068 return;
6070 auto &send = Send[sendidx];
6071 send.Gain = filter.gain;
6072 send.GainHF = filter.gain_hf;
6073 send.HFReference = LOWPASSFREQREF;
6074 send.GainLF = 1.0f;
6075 send.LFReference = HIGHPASSFREQREF;
6077 if(slot) IncrementRef(slot->ref);
6078 if(auto *oldslot = send.Slot)
6079 DecrementRef(oldslot->ref);
6080 send.Slot = slot;
6082 mPropsDirty = true;
6085 #endif // ALSOFT_EAX