Reset the ringbuffer when stopping OpenSL playback
[openal-soft.git] / al / source.cpp
blobd2ccfe08f8b095c450d1daa6d958b3c1c3d521f7
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 #ifdef ALSOFT_EAX
126 + source->RolloffFactor2
127 #endif
129 props->Position = source->Position;
130 props->Velocity = source->Velocity;
131 props->Direction = source->Direction;
132 props->OrientAt = source->OrientAt;
133 props->OrientUp = source->OrientUp;
134 props->HeadRelative = source->HeadRelative;
135 props->mDistanceModel = source->mDistanceModel;
136 props->mResampler = source->mResampler;
137 props->DirectChannels = source->DirectChannels;
138 props->mSpatializeMode = source->mSpatialize;
140 props->DryGainHFAuto = source->DryGainHFAuto;
141 props->WetGainAuto = source->WetGainAuto;
142 props->WetGainHFAuto = source->WetGainHFAuto;
143 props->OuterGainHF = source->OuterGainHF;
145 props->AirAbsorptionFactor = source->AirAbsorptionFactor;
146 props->RoomRolloffFactor = source->RoomRolloffFactor;
147 props->DopplerFactor = source->DopplerFactor;
149 props->StereoPan = source->StereoPan;
151 props->Radius = source->Radius;
152 props->EnhWidth = source->EnhWidth;
154 props->Direct.Gain = source->Direct.Gain;
155 props->Direct.GainHF = source->Direct.GainHF;
156 props->Direct.HFReference = source->Direct.HFReference;
157 props->Direct.GainLF = source->Direct.GainLF;
158 props->Direct.LFReference = source->Direct.LFReference;
160 auto copy_send = [](const ALsource::SendData &srcsend) noexcept -> VoiceProps::SendData
162 VoiceProps::SendData ret{};
163 ret.Slot = srcsend.Slot ? &srcsend.Slot->mSlot : nullptr;
164 ret.Gain = srcsend.Gain;
165 ret.GainHF = srcsend.GainHF;
166 ret.HFReference = srcsend.HFReference;
167 ret.GainLF = srcsend.GainLF;
168 ret.LFReference = srcsend.LFReference;
169 return ret;
171 std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send);
172 if(!props->Send[0].Slot && context->mDefaultSlot)
173 props->Send[0].Slot = &context->mDefaultSlot->mSlot;
175 /* Set the new container for updating internal parameters. */
176 props = voice->mUpdate.exchange(props, std::memory_order_acq_rel);
177 if(props)
179 /* If there was an unused update container, put it back in the
180 * freelist.
182 AtomicReplaceHead(context->mFreeVoiceProps, props);
186 /* GetSourceSampleOffset
188 * Gets the current read offset for the given Source, in 32.32 fixed-point
189 * samples. The offset is relative to the start of the queue (not the start of
190 * the current buffer).
192 int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
194 ALCdevice *device{context->mALDevice.get()};
195 const VoiceBufferItem *Current{};
196 uint64_t readPos{};
197 ALuint refcount;
198 Voice *voice;
200 do {
201 refcount = device->waitForMix();
202 *clocktime = GetDeviceClockTime(device);
203 voice = GetSourceVoice(Source, context);
204 if(voice)
206 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
208 readPos = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << 32;
209 readPos |= uint64_t{voice->mPositionFrac.load(std::memory_order_relaxed)} <<
210 (32-MixerFracBits);
212 std::atomic_thread_fence(std::memory_order_acquire);
213 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
215 if(!voice)
216 return 0;
218 for(auto &item : Source->mQueue)
220 if(&item == Current) break;
221 readPos += uint64_t{item.mSampleLen} << 32;
223 return static_cast<int64_t>(minu64(readPos, 0x7fffffffffffffff_u64));
226 /* GetSourceSecOffset
228 * Gets the current read offset for the given Source, in seconds. The offset is
229 * relative to the start of the queue (not the start of the current buffer).
231 double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
233 ALCdevice *device{context->mALDevice.get()};
234 const VoiceBufferItem *Current{};
235 uint64_t readPos{};
236 ALuint refcount;
237 Voice *voice;
239 do {
240 refcount = device->waitForMix();
241 *clocktime = GetDeviceClockTime(device);
242 voice = GetSourceVoice(Source, context);
243 if(voice)
245 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
247 readPos = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << MixerFracBits;
248 readPos |= voice->mPositionFrac.load(std::memory_order_relaxed);
250 std::atomic_thread_fence(std::memory_order_acquire);
251 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
253 if(!voice)
254 return 0.0f;
256 const ALbuffer *BufferFmt{nullptr};
257 auto BufferList = Source->mQueue.cbegin();
258 while(BufferList != Source->mQueue.cend() && std::addressof(*BufferList) != Current)
260 if(!BufferFmt) BufferFmt = BufferList->mBuffer;
261 readPos += uint64_t{BufferList->mSampleLen} << MixerFracBits;
262 ++BufferList;
264 while(BufferList != Source->mQueue.cend() && !BufferFmt)
266 BufferFmt = BufferList->mBuffer;
267 ++BufferList;
269 ASSUME(BufferFmt != nullptr);
271 return static_cast<double>(readPos) / double{MixerFracOne} / BufferFmt->mSampleRate;
274 /* GetSourceOffset
276 * Gets the current read offset for the given Source, in the appropriate format
277 * (Bytes, Samples or Seconds). The offset is relative to the start of the
278 * queue (not the start of the current buffer).
280 double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
282 ALCdevice *device{context->mALDevice.get()};
283 const VoiceBufferItem *Current{};
284 ALuint readPos{};
285 ALuint readPosFrac{};
286 ALuint refcount;
287 Voice *voice;
289 do {
290 refcount = device->waitForMix();
291 voice = GetSourceVoice(Source, context);
292 if(voice)
294 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
296 readPos = voice->mPosition.load(std::memory_order_relaxed);
297 readPosFrac = voice->mPositionFrac.load(std::memory_order_relaxed);
299 std::atomic_thread_fence(std::memory_order_acquire);
300 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
302 if(!voice)
303 return 0.0;
305 const ALbuffer *BufferFmt{nullptr};
306 auto BufferList = Source->mQueue.cbegin();
307 while(BufferList != Source->mQueue.cend() && std::addressof(*BufferList) != Current)
309 if(!BufferFmt) BufferFmt = BufferList->mBuffer;
310 readPos += BufferList->mSampleLen;
311 ++BufferList;
313 while(BufferList != Source->mQueue.cend() && !BufferFmt)
315 BufferFmt = BufferList->mBuffer;
316 ++BufferList;
318 ASSUME(BufferFmt != nullptr);
320 double offset{};
321 switch(name)
323 case AL_SEC_OFFSET:
324 offset = (readPos + readPosFrac/double{MixerFracOne}) / BufferFmt->mSampleRate;
325 break;
327 case AL_SAMPLE_OFFSET:
328 offset = readPos + readPosFrac/double{MixerFracOne};
329 break;
331 case AL_BYTE_OFFSET:
332 if(BufferFmt->OriginalType == UserFmtIMA4)
334 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
335 ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
336 ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
338 /* Round down to nearest ADPCM block */
339 offset = static_cast<double>(readPos / FrameBlockSize * BlockSize);
341 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
343 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
344 ALuint align{(FrameBlockSize-2)/2 + 7};
345 ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
347 /* Round down to nearest ADPCM block */
348 offset = static_cast<double>(readPos / FrameBlockSize * BlockSize);
350 else
352 const ALuint FrameSize{BufferFmt->frameSizeFromFmt()};
353 offset = static_cast<double>(readPos * FrameSize);
355 break;
357 return offset;
360 /* GetSourceLength
362 * Gets the length of the given Source's buffer queue, in the appropriate
363 * format (Bytes, Samples or Seconds).
365 double GetSourceLength(const ALsource *source, ALenum name)
367 uint64_t length{0};
368 const ALbuffer *BufferFmt{nullptr};
369 for(auto &listitem : source->mQueue)
371 if(!BufferFmt)
372 BufferFmt = listitem.mBuffer;
373 length += listitem.mSampleLen;
375 if(length == 0)
376 return 0.0;
378 ASSUME(BufferFmt != nullptr);
379 switch(name)
381 case AL_SEC_LENGTH_SOFT:
382 return static_cast<double>(length) / BufferFmt->mSampleRate;
384 case AL_SAMPLE_LENGTH_SOFT:
385 return static_cast<double>(length);
387 case AL_BYTE_LENGTH_SOFT:
388 if(BufferFmt->OriginalType == UserFmtIMA4)
390 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
391 ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
392 ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
394 /* Round down to nearest ADPCM block */
395 return static_cast<double>(length / FrameBlockSize) * BlockSize;
397 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
399 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
400 ALuint align{(FrameBlockSize-2)/2 + 7};
401 ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
403 /* Round down to nearest ADPCM block */
404 return static_cast<double>(length / FrameBlockSize) * BlockSize;
406 return static_cast<double>(length) * BufferFmt->frameSizeFromFmt();
408 return 0.0;
412 struct VoicePos {
413 ALuint pos, frac;
414 ALbufferQueueItem *bufferitem;
418 * GetSampleOffset
420 * Retrieves the voice position, fixed-point fraction, and bufferlist item
421 * using the givem offset type and offset. If the offset is out of range,
422 * returns an empty optional.
424 al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList, ALenum OffsetType,
425 double Offset)
427 /* Find the first valid Buffer in the Queue */
428 const ALbuffer *BufferFmt{nullptr};
429 for(auto &item : BufferList)
431 BufferFmt = item.mBuffer;
432 if(BufferFmt) break;
434 if(!BufferFmt || BufferFmt->mCallback)
435 return al::nullopt;
437 /* Get sample frame offset */
438 ALuint offset{0u}, frac{0u};
439 double dbloff, dblfrac;
440 switch(OffsetType)
442 case AL_SEC_OFFSET:
443 dblfrac = std::modf(Offset*BufferFmt->mSampleRate, &dbloff);
444 offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
445 frac = static_cast<ALuint>(mind(dblfrac*MixerFracOne, MixerFracOne-1.0));
446 break;
448 case AL_SAMPLE_OFFSET:
449 dblfrac = std::modf(Offset, &dbloff);
450 offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
451 frac = static_cast<ALuint>(mind(dblfrac*MixerFracOne, MixerFracOne-1.0));
452 break;
454 case AL_BYTE_OFFSET:
455 /* Determine the ByteOffset (and ensure it is block aligned) */
456 offset = static_cast<ALuint>(Offset);
457 if(BufferFmt->OriginalType == UserFmtIMA4)
459 const ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
460 offset /= align * BufferFmt->channelsFromFmt();
461 offset *= BufferFmt->OriginalAlign;
463 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
465 const ALuint align{(BufferFmt->OriginalAlign-2)/2 + 7};
466 offset /= align * BufferFmt->channelsFromFmt();
467 offset *= BufferFmt->OriginalAlign;
469 else
470 offset /= BufferFmt->frameSizeFromFmt();
471 frac = 0;
472 break;
475 /* Find the bufferlist item this offset belongs to. */
476 ALuint totalBufferLen{0u};
477 for(auto &item : BufferList)
479 if(totalBufferLen > offset)
480 break;
481 if(item.mSampleLen > offset-totalBufferLen)
483 /* Offset is in this buffer */
484 return VoicePos{offset-totalBufferLen, frac, &item};
486 totalBufferLen += item.mSampleLen;
489 /* Offset is out of range of the queue */
490 return al::nullopt;
494 void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, ALCcontext *context,
495 ALCdevice *device)
497 voice->mLoopBuffer.store(source->Looping ? &source->mQueue.front() : nullptr,
498 std::memory_order_relaxed);
500 ALbuffer *buffer{BufferList->mBuffer};
501 voice->mFrequency = buffer->mSampleRate;
502 voice->mFmtChannels =
503 (buffer->mChannels == FmtStereo && source->mStereoMode == SourceStereo::Enhanced) ?
504 FmtSuperStereo : buffer->mChannels;
505 voice->mFmtType = buffer->mType;
506 voice->mFrameStep = buffer->channelsFromFmt();
507 voice->mFrameSize = buffer->frameSizeFromFmt();
508 voice->mAmbiLayout = IsUHJ(voice->mFmtChannels) ? AmbiLayout::FuMa : buffer->mAmbiLayout;
509 voice->mAmbiScaling = IsUHJ(voice->mFmtChannels) ? AmbiScaling::UHJ : buffer->mAmbiScaling;
510 voice->mAmbiOrder = (voice->mFmtChannels == FmtSuperStereo) ? 1 : buffer->mAmbiOrder;
512 if(buffer->mCallback) voice->mFlags.set(VoiceIsCallback);
513 else if(source->SourceType == AL_STATIC) voice->mFlags.set(VoiceIsStatic);
514 voice->mNumCallbackSamples = 0;
516 voice->prepare(device);
518 source->mPropsDirty = false;
519 UpdateSourceProps(source, voice, context);
521 voice->mSourceID.store(source->id, std::memory_order_release);
525 VoiceChange *GetVoiceChanger(ALCcontext *ctx)
527 VoiceChange *vchg{ctx->mVoiceChangeTail};
528 if UNLIKELY(vchg == ctx->mCurrentVoiceChange.load(std::memory_order_acquire))
530 ctx->allocVoiceChanges();
531 vchg = ctx->mVoiceChangeTail;
534 ctx->mVoiceChangeTail = vchg->mNext.exchange(nullptr, std::memory_order_relaxed);
536 return vchg;
539 void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
541 ALCdevice *device{ctx->mALDevice.get()};
543 VoiceChange *oldhead{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
544 while(VoiceChange *next{oldhead->mNext.load(std::memory_order_relaxed)})
545 oldhead = next;
546 oldhead->mNext.store(tail, std::memory_order_release);
548 const bool connected{device->Connected.load(std::memory_order_acquire)};
549 device->waitForMix();
550 if UNLIKELY(!connected)
552 if(ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire))
554 /* If the device is disconnected and voices are stopped, just
555 * ignore all pending changes.
557 VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
558 while(VoiceChange *next{cur->mNext.load(std::memory_order_acquire)})
560 cur = next;
561 if(Voice *voice{cur->mVoice})
562 voice->mSourceID.store(0, std::memory_order_relaxed);
564 ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
570 bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALCcontext *context,
571 ALCdevice *device)
573 /* First, get a free voice to start at the new offset. */
574 auto voicelist = context->getVoicesSpan();
575 Voice *newvoice{};
576 ALuint vidx{0};
577 for(Voice *voice : voicelist)
579 if(voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
580 && voice->mSourceID.load(std::memory_order_relaxed) == 0u
581 && voice->mPendingChange.load(std::memory_order_relaxed) == false)
583 newvoice = voice;
584 break;
586 ++vidx;
588 if(unlikely(!newvoice))
590 auto &allvoices = *context->mVoices.load(std::memory_order_relaxed);
591 if(allvoices.size() == voicelist.size())
592 context->allocVoices(1);
593 context->mActiveVoiceCount.fetch_add(1, std::memory_order_release);
594 voicelist = context->getVoicesSpan();
596 vidx = 0;
597 for(Voice *voice : voicelist)
599 if(voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
600 && voice->mSourceID.load(std::memory_order_relaxed) == 0u
601 && voice->mPendingChange.load(std::memory_order_relaxed) == false)
603 newvoice = voice;
604 break;
606 ++vidx;
608 ASSUME(newvoice != nullptr);
611 /* Initialize the new voice and set its starting offset.
612 * TODO: It might be better to have the VoiceChange processing copy the old
613 * voice's mixing parameters (and pending update) insead of initializing it
614 * all here. This would just need to set the minimum properties to link the
615 * voice to the source and its position-dependent properties (including the
616 * fading flag).
618 newvoice->mPlayState.store(Voice::Pending, std::memory_order_relaxed);
619 newvoice->mPosition.store(vpos.pos, std::memory_order_relaxed);
620 newvoice->mPositionFrac.store(vpos.frac, std::memory_order_relaxed);
621 newvoice->mCurrentBuffer.store(vpos.bufferitem, std::memory_order_relaxed);
622 newvoice->mFlags.reset();
623 if(vpos.pos > 0 || vpos.frac > 0 || vpos.bufferitem != &source->mQueue.front())
624 newvoice->mFlags.set(VoiceIsFading);
625 InitVoice(newvoice, source, vpos.bufferitem, context, device);
626 source->VoiceIdx = vidx;
628 /* Set the old voice as having a pending change, and send it off with the
629 * new one with a new offset voice change.
631 oldvoice->mPendingChange.store(true, std::memory_order_relaxed);
633 VoiceChange *vchg{GetVoiceChanger(context)};
634 vchg->mOldVoice = oldvoice;
635 vchg->mVoice = newvoice;
636 vchg->mSourceID = source->id;
637 vchg->mState = VChangeState::Restart;
638 SendVoiceChanges(context, vchg);
640 /* If the old voice still has a sourceID, it's still active and the change-
641 * over will work on the next update.
643 if LIKELY(oldvoice->mSourceID.load(std::memory_order_acquire) != 0u)
644 return true;
646 /* Otherwise, if the new voice's state is not pending, the change-over
647 * already happened.
649 if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending)
650 return true;
652 /* Otherwise, wait for any current mix to finish and check one last time. */
653 device->waitForMix();
654 if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending)
655 return true;
656 /* The change-over failed because the old voice stopped before the new
657 * voice could start at the new offset. Let go of the new voice and have
658 * the caller store the source offset since it's stopped.
660 newvoice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
661 newvoice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
662 newvoice->mSourceID.store(0u, std::memory_order_relaxed);
663 newvoice->mPlayState.store(Voice::Stopped, std::memory_order_relaxed);
664 return false;
669 * Returns if the last known state for the source was playing or paused. Does
670 * not sync with the mixer voice.
672 inline bool IsPlayingOrPaused(ALsource *source)
673 { return source->state == AL_PLAYING || source->state == AL_PAUSED; }
676 * Returns an updated source state using the matching voice's status (or lack
677 * thereof).
679 inline ALenum GetSourceState(ALsource *source, Voice *voice)
681 if(!voice && source->state == AL_PLAYING)
682 source->state = AL_STOPPED;
683 return source->state;
687 bool EnsureSources(ALCcontext *context, size_t needed)
689 size_t count{std::accumulate(context->mSourceList.cbegin(), context->mSourceList.cend(),
690 size_t{0},
691 [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
692 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
694 while(needed > count)
696 if UNLIKELY(context->mSourceList.size() >= 1<<25)
697 return false;
699 context->mSourceList.emplace_back();
700 auto sublist = context->mSourceList.end() - 1;
701 sublist->FreeMask = ~0_u64;
702 sublist->Sources = static_cast<ALsource*>(al_calloc(alignof(ALsource), sizeof(ALsource)*64));
703 if UNLIKELY(!sublist->Sources)
705 context->mSourceList.pop_back();
706 return false;
708 count += 64;
710 return true;
713 ALsource *AllocSource(ALCcontext *context)
715 auto sublist = std::find_if(context->mSourceList.begin(), context->mSourceList.end(),
716 [](const SourceSubList &entry) noexcept -> bool
717 { return entry.FreeMask != 0; });
718 auto lidx = static_cast<ALuint>(std::distance(context->mSourceList.begin(), sublist));
719 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
720 ASSUME(slidx < 64);
722 ALsource *source{al::construct_at(sublist->Sources + slidx)};
724 /* Add 1 to avoid source ID 0. */
725 source->id = ((lidx<<6) | slidx) + 1;
727 context->mNumSources += 1;
728 sublist->FreeMask &= ~(1_u64 << slidx);
730 return source;
733 void FreeSource(ALCcontext *context, ALsource *source)
735 const ALuint id{source->id - 1};
736 const size_t lidx{id >> 6};
737 const ALuint slidx{id & 0x3f};
739 if(Voice *voice{GetSourceVoice(source, context)})
741 VoiceChange *vchg{GetVoiceChanger(context)};
743 voice->mPendingChange.store(true, std::memory_order_relaxed);
744 vchg->mVoice = voice;
745 vchg->mSourceID = source->id;
746 vchg->mState = VChangeState::Stop;
748 SendVoiceChanges(context, vchg);
751 al::destroy_at(source);
753 context->mSourceList[lidx].FreeMask |= 1_u64 << slidx;
754 context->mNumSources--;
758 inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept
760 const size_t lidx{(id-1) >> 6};
761 const ALuint slidx{(id-1) & 0x3f};
763 if UNLIKELY(lidx >= context->mSourceList.size())
764 return nullptr;
765 SourceSubList &sublist{context->mSourceList[lidx]};
766 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
767 return nullptr;
768 return sublist.Sources + slidx;
771 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
773 const size_t lidx{(id-1) >> 6};
774 const ALuint slidx{(id-1) & 0x3f};
776 if UNLIKELY(lidx >= device->BufferList.size())
777 return nullptr;
778 BufferSubList &sublist = device->BufferList[lidx];
779 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
780 return nullptr;
781 return sublist.Buffers + slidx;
784 inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept
786 const size_t lidx{(id-1) >> 6};
787 const ALuint slidx{(id-1) & 0x3f};
789 if UNLIKELY(lidx >= device->FilterList.size())
790 return nullptr;
791 FilterSubList &sublist = device->FilterList[lidx];
792 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
793 return nullptr;
794 return sublist.Filters + slidx;
797 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
799 const size_t lidx{(id-1) >> 6};
800 const ALuint slidx{(id-1) & 0x3f};
802 if UNLIKELY(lidx >= context->mEffectSlotList.size())
803 return nullptr;
804 EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
805 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
806 return nullptr;
807 return sublist.EffectSlots + slidx;
811 al::optional<SourceStereo> StereoModeFromEnum(ALenum mode)
813 switch(mode)
815 case AL_NORMAL_SOFT: return al::make_optional(SourceStereo::Normal);
816 case AL_SUPER_STEREO_SOFT: return al::make_optional(SourceStereo::Enhanced);
818 WARN("Unsupported stereo mode: 0x%04x\n", mode);
819 return al::nullopt;
821 ALenum EnumFromStereoMode(SourceStereo mode)
823 switch(mode)
825 case SourceStereo::Normal: return AL_NORMAL_SOFT;
826 case SourceStereo::Enhanced: return AL_SUPER_STEREO_SOFT;
828 throw std::runtime_error{"Invalid SourceStereo: "+std::to_string(int(mode))};
831 al::optional<SpatializeMode> SpatializeModeFromEnum(ALenum mode)
833 switch(mode)
835 case AL_FALSE: return al::make_optional(SpatializeMode::Off);
836 case AL_TRUE: return al::make_optional(SpatializeMode::On);
837 case AL_AUTO_SOFT: return al::make_optional(SpatializeMode::Auto);
839 WARN("Unsupported spatialize mode: 0x%04x\n", mode);
840 return al::nullopt;
842 ALenum EnumFromSpatializeMode(SpatializeMode mode)
844 switch(mode)
846 case SpatializeMode::Off: return AL_FALSE;
847 case SpatializeMode::On: return AL_TRUE;
848 case SpatializeMode::Auto: return AL_AUTO_SOFT;
850 throw std::runtime_error{"Invalid SpatializeMode: "+std::to_string(int(mode))};
853 al::optional<DirectMode> DirectModeFromEnum(ALenum mode)
855 switch(mode)
857 case AL_FALSE: return al::make_optional(DirectMode::Off);
858 case AL_DROP_UNMATCHED_SOFT: return al::make_optional(DirectMode::DropMismatch);
859 case AL_REMIX_UNMATCHED_SOFT: return al::make_optional(DirectMode::RemixMismatch);
861 WARN("Unsupported direct mode: 0x%04x\n", mode);
862 return al::nullopt;
864 ALenum EnumFromDirectMode(DirectMode mode)
866 switch(mode)
868 case DirectMode::Off: return AL_FALSE;
869 case DirectMode::DropMismatch: return AL_DROP_UNMATCHED_SOFT;
870 case DirectMode::RemixMismatch: return AL_REMIX_UNMATCHED_SOFT;
872 throw std::runtime_error{"Invalid DirectMode: "+std::to_string(int(mode))};
875 al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
877 switch(model)
879 case AL_NONE: return al::make_optional(DistanceModel::Disable);
880 case AL_INVERSE_DISTANCE: return al::make_optional(DistanceModel::Inverse);
881 case AL_INVERSE_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::InverseClamped);
882 case AL_LINEAR_DISTANCE: return al::make_optional(DistanceModel::Linear);
883 case AL_LINEAR_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::LinearClamped);
884 case AL_EXPONENT_DISTANCE: return al::make_optional(DistanceModel::Exponent);
885 case AL_EXPONENT_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::ExponentClamped);
887 return al::nullopt;
889 ALenum ALenumFromDistanceModel(DistanceModel model)
891 switch(model)
893 case DistanceModel::Disable: return AL_NONE;
894 case DistanceModel::Inverse: return AL_INVERSE_DISTANCE;
895 case DistanceModel::InverseClamped: return AL_INVERSE_DISTANCE_CLAMPED;
896 case DistanceModel::Linear: return AL_LINEAR_DISTANCE;
897 case DistanceModel::LinearClamped: return AL_LINEAR_DISTANCE_CLAMPED;
898 case DistanceModel::Exponent: return AL_EXPONENT_DISTANCE;
899 case DistanceModel::ExponentClamped: return AL_EXPONENT_DISTANCE_CLAMPED;
901 throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast<int>(model))};
904 enum SourceProp : ALenum {
905 srcPitch = AL_PITCH,
906 srcGain = AL_GAIN,
907 srcMinGain = AL_MIN_GAIN,
908 srcMaxGain = AL_MAX_GAIN,
909 srcMaxDistance = AL_MAX_DISTANCE,
910 srcRolloffFactor = AL_ROLLOFF_FACTOR,
911 srcDopplerFactor = AL_DOPPLER_FACTOR,
912 srcConeOuterGain = AL_CONE_OUTER_GAIN,
913 srcSecOffset = AL_SEC_OFFSET,
914 srcSampleOffset = AL_SAMPLE_OFFSET,
915 srcByteOffset = AL_BYTE_OFFSET,
916 srcConeInnerAngle = AL_CONE_INNER_ANGLE,
917 srcConeOuterAngle = AL_CONE_OUTER_ANGLE,
918 srcRefDistance = AL_REFERENCE_DISTANCE,
920 srcPosition = AL_POSITION,
921 srcVelocity = AL_VELOCITY,
922 srcDirection = AL_DIRECTION,
924 srcSourceRelative = AL_SOURCE_RELATIVE,
925 srcLooping = AL_LOOPING,
926 srcBuffer = AL_BUFFER,
927 srcSourceState = AL_SOURCE_STATE,
928 srcBuffersQueued = AL_BUFFERS_QUEUED,
929 srcBuffersProcessed = AL_BUFFERS_PROCESSED,
930 srcSourceType = AL_SOURCE_TYPE,
932 /* ALC_EXT_EFX */
933 srcConeOuterGainHF = AL_CONE_OUTER_GAINHF,
934 srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR,
935 srcRoomRolloffFactor = AL_ROOM_ROLLOFF_FACTOR,
936 srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO,
937 srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO,
938 srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO,
939 srcDirectFilter = AL_DIRECT_FILTER,
940 srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER,
942 /* AL_SOFT_direct_channels */
943 srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT,
945 /* AL_EXT_source_distance_model */
946 srcDistanceModel = AL_DISTANCE_MODEL,
948 /* AL_SOFT_source_latency */
949 srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT,
950 srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT,
952 /* AL_EXT_STEREO_ANGLES */
953 srcAngles = AL_STEREO_ANGLES,
955 /* AL_EXT_SOURCE_RADIUS */
956 srcRadius = AL_SOURCE_RADIUS,
958 /* AL_EXT_BFORMAT */
959 srcOrientation = AL_ORIENTATION,
961 /* AL_SOFT_source_length */
962 srcByteLength = AL_BYTE_LENGTH_SOFT,
963 srcSampleLength = AL_SAMPLE_LENGTH_SOFT,
964 srcSecLength = AL_SEC_LENGTH_SOFT,
966 /* AL_SOFT_source_resampler */
967 srcResampler = AL_SOURCE_RESAMPLER_SOFT,
969 /* AL_SOFT_source_spatialize */
970 srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT,
972 /* ALC_SOFT_device_clock */
973 srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT,
974 srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT,
976 /* AL_SOFT_UHJ */
977 srcStereoMode = AL_STEREO_MODE_SOFT,
978 srcSuperStereoWidth = AL_SUPER_STEREO_WIDTH_SOFT,
982 constexpr size_t MaxValues{6u};
984 ALuint FloatValsByProp(ALenum prop)
986 switch(static_cast<SourceProp>(prop))
988 case AL_PITCH:
989 case AL_GAIN:
990 case AL_MIN_GAIN:
991 case AL_MAX_GAIN:
992 case AL_MAX_DISTANCE:
993 case AL_ROLLOFF_FACTOR:
994 case AL_DOPPLER_FACTOR:
995 case AL_CONE_OUTER_GAIN:
996 case AL_SEC_OFFSET:
997 case AL_SAMPLE_OFFSET:
998 case AL_BYTE_OFFSET:
999 case AL_CONE_INNER_ANGLE:
1000 case AL_CONE_OUTER_ANGLE:
1001 case AL_REFERENCE_DISTANCE:
1002 case AL_CONE_OUTER_GAINHF:
1003 case AL_AIR_ABSORPTION_FACTOR:
1004 case AL_ROOM_ROLLOFF_FACTOR:
1005 case AL_DIRECT_FILTER_GAINHF_AUTO:
1006 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1007 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1008 case AL_DIRECT_CHANNELS_SOFT:
1009 case AL_DISTANCE_MODEL:
1010 case AL_SOURCE_RELATIVE:
1011 case AL_LOOPING:
1012 case AL_SOURCE_STATE:
1013 case AL_BUFFERS_QUEUED:
1014 case AL_BUFFERS_PROCESSED:
1015 case AL_SOURCE_TYPE:
1016 case AL_SOURCE_RADIUS:
1017 case AL_SOURCE_RESAMPLER_SOFT:
1018 case AL_SOURCE_SPATIALIZE_SOFT:
1019 case AL_BYTE_LENGTH_SOFT:
1020 case AL_SAMPLE_LENGTH_SOFT:
1021 case AL_SEC_LENGTH_SOFT:
1022 case AL_STEREO_MODE_SOFT:
1023 case AL_SUPER_STEREO_WIDTH_SOFT:
1024 return 1;
1026 case AL_STEREO_ANGLES:
1027 return 2;
1029 case AL_POSITION:
1030 case AL_VELOCITY:
1031 case AL_DIRECTION:
1032 return 3;
1034 case AL_ORIENTATION:
1035 return 6;
1037 case AL_SEC_OFFSET_LATENCY_SOFT:
1038 case AL_SEC_OFFSET_CLOCK_SOFT:
1039 break; /* Double only */
1041 case AL_BUFFER:
1042 case AL_DIRECT_FILTER:
1043 case AL_AUXILIARY_SEND_FILTER:
1044 break; /* i/i64 only */
1045 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1046 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1047 break; /* i64 only */
1049 return 0;
1051 ALuint DoubleValsByProp(ALenum prop)
1053 switch(static_cast<SourceProp>(prop))
1055 case AL_PITCH:
1056 case AL_GAIN:
1057 case AL_MIN_GAIN:
1058 case AL_MAX_GAIN:
1059 case AL_MAX_DISTANCE:
1060 case AL_ROLLOFF_FACTOR:
1061 case AL_DOPPLER_FACTOR:
1062 case AL_CONE_OUTER_GAIN:
1063 case AL_SEC_OFFSET:
1064 case AL_SAMPLE_OFFSET:
1065 case AL_BYTE_OFFSET:
1066 case AL_CONE_INNER_ANGLE:
1067 case AL_CONE_OUTER_ANGLE:
1068 case AL_REFERENCE_DISTANCE:
1069 case AL_CONE_OUTER_GAINHF:
1070 case AL_AIR_ABSORPTION_FACTOR:
1071 case AL_ROOM_ROLLOFF_FACTOR:
1072 case AL_DIRECT_FILTER_GAINHF_AUTO:
1073 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1074 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1075 case AL_DIRECT_CHANNELS_SOFT:
1076 case AL_DISTANCE_MODEL:
1077 case AL_SOURCE_RELATIVE:
1078 case AL_LOOPING:
1079 case AL_SOURCE_STATE:
1080 case AL_BUFFERS_QUEUED:
1081 case AL_BUFFERS_PROCESSED:
1082 case AL_SOURCE_TYPE:
1083 case AL_SOURCE_RADIUS:
1084 case AL_SOURCE_RESAMPLER_SOFT:
1085 case AL_SOURCE_SPATIALIZE_SOFT:
1086 case AL_BYTE_LENGTH_SOFT:
1087 case AL_SAMPLE_LENGTH_SOFT:
1088 case AL_SEC_LENGTH_SOFT:
1089 case AL_STEREO_MODE_SOFT:
1090 case AL_SUPER_STEREO_WIDTH_SOFT:
1091 return 1;
1093 case AL_SEC_OFFSET_LATENCY_SOFT:
1094 case AL_SEC_OFFSET_CLOCK_SOFT:
1095 case AL_STEREO_ANGLES:
1096 return 2;
1098 case AL_POSITION:
1099 case AL_VELOCITY:
1100 case AL_DIRECTION:
1101 return 3;
1103 case AL_ORIENTATION:
1104 return 6;
1106 case AL_BUFFER:
1107 case AL_DIRECT_FILTER:
1108 case AL_AUXILIARY_SEND_FILTER:
1109 break; /* i/i64 only */
1110 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1111 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1112 break; /* i64 only */
1114 return 0;
1118 void SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const float> values);
1119 void SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int> values);
1120 void SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int64_t> values);
1122 #define CHECKSIZE(v, s) do { \
1123 if LIKELY((v).size() == (s) || (v).size() == MaxValues) break; \
1124 Context->setError(AL_INVALID_ENUM, \
1125 "Property 0x%04x expects %d value(s), got %zu", prop, (s), \
1126 (v).size()); \
1127 return; \
1128 } while(0)
1129 #define CHECKVAL(x) do { \
1130 if LIKELY(x) break; \
1131 Context->setError(AL_INVALID_VALUE, "Value out of range"); \
1132 return; \
1133 } while(0)
1135 void UpdateSourceProps(ALsource *source, ALCcontext *context)
1137 if(!context->mDeferUpdates)
1139 if(Voice *voice{GetSourceVoice(source, context)})
1141 UpdateSourceProps(source, voice, context);
1142 return;
1145 source->mPropsDirty = true;
1147 #ifdef ALSOFT_EAX
1148 void CommitAndUpdateSourceProps(ALsource *source, ALCcontext *context)
1150 if(!context->mDeferUpdates)
1152 if(source->eax_is_initialized())
1153 source->eax_commit();
1154 if(Voice *voice{GetSourceVoice(source, context)})
1156 UpdateSourceProps(source, voice, context);
1157 return;
1160 source->mPropsDirty = true;
1163 #else
1165 inline void CommitAndUpdateSourceProps(ALsource *source, ALCcontext *context)
1166 { UpdateSourceProps(source, context); }
1167 #endif
1170 void SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop,
1171 const al::span<const float> values)
1173 int ival;
1175 switch(prop)
1177 case AL_SEC_LENGTH_SOFT:
1178 case AL_SEC_OFFSET_LATENCY_SOFT:
1179 case AL_SEC_OFFSET_CLOCK_SOFT:
1180 /* Query only */
1181 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1182 "Setting read-only source property 0x%04x", prop);
1184 case AL_PITCH:
1185 CHECKSIZE(values, 1);
1186 CHECKVAL(values[0] >= 0.0f);
1188 Source->Pitch = values[0];
1189 return UpdateSourceProps(Source, Context);
1191 case AL_CONE_INNER_ANGLE:
1192 CHECKSIZE(values, 1);
1193 CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
1195 Source->InnerAngle = values[0];
1196 return CommitAndUpdateSourceProps(Source, Context);
1198 case AL_CONE_OUTER_ANGLE:
1199 CHECKSIZE(values, 1);
1200 CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
1202 Source->OuterAngle = values[0];
1203 return CommitAndUpdateSourceProps(Source, Context);
1205 case AL_GAIN:
1206 CHECKSIZE(values, 1);
1207 CHECKVAL(values[0] >= 0.0f);
1209 Source->Gain = values[0];
1210 return UpdateSourceProps(Source, Context);
1212 case AL_MAX_DISTANCE:
1213 CHECKSIZE(values, 1);
1214 CHECKVAL(values[0] >= 0.0f);
1216 Source->MaxDistance = values[0];
1217 return CommitAndUpdateSourceProps(Source, Context);
1219 case AL_ROLLOFF_FACTOR:
1220 CHECKSIZE(values, 1);
1221 CHECKVAL(values[0] >= 0.0f);
1223 Source->RolloffFactor = values[0];
1224 return CommitAndUpdateSourceProps(Source, Context);
1226 case AL_REFERENCE_DISTANCE:
1227 CHECKSIZE(values, 1);
1228 CHECKVAL(values[0] >= 0.0f);
1230 Source->RefDistance = values[0];
1231 return CommitAndUpdateSourceProps(Source, Context);
1233 case AL_MIN_GAIN:
1234 CHECKSIZE(values, 1);
1235 CHECKVAL(values[0] >= 0.0f);
1237 Source->MinGain = values[0];
1238 return UpdateSourceProps(Source, Context);
1240 case AL_MAX_GAIN:
1241 CHECKSIZE(values, 1);
1242 CHECKVAL(values[0] >= 0.0f);
1244 Source->MaxGain = values[0];
1245 return UpdateSourceProps(Source, Context);
1247 case AL_CONE_OUTER_GAIN:
1248 CHECKSIZE(values, 1);
1249 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1251 Source->OuterGain = values[0];
1252 return UpdateSourceProps(Source, Context);
1254 case AL_CONE_OUTER_GAINHF:
1255 CHECKSIZE(values, 1);
1256 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1258 Source->OuterGainHF = values[0];
1259 return UpdateSourceProps(Source, Context);
1261 case AL_AIR_ABSORPTION_FACTOR:
1262 CHECKSIZE(values, 1);
1263 CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
1265 Source->AirAbsorptionFactor = values[0];
1266 return UpdateSourceProps(Source, Context);
1268 case AL_ROOM_ROLLOFF_FACTOR:
1269 CHECKSIZE(values, 1);
1270 CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
1272 Source->RoomRolloffFactor = values[0];
1273 return UpdateSourceProps(Source, Context);
1275 case AL_DOPPLER_FACTOR:
1276 CHECKSIZE(values, 1);
1277 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1279 Source->DopplerFactor = values[0];
1280 return UpdateSourceProps(Source, Context);
1282 case AL_SEC_OFFSET:
1283 case AL_SAMPLE_OFFSET:
1284 case AL_BYTE_OFFSET:
1285 CHECKSIZE(values, 1);
1286 CHECKVAL(values[0] >= 0.0f);
1288 if(Voice *voice{GetSourceVoice(Source, Context)})
1290 auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
1291 if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid offset");
1293 if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mALDevice.get()))
1294 return;
1296 Source->OffsetType = prop;
1297 Source->Offset = values[0];
1298 return;
1300 case AL_SOURCE_RADIUS:
1301 CHECKSIZE(values, 1);
1302 CHECKVAL(values[0] >= 0.0f && std::isfinite(values[0]));
1304 Source->Radius = values[0];
1305 return UpdateSourceProps(Source, Context);
1307 case AL_SUPER_STEREO_WIDTH_SOFT:
1308 CHECKSIZE(values, 1);
1309 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1311 Source->EnhWidth = values[0];
1312 return UpdateSourceProps(Source, Context);
1314 case AL_STEREO_ANGLES:
1315 CHECKSIZE(values, 2);
1316 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]));
1318 Source->StereoPan[0] = values[0];
1319 Source->StereoPan[1] = values[1];
1320 return UpdateSourceProps(Source, Context);
1323 case AL_POSITION:
1324 CHECKSIZE(values, 3);
1325 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1327 Source->Position[0] = values[0];
1328 Source->Position[1] = values[1];
1329 Source->Position[2] = values[2];
1330 return CommitAndUpdateSourceProps(Source, Context);
1332 case AL_VELOCITY:
1333 CHECKSIZE(values, 3);
1334 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1336 Source->Velocity[0] = values[0];
1337 Source->Velocity[1] = values[1];
1338 Source->Velocity[2] = values[2];
1339 return CommitAndUpdateSourceProps(Source, Context);
1341 case AL_DIRECTION:
1342 CHECKSIZE(values, 3);
1343 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1345 Source->Direction[0] = values[0];
1346 Source->Direction[1] = values[1];
1347 Source->Direction[2] = values[2];
1348 return CommitAndUpdateSourceProps(Source, Context);
1350 case AL_ORIENTATION:
1351 CHECKSIZE(values, 6);
1352 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2])
1353 && std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]));
1355 Source->OrientAt[0] = values[0];
1356 Source->OrientAt[1] = values[1];
1357 Source->OrientAt[2] = values[2];
1358 Source->OrientUp[0] = values[3];
1359 Source->OrientUp[1] = values[4];
1360 Source->OrientUp[2] = values[5];
1361 return UpdateSourceProps(Source, Context);
1364 case AL_SOURCE_RELATIVE:
1365 case AL_LOOPING:
1366 case AL_SOURCE_STATE:
1367 case AL_SOURCE_TYPE:
1368 case AL_DISTANCE_MODEL:
1369 case AL_DIRECT_FILTER_GAINHF_AUTO:
1370 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1371 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1372 case AL_DIRECT_CHANNELS_SOFT:
1373 case AL_SOURCE_RESAMPLER_SOFT:
1374 case AL_SOURCE_SPATIALIZE_SOFT:
1375 case AL_BYTE_LENGTH_SOFT:
1376 case AL_SAMPLE_LENGTH_SOFT:
1377 case AL_STEREO_MODE_SOFT:
1378 CHECKSIZE(values, 1);
1379 ival = static_cast<int>(values[0]);
1380 return SetSourceiv(Source, Context, prop, {&ival, 1u});
1382 case AL_BUFFERS_QUEUED:
1383 case AL_BUFFERS_PROCESSED:
1384 CHECKSIZE(values, 1);
1385 ival = static_cast<int>(static_cast<ALuint>(values[0]));
1386 return SetSourceiv(Source, Context, prop, {&ival, 1u});
1388 case AL_BUFFER:
1389 case AL_DIRECT_FILTER:
1390 case AL_AUXILIARY_SEND_FILTER:
1391 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1392 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1393 break;
1396 ERR("Unexpected property: 0x%04x\n", prop);
1397 Context->setError(AL_INVALID_ENUM, "Invalid source float property 0x%04x", prop);
1398 return;
1401 void SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop,
1402 const al::span<const int> values)
1404 ALCdevice *device{Context->mALDevice.get()};
1405 ALeffectslot *slot{nullptr};
1406 al::deque<ALbufferQueueItem> oldlist;
1407 std::unique_lock<std::mutex> slotlock;
1408 float fvals[6];
1410 switch(prop)
1412 case AL_SOURCE_STATE:
1413 case AL_SOURCE_TYPE:
1414 case AL_BUFFERS_QUEUED:
1415 case AL_BUFFERS_PROCESSED:
1416 case AL_BYTE_LENGTH_SOFT:
1417 case AL_SAMPLE_LENGTH_SOFT:
1418 /* Query only */
1419 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1420 "Setting read-only source property 0x%04x", prop);
1422 case AL_SOURCE_RELATIVE:
1423 CHECKSIZE(values, 1);
1424 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1426 Source->HeadRelative = values[0] != AL_FALSE;
1427 return CommitAndUpdateSourceProps(Source, Context);
1429 case AL_LOOPING:
1430 CHECKSIZE(values, 1);
1431 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1433 Source->Looping = values[0] != AL_FALSE;
1434 if(Voice *voice{GetSourceVoice(Source, Context)})
1436 if(Source->Looping)
1437 voice->mLoopBuffer.store(&Source->mQueue.front(), std::memory_order_release);
1438 else
1439 voice->mLoopBuffer.store(nullptr, std::memory_order_release);
1441 /* If the source is playing, wait for the current mix to finish to
1442 * ensure it isn't currently looping back or reaching the end.
1444 device->waitForMix();
1446 return;
1448 case AL_BUFFER:
1449 CHECKSIZE(values, 1);
1451 const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
1452 if(state == AL_PLAYING || state == AL_PAUSED)
1453 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1454 "Setting buffer on playing or paused source %u", Source->id);
1456 if(values[0])
1458 std::lock_guard<std::mutex> _{device->BufferLock};
1459 ALbuffer *buffer{LookupBuffer(device, static_cast<ALuint>(values[0]))};
1460 if(!buffer)
1461 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid buffer ID %u",
1462 static_cast<ALuint>(values[0]));
1463 if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
1464 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1465 "Setting non-persistently mapped buffer %u", buffer->id);
1466 if(buffer->mCallback && ReadRef(buffer->ref) != 0)
1467 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1468 "Setting already-set callback buffer %u", buffer->id);
1470 /* Add the selected buffer to a one-item queue */
1471 al::deque<ALbufferQueueItem> newlist;
1472 newlist.emplace_back();
1473 newlist.back().mCallback = buffer->mCallback;
1474 newlist.back().mUserData = buffer->mUserData;
1475 newlist.back().mSampleLen = buffer->mSampleLen;
1476 newlist.back().mLoopStart = buffer->mLoopStart;
1477 newlist.back().mLoopEnd = buffer->mLoopEnd;
1478 newlist.back().mSamples = buffer->mData.data();
1479 newlist.back().mBuffer = buffer;
1480 IncrementRef(buffer->ref);
1482 /* Source is now Static */
1483 Source->SourceType = AL_STATIC;
1484 Source->mQueue.swap(oldlist);
1485 Source->mQueue.swap(newlist);
1487 else
1489 /* Source is now Undetermined */
1490 Source->SourceType = AL_UNDETERMINED;
1491 Source->mQueue.swap(oldlist);
1494 /* Delete all elements in the previous queue */
1495 for(auto &item : oldlist)
1497 if(ALbuffer *buffer{item.mBuffer})
1498 DecrementRef(buffer->ref);
1500 return;
1502 case AL_SEC_OFFSET:
1503 case AL_SAMPLE_OFFSET:
1504 case AL_BYTE_OFFSET:
1505 CHECKSIZE(values, 1);
1506 CHECKVAL(values[0] >= 0);
1508 if(Voice *voice{GetSourceVoice(Source, Context)})
1510 auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
1511 if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid source offset");
1513 if(SetVoiceOffset(voice, *vpos, Source, Context, device))
1514 return;
1516 Source->OffsetType = prop;
1517 Source->Offset = values[0];
1518 return;
1520 case AL_DIRECT_FILTER:
1521 CHECKSIZE(values, 1);
1522 if(values[0])
1524 std::lock_guard<std::mutex> _{device->FilterLock};
1525 ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[0]))};
1526 if(!filter)
1527 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid filter ID %u",
1528 static_cast<ALuint>(values[0]));
1529 Source->Direct.Gain = filter->Gain;
1530 Source->Direct.GainHF = filter->GainHF;
1531 Source->Direct.HFReference = filter->HFReference;
1532 Source->Direct.GainLF = filter->GainLF;
1533 Source->Direct.LFReference = filter->LFReference;
1535 else
1537 Source->Direct.Gain = 1.0f;
1538 Source->Direct.GainHF = 1.0f;
1539 Source->Direct.HFReference = LOWPASSFREQREF;
1540 Source->Direct.GainLF = 1.0f;
1541 Source->Direct.LFReference = HIGHPASSFREQREF;
1543 return UpdateSourceProps(Source, Context);
1545 case AL_DIRECT_FILTER_GAINHF_AUTO:
1546 CHECKSIZE(values, 1);
1547 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1549 Source->DryGainHFAuto = values[0] != AL_FALSE;
1550 return UpdateSourceProps(Source, Context);
1552 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1553 CHECKSIZE(values, 1);
1554 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1556 Source->WetGainAuto = values[0] != AL_FALSE;
1557 return UpdateSourceProps(Source, Context);
1559 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1560 CHECKSIZE(values, 1);
1561 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1563 Source->WetGainHFAuto = values[0] != AL_FALSE;
1564 return UpdateSourceProps(Source, Context);
1566 case AL_DIRECT_CHANNELS_SOFT:
1567 CHECKSIZE(values, 1);
1568 if(auto mode = DirectModeFromEnum(values[0]))
1570 Source->DirectChannels = *mode;
1571 return UpdateSourceProps(Source, Context);
1573 Context->setError(AL_INVALID_VALUE, "Unsupported AL_DIRECT_CHANNELS_SOFT: 0x%04x\n",
1574 values[0]);
1575 return;
1577 case AL_DISTANCE_MODEL:
1578 CHECKSIZE(values, 1);
1579 if(auto model = DistanceModelFromALenum(values[0]))
1581 Source->mDistanceModel = *model;
1582 if(Context->mSourceDistanceModel)
1583 UpdateSourceProps(Source, Context);
1584 return;
1586 Context->setError(AL_INVALID_VALUE, "Distance model out of range: 0x%04x", values[0]);
1587 return;
1589 case AL_SOURCE_RESAMPLER_SOFT:
1590 CHECKSIZE(values, 1);
1591 CHECKVAL(values[0] >= 0 && values[0] <= static_cast<int>(Resampler::Max));
1593 Source->mResampler = static_cast<Resampler>(values[0]);
1594 return UpdateSourceProps(Source, Context);
1596 case AL_SOURCE_SPATIALIZE_SOFT:
1597 CHECKSIZE(values, 1);
1598 if(auto mode = SpatializeModeFromEnum(values[0]))
1600 Source->mSpatialize = *mode;
1601 return UpdateSourceProps(Source, Context);
1603 Context->setError(AL_INVALID_VALUE, "Unsupported AL_SOURCE_SPATIALIZE_SOFT: 0x%04x\n",
1604 values[0]);
1605 return;
1607 case AL_STEREO_MODE_SOFT:
1608 CHECKSIZE(values, 1);
1610 const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
1611 if(state == AL_PLAYING || state == AL_PAUSED)
1612 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1613 "Modifying stereo mode on playing or paused source %u", Source->id);
1615 if(auto mode = StereoModeFromEnum(values[0]))
1617 Source->mStereoMode = *mode;
1618 return;
1620 Context->setError(AL_INVALID_VALUE, "Unsupported AL_STEREO_MODE_SOFT: 0x%04x\n",
1621 values[0]);
1622 return;
1624 case AL_AUXILIARY_SEND_FILTER:
1625 CHECKSIZE(values, 3);
1626 slotlock = std::unique_lock<std::mutex>{Context->mEffectSlotLock};
1627 if(values[0] && (slot=LookupEffectSlot(Context, static_cast<ALuint>(values[0]))) == nullptr)
1628 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid effect ID %u", values[0]);
1629 if(static_cast<ALuint>(values[1]) >= device->NumAuxSends)
1630 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid send %u", values[1]);
1632 if(values[2])
1634 std::lock_guard<std::mutex> _{device->FilterLock};
1635 ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[2]))};
1636 if(!filter)
1637 SETERR_RETURN(Context, AL_INVALID_VALUE,, "Invalid filter ID %u", values[2]);
1639 auto &send = Source->Send[static_cast<ALuint>(values[1])];
1640 send.Gain = filter->Gain;
1641 send.GainHF = filter->GainHF;
1642 send.HFReference = filter->HFReference;
1643 send.GainLF = filter->GainLF;
1644 send.LFReference = filter->LFReference;
1646 else
1648 /* Disable filter */
1649 auto &send = Source->Send[static_cast<ALuint>(values[1])];
1650 send.Gain = 1.0f;
1651 send.GainHF = 1.0f;
1652 send.HFReference = LOWPASSFREQREF;
1653 send.GainLF = 1.0f;
1654 send.LFReference = HIGHPASSFREQREF;
1657 if(slot != Source->Send[static_cast<ALuint>(values[1])].Slot && IsPlayingOrPaused(Source))
1659 /* Add refcount on the new slot, and release the previous slot */
1660 if(slot) IncrementRef(slot->ref);
1661 if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
1662 DecrementRef(oldslot->ref);
1663 Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
1665 /* We must force an update if the auxiliary slot changed on an
1666 * active source, in case the slot is about to be deleted.
1668 Voice *voice{GetSourceVoice(Source, Context)};
1669 if(voice) UpdateSourceProps(Source, voice, Context);
1670 else Source->mPropsDirty = true;
1672 else
1674 if(slot) IncrementRef(slot->ref);
1675 if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
1676 DecrementRef(oldslot->ref);
1677 Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
1678 UpdateSourceProps(Source, Context);
1680 return;
1683 /* 1x float */
1684 case AL_CONE_INNER_ANGLE:
1685 case AL_CONE_OUTER_ANGLE:
1686 case AL_PITCH:
1687 case AL_GAIN:
1688 case AL_MIN_GAIN:
1689 case AL_MAX_GAIN:
1690 case AL_REFERENCE_DISTANCE:
1691 case AL_ROLLOFF_FACTOR:
1692 case AL_CONE_OUTER_GAIN:
1693 case AL_MAX_DISTANCE:
1694 case AL_DOPPLER_FACTOR:
1695 case AL_CONE_OUTER_GAINHF:
1696 case AL_AIR_ABSORPTION_FACTOR:
1697 case AL_ROOM_ROLLOFF_FACTOR:
1698 case AL_SOURCE_RADIUS:
1699 case AL_SEC_LENGTH_SOFT:
1700 case AL_SUPER_STEREO_WIDTH_SOFT:
1701 CHECKSIZE(values, 1);
1702 fvals[0] = static_cast<float>(values[0]);
1703 return SetSourcefv(Source, Context, prop, {fvals, 1u});
1705 /* 3x float */
1706 case AL_POSITION:
1707 case AL_VELOCITY:
1708 case AL_DIRECTION:
1709 CHECKSIZE(values, 3);
1710 fvals[0] = static_cast<float>(values[0]);
1711 fvals[1] = static_cast<float>(values[1]);
1712 fvals[2] = static_cast<float>(values[2]);
1713 return SetSourcefv(Source, Context, prop, {fvals, 3u});
1715 /* 6x float */
1716 case AL_ORIENTATION:
1717 CHECKSIZE(values, 6);
1718 fvals[0] = static_cast<float>(values[0]);
1719 fvals[1] = static_cast<float>(values[1]);
1720 fvals[2] = static_cast<float>(values[2]);
1721 fvals[3] = static_cast<float>(values[3]);
1722 fvals[4] = static_cast<float>(values[4]);
1723 fvals[5] = static_cast<float>(values[5]);
1724 return SetSourcefv(Source, Context, prop, {fvals, 6u});
1726 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1727 case AL_SEC_OFFSET_LATENCY_SOFT:
1728 case AL_SEC_OFFSET_CLOCK_SOFT:
1729 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1730 case AL_STEREO_ANGLES:
1731 break;
1734 ERR("Unexpected property: 0x%04x\n", prop);
1735 Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
1736 return;
1739 void SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop,
1740 const al::span<const int64_t> values)
1742 float fvals[MaxValues];
1743 int ivals[MaxValues];
1745 switch(prop)
1747 case AL_SOURCE_TYPE:
1748 case AL_BUFFERS_QUEUED:
1749 case AL_BUFFERS_PROCESSED:
1750 case AL_SOURCE_STATE:
1751 case AL_BYTE_LENGTH_SOFT:
1752 case AL_SAMPLE_LENGTH_SOFT:
1753 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1754 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1755 /* Query only */
1756 SETERR_RETURN(Context, AL_INVALID_OPERATION,,
1757 "Setting read-only source property 0x%04x", prop);
1759 /* 1x int */
1760 case AL_SOURCE_RELATIVE:
1761 case AL_LOOPING:
1762 case AL_SEC_OFFSET:
1763 case AL_SAMPLE_OFFSET:
1764 case AL_BYTE_OFFSET:
1765 case AL_DIRECT_FILTER_GAINHF_AUTO:
1766 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1767 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1768 case AL_DIRECT_CHANNELS_SOFT:
1769 case AL_DISTANCE_MODEL:
1770 case AL_SOURCE_RESAMPLER_SOFT:
1771 case AL_SOURCE_SPATIALIZE_SOFT:
1772 case AL_STEREO_MODE_SOFT:
1773 CHECKSIZE(values, 1);
1774 CHECKVAL(values[0] <= INT_MAX && values[0] >= INT_MIN);
1776 ivals[0] = static_cast<int>(values[0]);
1777 return SetSourceiv(Source, Context, prop, {ivals, 1u});
1779 /* 1x uint */
1780 case AL_BUFFER:
1781 case AL_DIRECT_FILTER:
1782 CHECKSIZE(values, 1);
1783 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0);
1785 ivals[0] = static_cast<int>(values[0]);
1786 return SetSourceiv(Source, Context, prop, {ivals, 1u});
1788 /* 3x uint */
1789 case AL_AUXILIARY_SEND_FILTER:
1790 CHECKSIZE(values, 3);
1791 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 && values[1] <= UINT_MAX && values[1] >= 0
1792 && values[2] <= UINT_MAX && values[2] >= 0);
1794 ivals[0] = static_cast<int>(values[0]);
1795 ivals[1] = static_cast<int>(values[1]);
1796 ivals[2] = static_cast<int>(values[2]);
1797 return SetSourceiv(Source, Context, prop, {ivals, 3u});
1799 /* 1x float */
1800 case AL_CONE_INNER_ANGLE:
1801 case AL_CONE_OUTER_ANGLE:
1802 case AL_PITCH:
1803 case AL_GAIN:
1804 case AL_MIN_GAIN:
1805 case AL_MAX_GAIN:
1806 case AL_REFERENCE_DISTANCE:
1807 case AL_ROLLOFF_FACTOR:
1808 case AL_CONE_OUTER_GAIN:
1809 case AL_MAX_DISTANCE:
1810 case AL_DOPPLER_FACTOR:
1811 case AL_CONE_OUTER_GAINHF:
1812 case AL_AIR_ABSORPTION_FACTOR:
1813 case AL_ROOM_ROLLOFF_FACTOR:
1814 case AL_SOURCE_RADIUS:
1815 case AL_SEC_LENGTH_SOFT:
1816 case AL_SUPER_STEREO_WIDTH_SOFT:
1817 CHECKSIZE(values, 1);
1818 fvals[0] = static_cast<float>(values[0]);
1819 return SetSourcefv(Source, Context, prop, {fvals, 1u});
1821 /* 3x float */
1822 case AL_POSITION:
1823 case AL_VELOCITY:
1824 case AL_DIRECTION:
1825 CHECKSIZE(values, 3);
1826 fvals[0] = static_cast<float>(values[0]);
1827 fvals[1] = static_cast<float>(values[1]);
1828 fvals[2] = static_cast<float>(values[2]);
1829 return SetSourcefv(Source, Context, prop, {fvals, 3u});
1831 /* 6x float */
1832 case AL_ORIENTATION:
1833 CHECKSIZE(values, 6);
1834 fvals[0] = static_cast<float>(values[0]);
1835 fvals[1] = static_cast<float>(values[1]);
1836 fvals[2] = static_cast<float>(values[2]);
1837 fvals[3] = static_cast<float>(values[3]);
1838 fvals[4] = static_cast<float>(values[4]);
1839 fvals[5] = static_cast<float>(values[5]);
1840 return SetSourcefv(Source, Context, prop, {fvals, 6u});
1842 case AL_SEC_OFFSET_LATENCY_SOFT:
1843 case AL_SEC_OFFSET_CLOCK_SOFT:
1844 case AL_STEREO_ANGLES:
1845 break;
1848 ERR("Unexpected property: 0x%04x\n", prop);
1849 Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
1850 return;
1853 #undef CHECKVAL
1854 #undef CHECKSIZE
1856 #define CHECKSIZE(v, s) do { \
1857 if LIKELY((v).size() == (s) || (v).size() == MaxValues) break; \
1858 Context->setError(AL_INVALID_ENUM, \
1859 "Property 0x%04x expects %d value(s), got %zu", prop, (s), \
1860 (v).size()); \
1861 return false; \
1862 } while(0)
1864 bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values);
1865 bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int> values);
1866 bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values);
1868 bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values)
1870 ALCdevice *device{Context->mALDevice.get()};
1871 ClockLatency clocktime;
1872 nanoseconds srcclock;
1873 int ivals[MaxValues];
1874 bool err;
1876 switch(prop)
1878 case AL_GAIN:
1879 CHECKSIZE(values, 1);
1880 values[0] = Source->Gain;
1881 return true;
1883 case AL_PITCH:
1884 CHECKSIZE(values, 1);
1885 values[0] = Source->Pitch;
1886 return true;
1888 case AL_MAX_DISTANCE:
1889 CHECKSIZE(values, 1);
1890 values[0] = Source->MaxDistance;
1891 return true;
1893 case AL_ROLLOFF_FACTOR:
1894 CHECKSIZE(values, 1);
1895 values[0] = Source->RolloffFactor;
1896 return true;
1898 case AL_REFERENCE_DISTANCE:
1899 CHECKSIZE(values, 1);
1900 values[0] = Source->RefDistance;
1901 return true;
1903 case AL_CONE_INNER_ANGLE:
1904 CHECKSIZE(values, 1);
1905 values[0] = Source->InnerAngle;
1906 return true;
1908 case AL_CONE_OUTER_ANGLE:
1909 CHECKSIZE(values, 1);
1910 values[0] = Source->OuterAngle;
1911 return true;
1913 case AL_MIN_GAIN:
1914 CHECKSIZE(values, 1);
1915 values[0] = Source->MinGain;
1916 return true;
1918 case AL_MAX_GAIN:
1919 CHECKSIZE(values, 1);
1920 values[0] = Source->MaxGain;
1921 return true;
1923 case AL_CONE_OUTER_GAIN:
1924 CHECKSIZE(values, 1);
1925 values[0] = Source->OuterGain;
1926 return true;
1928 case AL_SEC_OFFSET:
1929 case AL_SAMPLE_OFFSET:
1930 case AL_BYTE_OFFSET:
1931 CHECKSIZE(values, 1);
1932 values[0] = GetSourceOffset(Source, prop, Context);
1933 return true;
1935 case AL_CONE_OUTER_GAINHF:
1936 CHECKSIZE(values, 1);
1937 values[0] = Source->OuterGainHF;
1938 return true;
1940 case AL_AIR_ABSORPTION_FACTOR:
1941 CHECKSIZE(values, 1);
1942 values[0] = Source->AirAbsorptionFactor;
1943 return true;
1945 case AL_ROOM_ROLLOFF_FACTOR:
1946 CHECKSIZE(values, 1);
1947 values[0] = Source->RoomRolloffFactor;
1948 return true;
1950 case AL_DOPPLER_FACTOR:
1951 CHECKSIZE(values, 1);
1952 values[0] = Source->DopplerFactor;
1953 return true;
1955 case AL_SOURCE_RADIUS:
1956 CHECKSIZE(values, 1);
1957 values[0] = Source->Radius;
1958 return true;
1960 case AL_SUPER_STEREO_WIDTH_SOFT:
1961 CHECKSIZE(values, 1);
1962 values[0] = Source->EnhWidth;
1963 return true;
1965 case AL_BYTE_LENGTH_SOFT:
1966 case AL_SAMPLE_LENGTH_SOFT:
1967 case AL_SEC_LENGTH_SOFT:
1968 CHECKSIZE(values, 1);
1969 values[0] = GetSourceLength(Source, prop);
1970 return true;
1972 case AL_STEREO_ANGLES:
1973 CHECKSIZE(values, 2);
1974 values[0] = Source->StereoPan[0];
1975 values[1] = Source->StereoPan[1];
1976 return true;
1978 case AL_SEC_OFFSET_LATENCY_SOFT:
1979 CHECKSIZE(values, 2);
1980 /* Get the source offset with the clock time first. Then get the clock
1981 * time with the device latency. Order is important.
1983 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1985 std::lock_guard<std::mutex> _{device->StateLock};
1986 clocktime = GetClockLatency(device, device->Backend.get());
1988 if(srcclock == clocktime.ClockTime)
1989 values[1] = static_cast<double>(clocktime.Latency.count()) / 1000000000.0;
1990 else
1992 /* If the clock time incremented, reduce the latency by that much
1993 * since it's that much closer to the source offset it got earlier.
1995 const nanoseconds diff{clocktime.ClockTime - srcclock};
1996 const nanoseconds latency{clocktime.Latency - std::min(clocktime.Latency, diff)};
1997 values[1] = static_cast<double>(latency.count()) / 1000000000.0;
1999 return true;
2001 case AL_SEC_OFFSET_CLOCK_SOFT:
2002 CHECKSIZE(values, 2);
2003 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
2004 values[1] = static_cast<double>(srcclock.count()) / 1000000000.0;
2005 return true;
2007 case AL_POSITION:
2008 CHECKSIZE(values, 3);
2009 values[0] = Source->Position[0];
2010 values[1] = Source->Position[1];
2011 values[2] = Source->Position[2];
2012 return true;
2014 case AL_VELOCITY:
2015 CHECKSIZE(values, 3);
2016 values[0] = Source->Velocity[0];
2017 values[1] = Source->Velocity[1];
2018 values[2] = Source->Velocity[2];
2019 return true;
2021 case AL_DIRECTION:
2022 CHECKSIZE(values, 3);
2023 values[0] = Source->Direction[0];
2024 values[1] = Source->Direction[1];
2025 values[2] = Source->Direction[2];
2026 return true;
2028 case AL_ORIENTATION:
2029 CHECKSIZE(values, 6);
2030 values[0] = Source->OrientAt[0];
2031 values[1] = Source->OrientAt[1];
2032 values[2] = Source->OrientAt[2];
2033 values[3] = Source->OrientUp[0];
2034 values[4] = Source->OrientUp[1];
2035 values[5] = Source->OrientUp[2];
2036 return true;
2038 /* 1x int */
2039 case AL_SOURCE_RELATIVE:
2040 case AL_LOOPING:
2041 case AL_SOURCE_STATE:
2042 case AL_BUFFERS_QUEUED:
2043 case AL_BUFFERS_PROCESSED:
2044 case AL_SOURCE_TYPE:
2045 case AL_DIRECT_FILTER_GAINHF_AUTO:
2046 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2047 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2048 case AL_DIRECT_CHANNELS_SOFT:
2049 case AL_DISTANCE_MODEL:
2050 case AL_SOURCE_RESAMPLER_SOFT:
2051 case AL_SOURCE_SPATIALIZE_SOFT:
2052 case AL_STEREO_MODE_SOFT:
2053 CHECKSIZE(values, 1);
2054 if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2055 values[0] = static_cast<double>(ivals[0]);
2056 return err;
2058 case AL_BUFFER:
2059 case AL_DIRECT_FILTER:
2060 case AL_AUXILIARY_SEND_FILTER:
2061 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
2062 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
2063 break;
2066 ERR("Unexpected property: 0x%04x\n", prop);
2067 Context->setError(AL_INVALID_ENUM, "Invalid source double property 0x%04x", prop);
2068 return false;
2071 bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int> values)
2073 double dvals[MaxValues];
2074 bool err;
2076 switch(prop)
2078 case AL_SOURCE_RELATIVE:
2079 CHECKSIZE(values, 1);
2080 values[0] = Source->HeadRelative;
2081 return true;
2083 case AL_LOOPING:
2084 CHECKSIZE(values, 1);
2085 values[0] = Source->Looping;
2086 return true;
2088 case AL_BUFFER:
2089 CHECKSIZE(values, 1);
2091 ALbufferQueueItem *BufferList{(Source->SourceType == AL_STATIC)
2092 ? &Source->mQueue.front() : nullptr};
2093 ALbuffer *buffer{BufferList ? BufferList->mBuffer : nullptr};
2094 values[0] = buffer ? static_cast<int>(buffer->id) : 0;
2096 return true;
2098 case AL_SOURCE_STATE:
2099 CHECKSIZE(values, 1);
2100 values[0] = GetSourceState(Source, GetSourceVoice(Source, Context));
2101 return true;
2103 case AL_BUFFERS_QUEUED:
2104 CHECKSIZE(values, 1);
2105 values[0] = static_cast<int>(Source->mQueue.size());
2106 return true;
2108 case AL_BUFFERS_PROCESSED:
2109 CHECKSIZE(values, 1);
2110 if(Source->Looping || Source->SourceType != AL_STREAMING)
2112 /* Buffers on a looping source are in a perpetual state of PENDING,
2113 * so don't report any as PROCESSED
2115 values[0] = 0;
2117 else
2119 int played{0};
2120 if(Source->state != AL_INITIAL)
2122 const VoiceBufferItem *Current{nullptr};
2123 if(Voice *voice{GetSourceVoice(Source, Context)})
2124 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
2125 for(auto &item : Source->mQueue)
2127 if(&item == Current)
2128 break;
2129 ++played;
2132 values[0] = played;
2134 return true;
2136 case AL_SOURCE_TYPE:
2137 CHECKSIZE(values, 1);
2138 values[0] = Source->SourceType;
2139 return true;
2141 case AL_DIRECT_FILTER_GAINHF_AUTO:
2142 CHECKSIZE(values, 1);
2143 values[0] = Source->DryGainHFAuto;
2144 return true;
2146 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2147 CHECKSIZE(values, 1);
2148 values[0] = Source->WetGainAuto;
2149 return true;
2151 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2152 CHECKSIZE(values, 1);
2153 values[0] = Source->WetGainHFAuto;
2154 return true;
2156 case AL_DIRECT_CHANNELS_SOFT:
2157 CHECKSIZE(values, 1);
2158 values[0] = EnumFromDirectMode(Source->DirectChannels);
2159 return true;
2161 case AL_DISTANCE_MODEL:
2162 CHECKSIZE(values, 1);
2163 values[0] = ALenumFromDistanceModel(Source->mDistanceModel);
2164 return true;
2166 case AL_BYTE_LENGTH_SOFT:
2167 case AL_SAMPLE_LENGTH_SOFT:
2168 case AL_SEC_LENGTH_SOFT:
2169 CHECKSIZE(values, 1);
2170 values[0] = static_cast<int>(mind(GetSourceLength(Source, prop),
2171 std::numeric_limits<int>::max()));
2172 return true;
2174 case AL_SOURCE_RESAMPLER_SOFT:
2175 CHECKSIZE(values, 1);
2176 values[0] = static_cast<int>(Source->mResampler);
2177 return true;
2179 case AL_SOURCE_SPATIALIZE_SOFT:
2180 CHECKSIZE(values, 1);
2181 values[0] = EnumFromSpatializeMode(Source->mSpatialize);
2182 return true;
2184 case AL_STEREO_MODE_SOFT:
2185 CHECKSIZE(values, 1);
2186 values[0] = EnumFromStereoMode(Source->mStereoMode);
2187 return true;
2189 /* 1x float/double */
2190 case AL_CONE_INNER_ANGLE:
2191 case AL_CONE_OUTER_ANGLE:
2192 case AL_PITCH:
2193 case AL_GAIN:
2194 case AL_MIN_GAIN:
2195 case AL_MAX_GAIN:
2196 case AL_REFERENCE_DISTANCE:
2197 case AL_ROLLOFF_FACTOR:
2198 case AL_CONE_OUTER_GAIN:
2199 case AL_MAX_DISTANCE:
2200 case AL_SEC_OFFSET:
2201 case AL_SAMPLE_OFFSET:
2202 case AL_BYTE_OFFSET:
2203 case AL_DOPPLER_FACTOR:
2204 case AL_AIR_ABSORPTION_FACTOR:
2205 case AL_ROOM_ROLLOFF_FACTOR:
2206 case AL_CONE_OUTER_GAINHF:
2207 case AL_SOURCE_RADIUS:
2208 case AL_SUPER_STEREO_WIDTH_SOFT:
2209 CHECKSIZE(values, 1);
2210 if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
2211 values[0] = static_cast<int>(dvals[0]);
2212 return err;
2214 /* 3x float/double */
2215 case AL_POSITION:
2216 case AL_VELOCITY:
2217 case AL_DIRECTION:
2218 CHECKSIZE(values, 3);
2219 if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
2221 values[0] = static_cast<int>(dvals[0]);
2222 values[1] = static_cast<int>(dvals[1]);
2223 values[2] = static_cast<int>(dvals[2]);
2225 return err;
2227 /* 6x float/double */
2228 case AL_ORIENTATION:
2229 CHECKSIZE(values, 6);
2230 if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
2232 values[0] = static_cast<int>(dvals[0]);
2233 values[1] = static_cast<int>(dvals[1]);
2234 values[2] = static_cast<int>(dvals[2]);
2235 values[3] = static_cast<int>(dvals[3]);
2236 values[4] = static_cast<int>(dvals[4]);
2237 values[5] = static_cast<int>(dvals[5]);
2239 return err;
2241 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
2242 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
2243 break; /* i64 only */
2244 case AL_SEC_OFFSET_LATENCY_SOFT:
2245 case AL_SEC_OFFSET_CLOCK_SOFT:
2246 break; /* Double only */
2247 case AL_STEREO_ANGLES:
2248 break; /* Float/double only */
2250 case AL_DIRECT_FILTER:
2251 case AL_AUXILIARY_SEND_FILTER:
2252 break; /* ??? */
2255 ERR("Unexpected property: 0x%04x\n", prop);
2256 Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
2257 return false;
2260 bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values)
2262 ALCdevice *device{Context->mALDevice.get()};
2263 ClockLatency clocktime;
2264 nanoseconds srcclock;
2265 double dvals[MaxValues];
2266 int ivals[MaxValues];
2267 bool err;
2269 switch(prop)
2271 case AL_BYTE_LENGTH_SOFT:
2272 case AL_SAMPLE_LENGTH_SOFT:
2273 case AL_SEC_LENGTH_SOFT:
2274 CHECKSIZE(values, 1);
2275 values[0] = static_cast<int64_t>(GetSourceLength(Source, prop));
2276 return true;
2278 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
2279 CHECKSIZE(values, 2);
2280 /* Get the source offset with the clock time first. Then get the clock
2281 * time with the device latency. Order is important.
2283 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
2285 std::lock_guard<std::mutex> _{device->StateLock};
2286 clocktime = GetClockLatency(device, device->Backend.get());
2288 if(srcclock == clocktime.ClockTime)
2289 values[1] = clocktime.Latency.count();
2290 else
2292 /* If the clock time incremented, reduce the latency by that much
2293 * since it's that much closer to the source offset it got earlier.
2295 const nanoseconds diff{clocktime.ClockTime - srcclock};
2296 values[1] = nanoseconds{clocktime.Latency - std::min(clocktime.Latency, diff)}.count();
2298 return true;
2300 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
2301 CHECKSIZE(values, 2);
2302 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
2303 values[1] = srcclock.count();
2304 return true;
2306 /* 1x float/double */
2307 case AL_CONE_INNER_ANGLE:
2308 case AL_CONE_OUTER_ANGLE:
2309 case AL_PITCH:
2310 case AL_GAIN:
2311 case AL_MIN_GAIN:
2312 case AL_MAX_GAIN:
2313 case AL_REFERENCE_DISTANCE:
2314 case AL_ROLLOFF_FACTOR:
2315 case AL_CONE_OUTER_GAIN:
2316 case AL_MAX_DISTANCE:
2317 case AL_SEC_OFFSET:
2318 case AL_SAMPLE_OFFSET:
2319 case AL_BYTE_OFFSET:
2320 case AL_DOPPLER_FACTOR:
2321 case AL_AIR_ABSORPTION_FACTOR:
2322 case AL_ROOM_ROLLOFF_FACTOR:
2323 case AL_CONE_OUTER_GAINHF:
2324 case AL_SOURCE_RADIUS:
2325 case AL_SUPER_STEREO_WIDTH_SOFT:
2326 CHECKSIZE(values, 1);
2327 if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
2328 values[0] = static_cast<int64_t>(dvals[0]);
2329 return err;
2331 /* 3x float/double */
2332 case AL_POSITION:
2333 case AL_VELOCITY:
2334 case AL_DIRECTION:
2335 CHECKSIZE(values, 3);
2336 if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
2338 values[0] = static_cast<int64_t>(dvals[0]);
2339 values[1] = static_cast<int64_t>(dvals[1]);
2340 values[2] = static_cast<int64_t>(dvals[2]);
2342 return err;
2344 /* 6x float/double */
2345 case AL_ORIENTATION:
2346 CHECKSIZE(values, 6);
2347 if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
2349 values[0] = static_cast<int64_t>(dvals[0]);
2350 values[1] = static_cast<int64_t>(dvals[1]);
2351 values[2] = static_cast<int64_t>(dvals[2]);
2352 values[3] = static_cast<int64_t>(dvals[3]);
2353 values[4] = static_cast<int64_t>(dvals[4]);
2354 values[5] = static_cast<int64_t>(dvals[5]);
2356 return err;
2358 /* 1x int */
2359 case AL_SOURCE_RELATIVE:
2360 case AL_LOOPING:
2361 case AL_SOURCE_STATE:
2362 case AL_BUFFERS_QUEUED:
2363 case AL_BUFFERS_PROCESSED:
2364 case AL_SOURCE_TYPE:
2365 case AL_DIRECT_FILTER_GAINHF_AUTO:
2366 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2367 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2368 case AL_DIRECT_CHANNELS_SOFT:
2369 case AL_DISTANCE_MODEL:
2370 case AL_SOURCE_RESAMPLER_SOFT:
2371 case AL_SOURCE_SPATIALIZE_SOFT:
2372 case AL_STEREO_MODE_SOFT:
2373 CHECKSIZE(values, 1);
2374 if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2375 values[0] = ivals[0];
2376 return err;
2378 /* 1x uint */
2379 case AL_BUFFER:
2380 case AL_DIRECT_FILTER:
2381 CHECKSIZE(values, 1);
2382 if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2383 values[0] = static_cast<ALuint>(ivals[0]);
2384 return err;
2386 /* 3x uint */
2387 case AL_AUXILIARY_SEND_FILTER:
2388 CHECKSIZE(values, 3);
2389 if((err=GetSourceiv(Source, Context, prop, {ivals, 3u})) != false)
2391 values[0] = static_cast<ALuint>(ivals[0]);
2392 values[1] = static_cast<ALuint>(ivals[1]);
2393 values[2] = static_cast<ALuint>(ivals[2]);
2395 return err;
2397 case AL_SEC_OFFSET_LATENCY_SOFT:
2398 case AL_SEC_OFFSET_CLOCK_SOFT:
2399 break; /* Double only */
2400 case AL_STEREO_ANGLES:
2401 break; /* Float/double only */
2404 ERR("Unexpected property: 0x%04x\n", prop);
2405 Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
2406 return false;
2409 } // namespace
2411 AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
2412 START_API_FUNC
2414 ContextRef context{GetContextRef()};
2415 if UNLIKELY(!context) return;
2417 if UNLIKELY(n < 0)
2418 context->setError(AL_INVALID_VALUE, "Generating %d sources", n);
2419 if UNLIKELY(n <= 0) return;
2421 #ifdef ALSOFT_EAX
2422 const bool has_eax{context->has_eax()};
2423 std::unique_lock<std::mutex> proplock{};
2424 if(has_eax)
2425 proplock = std::unique_lock<std::mutex>{context->mPropLock};
2426 #endif
2427 std::unique_lock<std::mutex> srclock{context->mSourceLock};
2428 ALCdevice *device{context->mALDevice.get()};
2429 if(static_cast<ALuint>(n) > device->SourcesMax-context->mNumSources)
2431 context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)",
2432 device->SourcesMax, context->mNumSources, n);
2433 return;
2435 if(!EnsureSources(context.get(), static_cast<ALuint>(n)))
2437 context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, (n==1)?"":"s");
2438 return;
2441 if(n == 1)
2443 ALsource *source{AllocSource(context.get())};
2444 sources[0] = source->id;
2446 #ifdef ALSOFT_EAX
2447 if(has_eax)
2448 source->eax_initialize(context.get());
2449 #endif // ALSOFT_EAX
2451 else
2453 #ifdef ALSOFT_EAX
2454 auto eax_sources = al::vector<ALsource*>{};
2455 if(has_eax)
2456 eax_sources.reserve(static_cast<ALuint>(n));
2457 #endif // ALSOFT_EAX
2459 al::vector<ALuint> ids;
2460 ids.reserve(static_cast<ALuint>(n));
2461 do {
2462 ALsource *source{AllocSource(context.get())};
2463 ids.emplace_back(source->id);
2465 #ifdef ALSOFT_EAX
2466 if(has_eax)
2467 eax_sources.emplace_back(source);
2468 #endif // ALSOFT_EAX
2469 } while(--n);
2470 std::copy(ids.cbegin(), ids.cend(), sources);
2472 #ifdef ALSOFT_EAX
2473 for(auto& eax_source : eax_sources)
2474 eax_source->eax_initialize(context.get());
2475 #endif // ALSOFT_EAX
2478 END_API_FUNC
2480 AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
2481 START_API_FUNC
2483 ContextRef context{GetContextRef()};
2484 if UNLIKELY(!context) return;
2486 if UNLIKELY(n < 0)
2487 SETERR_RETURN(context, AL_INVALID_VALUE,, "Deleting %d sources", n);
2489 std::lock_guard<std::mutex> _{context->mSourceLock};
2491 /* Check that all Sources are valid */
2492 auto validate_source = [&context](const ALuint sid) -> bool
2493 { return LookupSource(context.get(), sid) != nullptr; };
2495 const ALuint *sources_end = sources + n;
2496 auto invsrc = std::find_if_not(sources, sources_end, validate_source);
2497 if UNLIKELY(invsrc != sources_end)
2499 context->setError(AL_INVALID_NAME, "Invalid source ID %u", *invsrc);
2500 return;
2503 /* All good. Delete source IDs. */
2504 auto delete_source = [&context](const ALuint sid) -> void
2506 ALsource *src{LookupSource(context.get(), sid)};
2507 if(src) FreeSource(context.get(), src);
2509 std::for_each(sources, sources_end, delete_source);
2511 END_API_FUNC
2513 AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
2514 START_API_FUNC
2516 ContextRef context{GetContextRef()};
2517 if LIKELY(context)
2519 std::lock_guard<std::mutex> _{context->mSourceLock};
2520 if(LookupSource(context.get(), source) != nullptr)
2521 return AL_TRUE;
2523 return AL_FALSE;
2525 END_API_FUNC
2528 AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
2529 START_API_FUNC
2531 ContextRef context{GetContextRef()};
2532 if UNLIKELY(!context) return;
2534 std::lock_guard<std::mutex> _{context->mPropLock};
2535 std::lock_guard<std::mutex> __{context->mSourceLock};
2536 ALsource *Source = LookupSource(context.get(), source);
2537 if UNLIKELY(!Source)
2538 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2539 else
2540 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2542 END_API_FUNC
2544 AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
2545 START_API_FUNC
2547 ContextRef context{GetContextRef()};
2548 if UNLIKELY(!context) return;
2550 std::lock_guard<std::mutex> _{context->mPropLock};
2551 std::lock_guard<std::mutex> __{context->mSourceLock};
2552 ALsource *Source = LookupSource(context.get(), source);
2553 if UNLIKELY(!Source)
2554 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2555 else
2557 const float fvals[3]{ value1, value2, value3 };
2558 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2561 END_API_FUNC
2563 AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
2564 START_API_FUNC
2566 ContextRef context{GetContextRef()};
2567 if UNLIKELY(!context) return;
2569 std::lock_guard<std::mutex> _{context->mPropLock};
2570 std::lock_guard<std::mutex> __{context->mSourceLock};
2571 ALsource *Source = LookupSource(context.get(), source);
2572 if UNLIKELY(!Source)
2573 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2574 else if UNLIKELY(!values)
2575 context->setError(AL_INVALID_VALUE, "NULL pointer");
2576 else
2577 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2579 END_API_FUNC
2582 AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
2583 START_API_FUNC
2585 ContextRef context{GetContextRef()};
2586 if UNLIKELY(!context) return;
2588 std::lock_guard<std::mutex> _{context->mPropLock};
2589 std::lock_guard<std::mutex> __{context->mSourceLock};
2590 ALsource *Source = LookupSource(context.get(), source);
2591 if UNLIKELY(!Source)
2592 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2593 else
2595 const float fval[1]{static_cast<float>(value)};
2596 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fval);
2599 END_API_FUNC
2601 AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
2602 START_API_FUNC
2604 ContextRef context{GetContextRef()};
2605 if UNLIKELY(!context) return;
2607 std::lock_guard<std::mutex> _{context->mPropLock};
2608 std::lock_guard<std::mutex> __{context->mSourceLock};
2609 ALsource *Source = LookupSource(context.get(), source);
2610 if UNLIKELY(!Source)
2611 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2612 else
2614 const float fvals[3]{static_cast<float>(value1), static_cast<float>(value2),
2615 static_cast<float>(value3)};
2616 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2619 END_API_FUNC
2621 AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
2622 START_API_FUNC
2624 ContextRef context{GetContextRef()};
2625 if UNLIKELY(!context) return;
2627 std::lock_guard<std::mutex> _{context->mPropLock};
2628 std::lock_guard<std::mutex> __{context->mSourceLock};
2629 ALsource *Source = LookupSource(context.get(), source);
2630 if UNLIKELY(!Source)
2631 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2632 else if UNLIKELY(!values)
2633 context->setError(AL_INVALID_VALUE, "NULL pointer");
2634 else
2636 const ALuint count{DoubleValsByProp(param)};
2637 float fvals[MaxValues];
2638 for(ALuint i{0};i < count;i++)
2639 fvals[i] = static_cast<float>(values[i]);
2640 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {fvals, count});
2643 END_API_FUNC
2646 AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
2647 START_API_FUNC
2649 ContextRef context{GetContextRef()};
2650 if UNLIKELY(!context) return;
2652 std::lock_guard<std::mutex> _{context->mPropLock};
2653 std::lock_guard<std::mutex> __{context->mSourceLock};
2654 ALsource *Source = LookupSource(context.get(), source);
2655 if UNLIKELY(!Source)
2656 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2657 else
2658 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2660 END_API_FUNC
2662 AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
2663 START_API_FUNC
2665 ContextRef context{GetContextRef()};
2666 if UNLIKELY(!context) return;
2668 std::lock_guard<std::mutex> _{context->mPropLock};
2669 std::lock_guard<std::mutex> __{context->mSourceLock};
2670 ALsource *Source = LookupSource(context.get(), source);
2671 if UNLIKELY(!Source)
2672 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2673 else
2675 const int ivals[3]{ value1, value2, value3 };
2676 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals);
2679 END_API_FUNC
2681 AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
2682 START_API_FUNC
2684 ContextRef context{GetContextRef()};
2685 if UNLIKELY(!context) return;
2687 std::lock_guard<std::mutex> _{context->mPropLock};
2688 std::lock_guard<std::mutex> __{context->mSourceLock};
2689 ALsource *Source = LookupSource(context.get(), source);
2690 if UNLIKELY(!Source)
2691 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2692 else if UNLIKELY(!values)
2693 context->setError(AL_INVALID_VALUE, "NULL pointer");
2694 else
2695 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2697 END_API_FUNC
2700 AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
2701 START_API_FUNC
2703 ContextRef context{GetContextRef()};
2704 if UNLIKELY(!context) return;
2706 std::lock_guard<std::mutex> _{context->mPropLock};
2707 std::lock_guard<std::mutex> __{context->mSourceLock};
2708 ALsource *Source{LookupSource(context.get(), source)};
2709 if UNLIKELY(!Source)
2710 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2711 else
2712 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2714 END_API_FUNC
2716 AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
2717 START_API_FUNC
2719 ContextRef context{GetContextRef()};
2720 if UNLIKELY(!context) return;
2722 std::lock_guard<std::mutex> _{context->mPropLock};
2723 std::lock_guard<std::mutex> __{context->mSourceLock};
2724 ALsource *Source{LookupSource(context.get(), source)};
2725 if UNLIKELY(!Source)
2726 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2727 else
2729 const int64_t i64vals[3]{ value1, value2, value3 };
2730 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals);
2733 END_API_FUNC
2735 AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
2736 START_API_FUNC
2738 ContextRef context{GetContextRef()};
2739 if UNLIKELY(!context) return;
2741 std::lock_guard<std::mutex> _{context->mPropLock};
2742 std::lock_guard<std::mutex> __{context->mSourceLock};
2743 ALsource *Source{LookupSource(context.get(), source)};
2744 if UNLIKELY(!Source)
2745 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2746 else if UNLIKELY(!values)
2747 context->setError(AL_INVALID_VALUE, "NULL pointer");
2748 else
2749 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2751 END_API_FUNC
2754 AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
2755 START_API_FUNC
2757 ContextRef context{GetContextRef()};
2758 if UNLIKELY(!context) return;
2760 std::lock_guard<std::mutex> _{context->mSourceLock};
2761 ALsource *Source{LookupSource(context.get(), source)};
2762 if UNLIKELY(!Source)
2763 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2764 else if UNLIKELY(!value)
2765 context->setError(AL_INVALID_VALUE, "NULL pointer");
2766 else
2768 double dval[1];
2769 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dval))
2770 *value = static_cast<float>(dval[0]);
2773 END_API_FUNC
2775 AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
2776 START_API_FUNC
2778 ContextRef context{GetContextRef()};
2779 if UNLIKELY(!context) return;
2781 std::lock_guard<std::mutex> _{context->mSourceLock};
2782 ALsource *Source{LookupSource(context.get(), source)};
2783 if UNLIKELY(!Source)
2784 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2785 else if UNLIKELY(!(value1 && value2 && value3))
2786 context->setError(AL_INVALID_VALUE, "NULL pointer");
2787 else
2789 double dvals[3];
2790 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2792 *value1 = static_cast<float>(dvals[0]);
2793 *value2 = static_cast<float>(dvals[1]);
2794 *value3 = static_cast<float>(dvals[2]);
2798 END_API_FUNC
2800 AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
2801 START_API_FUNC
2803 ContextRef context{GetContextRef()};
2804 if UNLIKELY(!context) return;
2806 std::lock_guard<std::mutex> _{context->mSourceLock};
2807 ALsource *Source{LookupSource(context.get(), source)};
2808 if UNLIKELY(!Source)
2809 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2810 else if UNLIKELY(!values)
2811 context->setError(AL_INVALID_VALUE, "NULL pointer");
2812 else
2814 const ALuint count{FloatValsByProp(param)};
2815 double dvals[MaxValues];
2816 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {dvals, count}))
2818 for(ALuint i{0};i < count;i++)
2819 values[i] = static_cast<float>(dvals[i]);
2823 END_API_FUNC
2826 AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
2827 START_API_FUNC
2829 ContextRef context{GetContextRef()};
2830 if UNLIKELY(!context) return;
2832 std::lock_guard<std::mutex> _{context->mSourceLock};
2833 ALsource *Source{LookupSource(context.get(), source)};
2834 if UNLIKELY(!Source)
2835 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2836 else if UNLIKELY(!value)
2837 context->setError(AL_INVALID_VALUE, "NULL pointer");
2838 else
2839 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2841 END_API_FUNC
2843 AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
2844 START_API_FUNC
2846 ContextRef context{GetContextRef()};
2847 if UNLIKELY(!context) return;
2849 std::lock_guard<std::mutex> _{context->mSourceLock};
2850 ALsource *Source{LookupSource(context.get(), source)};
2851 if UNLIKELY(!Source)
2852 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2853 else if UNLIKELY(!(value1 && value2 && value3))
2854 context->setError(AL_INVALID_VALUE, "NULL pointer");
2855 else
2857 double dvals[3];
2858 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2860 *value1 = dvals[0];
2861 *value2 = dvals[1];
2862 *value3 = dvals[2];
2866 END_API_FUNC
2868 AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
2869 START_API_FUNC
2871 ContextRef context{GetContextRef()};
2872 if UNLIKELY(!context) return;
2874 std::lock_guard<std::mutex> _{context->mSourceLock};
2875 ALsource *Source{LookupSource(context.get(), source)};
2876 if UNLIKELY(!Source)
2877 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2878 else if UNLIKELY(!values)
2879 context->setError(AL_INVALID_VALUE, "NULL pointer");
2880 else
2881 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2883 END_API_FUNC
2886 AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
2887 START_API_FUNC
2889 ContextRef context{GetContextRef()};
2890 if UNLIKELY(!context) return;
2892 std::lock_guard<std::mutex> _{context->mSourceLock};
2893 ALsource *Source{LookupSource(context.get(), source)};
2894 if UNLIKELY(!Source)
2895 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2896 else if UNLIKELY(!value)
2897 context->setError(AL_INVALID_VALUE, "NULL pointer");
2898 else
2899 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2901 END_API_FUNC
2903 AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
2904 START_API_FUNC
2906 ContextRef context{GetContextRef()};
2907 if UNLIKELY(!context) return;
2909 std::lock_guard<std::mutex> _{context->mSourceLock};
2910 ALsource *Source{LookupSource(context.get(), source)};
2911 if UNLIKELY(!Source)
2912 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2913 else if UNLIKELY(!(value1 && value2 && value3))
2914 context->setError(AL_INVALID_VALUE, "NULL pointer");
2915 else
2917 int ivals[3];
2918 if(GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals))
2920 *value1 = ivals[0];
2921 *value2 = ivals[1];
2922 *value3 = ivals[2];
2926 END_API_FUNC
2928 AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
2929 START_API_FUNC
2931 ContextRef context{GetContextRef()};
2932 if UNLIKELY(!context) return;
2934 std::lock_guard<std::mutex> _{context->mSourceLock};
2935 ALsource *Source{LookupSource(context.get(), source)};
2936 if UNLIKELY(!Source)
2937 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2938 else if UNLIKELY(!values)
2939 context->setError(AL_INVALID_VALUE, "NULL pointer");
2940 else
2941 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2943 END_API_FUNC
2946 AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
2947 START_API_FUNC
2949 ContextRef context{GetContextRef()};
2950 if UNLIKELY(!context) return;
2952 std::lock_guard<std::mutex> _{context->mSourceLock};
2953 ALsource *Source{LookupSource(context.get(), source)};
2954 if UNLIKELY(!Source)
2955 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2956 else if UNLIKELY(!value)
2957 context->setError(AL_INVALID_VALUE, "NULL pointer");
2958 else
2959 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2961 END_API_FUNC
2963 AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
2964 START_API_FUNC
2966 ContextRef context{GetContextRef()};
2967 if UNLIKELY(!context) return;
2969 std::lock_guard<std::mutex> _{context->mSourceLock};
2970 ALsource *Source{LookupSource(context.get(), source)};
2971 if UNLIKELY(!Source)
2972 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2973 else if UNLIKELY(!(value1 && value2 && value3))
2974 context->setError(AL_INVALID_VALUE, "NULL pointer");
2975 else
2977 int64_t i64vals[3];
2978 if(GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals))
2980 *value1 = i64vals[0];
2981 *value2 = i64vals[1];
2982 *value3 = i64vals[2];
2986 END_API_FUNC
2988 AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
2989 START_API_FUNC
2991 ContextRef context{GetContextRef()};
2992 if UNLIKELY(!context) return;
2994 std::lock_guard<std::mutex> _{context->mSourceLock};
2995 ALsource *Source{LookupSource(context.get(), source)};
2996 if UNLIKELY(!Source)
2997 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2998 else if UNLIKELY(!values)
2999 context->setError(AL_INVALID_VALUE, "NULL pointer");
3000 else
3001 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
3003 END_API_FUNC
3006 AL_API void AL_APIENTRY alSourcePlay(ALuint source)
3007 START_API_FUNC
3008 { alSourcePlayv(1, &source); }
3009 END_API_FUNC
3011 AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
3012 START_API_FUNC
3014 ContextRef context{GetContextRef()};
3015 if UNLIKELY(!context) return;
3017 if UNLIKELY(n < 0)
3018 context->setError(AL_INVALID_VALUE, "Playing %d sources", n);
3019 if UNLIKELY(n <= 0) return;
3021 al::vector<ALsource*> extra_sources;
3022 std::array<ALsource*,8> source_storage;
3023 al::span<ALsource*> srchandles;
3024 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3025 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3026 else
3028 extra_sources.resize(static_cast<ALuint>(n));
3029 srchandles = {extra_sources.data(), extra_sources.size()};
3032 std::lock_guard<std::mutex> _{context->mSourceLock};
3033 for(auto &srchdl : srchandles)
3035 srchdl = LookupSource(context.get(), *sources);
3036 if(!srchdl)
3037 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3038 ++sources;
3041 ALCdevice *device{context->mALDevice.get()};
3042 /* If the device is disconnected, and voices stop on disconnect, go right
3043 * to stopped.
3045 if UNLIKELY(!device->Connected.load(std::memory_order_acquire))
3047 if(context->mStopVoicesOnDisconnect.load(std::memory_order_acquire))
3049 for(ALsource *source : srchandles)
3051 /* TODO: Send state change event? */
3052 source->Offset = 0.0;
3053 source->OffsetType = AL_NONE;
3054 source->state = AL_STOPPED;
3056 return;
3060 /* Count the number of reusable voices. */
3061 auto voicelist = context->getVoicesSpan();
3062 size_t free_voices{0};
3063 for(const Voice *voice : voicelist)
3065 free_voices += (voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
3066 && voice->mSourceID.load(std::memory_order_relaxed) == 0u
3067 && voice->mPendingChange.load(std::memory_order_relaxed) == false);
3068 if(free_voices == srchandles.size())
3069 break;
3071 if UNLIKELY(srchandles.size() != free_voices)
3073 const size_t inc_amount{srchandles.size() - free_voices};
3074 auto &allvoices = *context->mVoices.load(std::memory_order_relaxed);
3075 if(inc_amount > allvoices.size() - voicelist.size())
3077 /* Increase the number of voices to handle the request. */
3078 context->allocVoices(inc_amount - (allvoices.size() - voicelist.size()));
3080 context->mActiveVoiceCount.fetch_add(inc_amount, std::memory_order_release);
3081 voicelist = context->getVoicesSpan();
3084 auto voiceiter = voicelist.begin();
3085 ALuint vidx{0};
3086 VoiceChange *tail{}, *cur{};
3087 for(ALsource *source : srchandles)
3089 /* Check that there is a queue containing at least one valid, non zero
3090 * length buffer.
3092 auto BufferList = source->mQueue.begin();
3093 for(;BufferList != source->mQueue.end();++BufferList)
3095 if(BufferList->mSampleLen != 0 || BufferList->mCallback)
3096 break;
3099 /* If there's nothing to play, go right to stopped. */
3100 if UNLIKELY(BufferList == source->mQueue.end())
3102 /* NOTE: A source without any playable buffers should not have a
3103 * Voice since it shouldn't be in a playing or paused state. So
3104 * there's no need to look up its voice and clear the source.
3106 source->Offset = 0.0;
3107 source->OffsetType = AL_NONE;
3108 source->state = AL_STOPPED;
3109 continue;
3112 if(!cur)
3113 cur = tail = GetVoiceChanger(context.get());
3114 else
3116 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3117 cur = cur->mNext.load(std::memory_order_relaxed);
3120 Voice *voice{GetSourceVoice(source, context.get())};
3121 switch(GetSourceState(source, voice))
3123 case AL_PAUSED:
3124 /* A source that's paused simply resumes. If there's no voice, it
3125 * was lost from a disconnect, so just start over with a new one.
3127 cur->mOldVoice = nullptr;
3128 if(!voice) break;
3129 cur->mVoice = voice;
3130 cur->mSourceID = source->id;
3131 cur->mState = VChangeState::Play;
3132 source->state = AL_PLAYING;
3133 #ifdef ALSOFT_EAX
3134 if(source->eax_is_initialized())
3135 source->eax_commit();
3136 #endif // ALSOFT_EAX
3137 continue;
3139 case AL_PLAYING:
3140 /* A source that's already playing is restarted from the beginning.
3141 * Stop the current voice and start a new one so it properly cross-
3142 * fades back to the beginning.
3144 if(voice)
3145 voice->mPendingChange.store(true, std::memory_order_relaxed);
3146 cur->mOldVoice = voice;
3147 voice = nullptr;
3148 break;
3150 default:
3151 assert(voice == nullptr);
3152 cur->mOldVoice = nullptr;
3153 #ifdef ALSOFT_EAX
3154 if(source->eax_is_initialized())
3155 source->eax_commit();
3156 #endif // ALSOFT_EAX
3157 break;
3160 /* Find the next unused voice to play this source with. */
3161 for(;voiceiter != voicelist.end();++voiceiter,++vidx)
3163 Voice *v{*voiceiter};
3164 if(v->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
3165 && v->mSourceID.load(std::memory_order_relaxed) == 0u
3166 && v->mPendingChange.load(std::memory_order_relaxed) == false)
3168 voice = v;
3169 break;
3172 ASSUME(voice != nullptr);
3174 voice->mPosition.store(0u, std::memory_order_relaxed);
3175 voice->mPositionFrac.store(0, std::memory_order_relaxed);
3176 voice->mCurrentBuffer.store(&source->mQueue.front(), std::memory_order_relaxed);
3177 voice->mFlags.reset();
3178 /* A source that's not playing or paused has any offset applied when it
3179 * starts playing.
3181 if(const ALenum offsettype{source->OffsetType})
3183 const double offset{source->Offset};
3184 source->OffsetType = AL_NONE;
3185 source->Offset = 0.0;
3186 if(auto vpos = GetSampleOffset(source->mQueue, offsettype, offset))
3188 voice->mPosition.store(vpos->pos, std::memory_order_relaxed);
3189 voice->mPositionFrac.store(vpos->frac, std::memory_order_relaxed);
3190 voice->mCurrentBuffer.store(vpos->bufferitem, std::memory_order_relaxed);
3191 if(vpos->pos!=0 || vpos->frac!=0 || vpos->bufferitem!=&source->mQueue.front())
3192 voice->mFlags.set(VoiceIsFading);
3195 InitVoice(voice, source, std::addressof(*BufferList), context.get(), device);
3197 source->VoiceIdx = vidx;
3198 source->state = AL_PLAYING;
3200 cur->mVoice = voice;
3201 cur->mSourceID = source->id;
3202 cur->mState = VChangeState::Play;
3204 if LIKELY(tail)
3205 SendVoiceChanges(context.get(), tail);
3207 END_API_FUNC
3210 AL_API void AL_APIENTRY alSourcePause(ALuint source)
3211 START_API_FUNC
3212 { alSourcePausev(1, &source); }
3213 END_API_FUNC
3215 AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
3216 START_API_FUNC
3218 ContextRef context{GetContextRef()};
3219 if UNLIKELY(!context) return;
3221 if UNLIKELY(n < 0)
3222 context->setError(AL_INVALID_VALUE, "Pausing %d sources", n);
3223 if UNLIKELY(n <= 0) return;
3225 al::vector<ALsource*> extra_sources;
3226 std::array<ALsource*,8> source_storage;
3227 al::span<ALsource*> srchandles;
3228 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3229 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3230 else
3232 extra_sources.resize(static_cast<ALuint>(n));
3233 srchandles = {extra_sources.data(), extra_sources.size()};
3236 std::lock_guard<std::mutex> _{context->mSourceLock};
3237 for(auto &srchdl : srchandles)
3239 srchdl = LookupSource(context.get(), *sources);
3240 if(!srchdl)
3241 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3242 ++sources;
3245 /* Pausing has to be done in two steps. First, for each source that's
3246 * detected to be playing, chamge the voice (asynchronously) to
3247 * stopping/paused.
3249 VoiceChange *tail{}, *cur{};
3250 for(ALsource *source : srchandles)
3252 Voice *voice{GetSourceVoice(source, context.get())};
3253 if(GetSourceState(source, voice) == AL_PLAYING)
3255 if(!cur)
3256 cur = tail = GetVoiceChanger(context.get());
3257 else
3259 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3260 cur = cur->mNext.load(std::memory_order_relaxed);
3262 cur->mVoice = voice;
3263 cur->mSourceID = source->id;
3264 cur->mState = VChangeState::Pause;
3267 if LIKELY(tail)
3269 SendVoiceChanges(context.get(), tail);
3270 /* Second, now that the voice changes have been sent, because it's
3271 * possible that the voice stopped after it was detected playing and
3272 * before the voice got paused, recheck that the source is still
3273 * considered playing and set it to paused if so.
3275 for(ALsource *source : srchandles)
3277 Voice *voice{GetSourceVoice(source, context.get())};
3278 if(GetSourceState(source, voice) == AL_PLAYING)
3279 source->state = AL_PAUSED;
3283 END_API_FUNC
3286 AL_API void AL_APIENTRY alSourceStop(ALuint source)
3287 START_API_FUNC
3288 { alSourceStopv(1, &source); }
3289 END_API_FUNC
3291 AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
3292 START_API_FUNC
3294 ContextRef context{GetContextRef()};
3295 if UNLIKELY(!context) return;
3297 if UNLIKELY(n < 0)
3298 context->setError(AL_INVALID_VALUE, "Stopping %d sources", n);
3299 if UNLIKELY(n <= 0) return;
3301 al::vector<ALsource*> extra_sources;
3302 std::array<ALsource*,8> source_storage;
3303 al::span<ALsource*> srchandles;
3304 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3305 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3306 else
3308 extra_sources.resize(static_cast<ALuint>(n));
3309 srchandles = {extra_sources.data(), extra_sources.size()};
3312 std::lock_guard<std::mutex> _{context->mSourceLock};
3313 for(auto &srchdl : srchandles)
3315 srchdl = LookupSource(context.get(), *sources);
3316 if(!srchdl)
3317 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3318 ++sources;
3321 VoiceChange *tail{}, *cur{};
3322 for(ALsource *source : srchandles)
3324 if(Voice *voice{GetSourceVoice(source, context.get())})
3326 if(!cur)
3327 cur = tail = GetVoiceChanger(context.get());
3328 else
3330 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3331 cur = cur->mNext.load(std::memory_order_relaxed);
3333 voice->mPendingChange.store(true, std::memory_order_relaxed);
3334 cur->mVoice = voice;
3335 cur->mSourceID = source->id;
3336 cur->mState = VChangeState::Stop;
3337 source->state = AL_STOPPED;
3339 source->Offset = 0.0;
3340 source->OffsetType = AL_NONE;
3341 source->VoiceIdx = INVALID_VOICE_IDX;
3343 if LIKELY(tail)
3344 SendVoiceChanges(context.get(), tail);
3346 END_API_FUNC
3349 AL_API void AL_APIENTRY alSourceRewind(ALuint source)
3350 START_API_FUNC
3351 { alSourceRewindv(1, &source); }
3352 END_API_FUNC
3354 AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
3355 START_API_FUNC
3357 ContextRef context{GetContextRef()};
3358 if UNLIKELY(!context) return;
3360 if UNLIKELY(n < 0)
3361 context->setError(AL_INVALID_VALUE, "Rewinding %d sources", n);
3362 if UNLIKELY(n <= 0) return;
3364 al::vector<ALsource*> extra_sources;
3365 std::array<ALsource*,8> source_storage;
3366 al::span<ALsource*> srchandles;
3367 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3368 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3369 else
3371 extra_sources.resize(static_cast<ALuint>(n));
3372 srchandles = {extra_sources.data(), extra_sources.size()};
3375 std::lock_guard<std::mutex> _{context->mSourceLock};
3376 for(auto &srchdl : srchandles)
3378 srchdl = LookupSource(context.get(), *sources);
3379 if(!srchdl)
3380 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3381 ++sources;
3384 VoiceChange *tail{}, *cur{};
3385 for(ALsource *source : srchandles)
3387 Voice *voice{GetSourceVoice(source, context.get())};
3388 if(source->state != AL_INITIAL)
3390 if(!cur)
3391 cur = tail = GetVoiceChanger(context.get());
3392 else
3394 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3395 cur = cur->mNext.load(std::memory_order_relaxed);
3397 if(voice)
3398 voice->mPendingChange.store(true, std::memory_order_relaxed);
3399 cur->mVoice = voice;
3400 cur->mSourceID = source->id;
3401 cur->mState = VChangeState::Reset;
3402 source->state = AL_INITIAL;
3404 source->Offset = 0.0;
3405 source->OffsetType = AL_NONE;
3406 source->VoiceIdx = INVALID_VOICE_IDX;
3408 if LIKELY(tail)
3409 SendVoiceChanges(context.get(), tail);
3411 END_API_FUNC
3414 AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
3415 START_API_FUNC
3417 ContextRef context{GetContextRef()};
3418 if UNLIKELY(!context) return;
3420 if UNLIKELY(nb < 0)
3421 context->setError(AL_INVALID_VALUE, "Queueing %d buffers", nb);
3422 if UNLIKELY(nb <= 0) return;
3424 std::lock_guard<std::mutex> _{context->mSourceLock};
3425 ALsource *source{LookupSource(context.get(),src)};
3426 if UNLIKELY(!source)
3427 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
3429 /* Can't queue on a Static Source */
3430 if UNLIKELY(source->SourceType == AL_STATIC)
3431 SETERR_RETURN(context, AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
3433 /* Check for a valid Buffer, for its frequency and format */
3434 ALCdevice *device{context->mALDevice.get()};
3435 ALbuffer *BufferFmt{nullptr};
3436 for(auto &item : source->mQueue)
3438 BufferFmt = item.mBuffer;
3439 if(BufferFmt) break;
3442 std::unique_lock<std::mutex> buflock{device->BufferLock};
3443 const size_t NewListStart{source->mQueue.size()};
3444 ALbufferQueueItem *BufferList{nullptr};
3445 for(ALsizei i{0};i < nb;i++)
3447 bool fmt_mismatch{false};
3448 ALbuffer *buffer{nullptr};
3449 if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr)
3451 context->setError(AL_INVALID_NAME, "Queueing invalid buffer ID %u", buffers[i]);
3452 goto buffer_error;
3454 if(buffer && buffer->mCallback)
3456 context->setError(AL_INVALID_OPERATION, "Queueing callback buffer %u", buffers[i]);
3457 goto buffer_error;
3460 source->mQueue.emplace_back();
3461 if(!BufferList)
3462 BufferList = &source->mQueue.back();
3463 else
3465 auto &item = source->mQueue.back();
3466 BufferList->mNext.store(&item, std::memory_order_relaxed);
3467 BufferList = &item;
3469 if(!buffer) continue;
3470 BufferList->mSampleLen = buffer->mSampleLen;
3471 BufferList->mLoopEnd = buffer->mSampleLen;
3472 BufferList->mSamples = buffer->mData.data();
3473 BufferList->mBuffer = buffer;
3474 IncrementRef(buffer->ref);
3476 if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
3478 context->setError(AL_INVALID_OPERATION, "Queueing non-persistently mapped buffer %u",
3479 buffer->id);
3480 goto buffer_error;
3483 if(BufferFmt == nullptr)
3484 BufferFmt = buffer;
3485 else
3487 fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate;
3488 fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels;
3489 if(BufferFmt->isBFormat())
3491 fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout;
3492 fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling;
3494 fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder;
3495 fmt_mismatch |= BufferFmt->OriginalType != buffer->OriginalType;
3497 if UNLIKELY(fmt_mismatch)
3499 context->setError(AL_INVALID_OPERATION, "Queueing buffer with mismatched format");
3501 buffer_error:
3502 /* A buffer failed (invalid ID or format), so unlock and release
3503 * each buffer we had.
3505 auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart);
3506 for(;iter != source->mQueue.end();++iter)
3508 if(ALbuffer *buf{iter->mBuffer})
3509 DecrementRef(buf->ref);
3511 source->mQueue.resize(NewListStart);
3512 return;
3515 /* All buffers good. */
3516 buflock.unlock();
3518 /* Source is now streaming */
3519 source->SourceType = AL_STREAMING;
3521 if(NewListStart != 0)
3523 auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart);
3524 (iter-1)->mNext.store(std::addressof(*iter), std::memory_order_release);
3527 END_API_FUNC
3529 AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
3530 START_API_FUNC
3532 ContextRef context{GetContextRef()};
3533 if UNLIKELY(!context) return;
3535 if UNLIKELY(nb < 0)
3536 context->setError(AL_INVALID_VALUE, "Unqueueing %d buffers", nb);
3537 if UNLIKELY(nb <= 0) return;
3539 std::lock_guard<std::mutex> _{context->mSourceLock};
3540 ALsource *source{LookupSource(context.get(),src)};
3541 if UNLIKELY(!source)
3542 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
3544 if UNLIKELY(source->SourceType != AL_STREAMING)
3545 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from a non-streaming source %u",
3546 src);
3547 if UNLIKELY(source->Looping)
3548 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from looping source %u", src);
3550 /* Make sure enough buffers have been processed to unqueue. */
3551 uint processed{0u};
3552 if LIKELY(source->state != AL_INITIAL)
3554 VoiceBufferItem *Current{nullptr};
3555 if(Voice *voice{GetSourceVoice(source, context.get())})
3556 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
3557 for(auto &item : source->mQueue)
3559 if(&item == Current)
3560 break;
3561 ++processed;
3564 if UNLIKELY(processed < static_cast<ALuint>(nb))
3565 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing %d buffer%s (only %u processed)",
3566 nb, (nb==1)?"":"s", processed);
3568 do {
3569 auto &head = source->mQueue.front();
3570 if(ALbuffer *buffer{head.mBuffer})
3572 *(buffers++) = buffer->id;
3573 DecrementRef(buffer->ref);
3575 else
3576 *(buffers++) = 0;
3577 source->mQueue.pop_front();
3578 } while(--nb);
3580 END_API_FUNC
3583 AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint, ALsizei, const ALuint*)
3584 START_API_FUNC
3586 ContextRef context{GetContextRef()};
3587 if UNLIKELY(!context) return;
3589 context->setError(AL_INVALID_OPERATION, "alSourceQueueBufferLayersSOFT not supported");
3591 END_API_FUNC
3594 ALsource::ALsource()
3596 Direct.Gain = 1.0f;
3597 Direct.GainHF = 1.0f;
3598 Direct.HFReference = LOWPASSFREQREF;
3599 Direct.GainLF = 1.0f;
3600 Direct.LFReference = HIGHPASSFREQREF;
3601 for(auto &send : Send)
3603 send.Slot = nullptr;
3604 send.Gain = 1.0f;
3605 send.GainHF = 1.0f;
3606 send.HFReference = LOWPASSFREQREF;
3607 send.GainLF = 1.0f;
3608 send.LFReference = HIGHPASSFREQREF;
3612 ALsource::~ALsource()
3614 for(auto &item : mQueue)
3616 if(ALbuffer *buffer{item.mBuffer})
3617 DecrementRef(buffer->ref);
3620 auto clear_send = [](ALsource::SendData &send) -> void
3621 { if(send.Slot) DecrementRef(send.Slot->ref); };
3622 std::for_each(Send.begin(), Send.end(), clear_send);
3625 void UpdateAllSourceProps(ALCcontext *context)
3627 std::lock_guard<std::mutex> _{context->mSourceLock};
3628 #ifdef ALSOFT_EAX
3629 if(context->has_eax())
3631 /* If EAX is enabled, we need to go through and commit all sources' EAX
3632 * changes, along with updating its voice, if any.
3634 for(auto &sublist : context->mSourceList)
3636 uint64_t usemask{~sublist.FreeMask};
3637 while(usemask)
3639 const int idx{al::countr_zero(usemask)};
3640 usemask &= ~(1_u64 << idx);
3642 ALsource *source{sublist.Sources + idx};
3643 source->eax_commit();
3645 if(Voice *voice{GetSourceVoice(source, context)})
3647 if(std::exchange(source->mPropsDirty, false))
3648 UpdateSourceProps(source, voice, context);
3653 else
3654 #endif
3656 auto voicelist = context->getVoicesSpan();
3657 ALuint vidx{0u};
3658 for(Voice *voice : voicelist)
3660 ALuint sid{voice->mSourceID.load(std::memory_order_acquire)};
3661 ALsource *source = sid ? LookupSource(context, sid) : nullptr;
3662 if(source && source->VoiceIdx == vidx)
3664 if(std::exchange(source->mPropsDirty, false))
3665 UpdateSourceProps(source, voice, context);
3667 ++vidx;
3672 SourceSubList::~SourceSubList()
3674 uint64_t usemask{~FreeMask};
3675 while(usemask)
3677 const int idx{al::countr_zero(usemask)};
3678 usemask &= ~(1_u64 << idx);
3679 al::destroy_at(Sources+idx);
3681 FreeMask = ~usemask;
3682 al_free(Sources);
3683 Sources = nullptr;
3687 #ifdef ALSOFT_EAX
3688 class EaxSourceException :
3689 public EaxException
3691 public:
3692 explicit EaxSourceException(
3693 const char* message)
3695 EaxException{"EAX_SOURCE", message}
3698 }; // EaxSourceException
3701 class EaxSourceActiveFxSlotsException :
3702 public EaxException
3704 public:
3705 explicit EaxSourceActiveFxSlotsException(
3706 const char* message)
3708 EaxException{"EAX_SOURCE_ACTIVE_FX_SLOTS", message}
3711 }; // EaxSourceActiveFxSlotsException
3714 class EaxSourceSendException :
3715 public EaxException
3717 public:
3718 explicit EaxSourceSendException(
3719 const char* message)
3721 EaxException{"EAX_SOURCE_SEND", message}
3724 }; // EaxSourceSendException
3727 void EaxUpdateSourceVoice(ALsource *source, ALCcontext *context)
3729 if(Voice *voice{GetSourceVoice(source, context)})
3731 if(std::exchange(source->mPropsDirty, false))
3732 UpdateSourceProps(source, voice, context);
3737 void ALsource::eax_initialize(ALCcontext *context) noexcept
3739 assert(context);
3740 eax_al_context_ = context;
3741 eax_set_defaults();
3742 eax_initialize_fx_slots();
3744 eax_d_ = eax_;
3747 void ALsource::eax_update_filters()
3749 eax_update_filters_internal();
3752 void ALsource::eax_update(EaxContextSharedDirtyFlags)
3754 /* NOTE: EaxContextSharedDirtyFlags only has one flag (primary_fx_slot_id),
3755 * which must be true for this to be called.
3757 if(eax_uses_primary_id_)
3758 eax_update_primary_fx_slot_id();
3761 void ALsource::eax_commit_and_update()
3763 eax_apply_deferred();
3764 EaxUpdateSourceVoice(this, eax_al_context_);
3767 ALsource* ALsource::eax_lookup_source(
3768 ALCcontext& al_context,
3769 ALuint source_id) noexcept
3771 return LookupSource(&al_context, source_id);
3774 [[noreturn]]
3775 void ALsource::eax_fail(
3776 const char* message)
3778 throw EaxSourceException{message};
3781 void ALsource::eax_set_source_defaults() noexcept
3783 eax1_.fMix = EAX_REVERBMIX_USEDISTANCE;
3785 eax_.source.lDirect = EAXSOURCE_DEFAULTDIRECT;
3786 eax_.source.lDirectHF = EAXSOURCE_DEFAULTDIRECTHF;
3787 eax_.source.lRoom = EAXSOURCE_DEFAULTROOM;
3788 eax_.source.lRoomHF = EAXSOURCE_DEFAULTROOMHF;
3789 eax_.source.lObstruction = EAXSOURCE_DEFAULTOBSTRUCTION;
3790 eax_.source.flObstructionLFRatio = EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO;
3791 eax_.source.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION;
3792 eax_.source.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO;
3793 eax_.source.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO;
3794 eax_.source.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO;
3795 eax_.source.lExclusion = EAXSOURCE_DEFAULTEXCLUSION;
3796 eax_.source.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO;
3797 eax_.source.lOutsideVolumeHF = EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF;
3798 eax_.source.flDopplerFactor = EAXSOURCE_DEFAULTDOPPLERFACTOR;
3799 eax_.source.flRolloffFactor = EAXSOURCE_DEFAULTROLLOFFFACTOR;
3800 eax_.source.flRoomRolloffFactor = EAXSOURCE_DEFAULTROOMROLLOFFFACTOR;
3801 eax_.source.flAirAbsorptionFactor = EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR;
3802 eax_.source.ulFlags = EAXSOURCE_DEFAULTFLAGS;
3803 eax_.source.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR;
3806 void ALsource::eax_set_active_fx_slots_defaults() noexcept
3808 eax_.active_fx_slots = EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID;
3811 void ALsource::eax_set_send_defaults(EAXSOURCEALLSENDPROPERTIES& eax_send) noexcept
3813 eax_send.guidReceivingFXSlotID = EAX_NULL_GUID;
3814 eax_send.lSend = EAXSOURCE_DEFAULTSEND;
3815 eax_send.lSendHF = EAXSOURCE_DEFAULTSENDHF;
3816 eax_send.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION;
3817 eax_send.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO;
3818 eax_send.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO;
3819 eax_send.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO;
3820 eax_send.lExclusion = EAXSOURCE_DEFAULTEXCLUSION;
3821 eax_send.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO;
3824 void ALsource::eax_set_sends_defaults() noexcept
3826 for (auto& eax_send : eax_.sends)
3828 eax_set_send_defaults(eax_send);
3832 void ALsource::eax_set_speaker_levels_defaults() noexcept
3834 std::fill(eax_.speaker_levels.begin(), eax_.speaker_levels.end(), EAXSOURCE_DEFAULTSPEAKERLEVEL);
3837 void ALsource::eax_set_defaults() noexcept
3839 eax_set_source_defaults();
3840 eax_set_active_fx_slots_defaults();
3841 eax_set_sends_defaults();
3842 eax_set_speaker_levels_defaults();
3845 float ALsource::eax_calculate_dst_occlusion_mb(
3846 long src_occlusion_mb,
3847 float path_ratio,
3848 float lf_ratio) noexcept
3850 const auto ratio_1 = path_ratio + lf_ratio - 1.0F;
3851 const auto ratio_2 = path_ratio * lf_ratio;
3852 const auto ratio = (ratio_2 > ratio_1) ? ratio_2 : ratio_1;
3853 const auto dst_occlustion_mb = static_cast<float>(src_occlusion_mb) * ratio;
3855 return dst_occlustion_mb;
3858 EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept
3860 auto gain_mb =
3861 static_cast<float>(eax_.source.lDirect) +
3863 (static_cast<float>(eax_.source.lObstruction) * eax_.source.flObstructionLFRatio) +
3865 eax_calculate_dst_occlusion_mb(
3866 eax_.source.lOcclusion,
3867 eax_.source.flOcclusionDirectRatio,
3868 eax_.source.flOcclusionLFRatio);
3870 auto gain_hf_mb =
3871 static_cast<float>(eax_.source.lDirectHF) +
3873 static_cast<float>(eax_.source.lObstruction) +
3875 (static_cast<float>(eax_.source.lOcclusion) * eax_.source.flOcclusionDirectRatio);
3877 for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i)
3879 if (eax_active_fx_slots_[i])
3881 const auto& send = eax_.sends[i];
3883 gain_mb += eax_calculate_dst_occlusion_mb(
3884 send.lOcclusion,
3885 send.flOcclusionDirectRatio,
3886 send.flOcclusionLFRatio);
3888 gain_hf_mb += static_cast<float>(send.lOcclusion) * send.flOcclusionDirectRatio;
3892 const auto al_low_pass_param = EaxAlLowPassParam
3894 level_mb_to_gain(gain_mb),
3895 minf(level_mb_to_gain(gain_hf_mb), 1.0f)
3898 return al_low_pass_param;
3901 EaxAlLowPassParam ALsource::eax_create_room_filter_param(
3902 const ALeffectslot& fx_slot,
3903 const EAXSOURCEALLSENDPROPERTIES& send) const noexcept
3905 const auto& fx_slot_eax = fx_slot.eax_get_eax_fx_slot();
3907 const auto gain_mb =
3908 static_cast<float>(
3909 eax_.source.lRoom +
3910 send.lSend) +
3912 eax_calculate_dst_occlusion_mb(
3913 eax_.source.lOcclusion,
3914 eax_.source.flOcclusionRoomRatio,
3915 eax_.source.flOcclusionLFRatio
3918 eax_calculate_dst_occlusion_mb(
3919 send.lOcclusion,
3920 send.flOcclusionRoomRatio,
3921 send.flOcclusionLFRatio
3924 (static_cast<float>(eax_.source.lExclusion) * eax_.source.flExclusionLFRatio) +
3925 (static_cast<float>(send.lExclusion) * send.flExclusionLFRatio) +
3927 0.0F;
3929 const auto gain_hf_mb =
3930 static_cast<float>(
3931 eax_.source.lRoomHF +
3932 send.lSendHF) +
3934 (static_cast<float>(fx_slot_eax.lOcclusion + eax_.source.lOcclusion) * eax_.source.flOcclusionRoomRatio) +
3935 (static_cast<float>(send.lOcclusion) * send.flOcclusionRoomRatio) +
3937 static_cast<float>(
3938 eax_.source.lExclusion +
3939 send.lExclusion) +
3941 0.0F;
3943 const auto al_low_pass_param = EaxAlLowPassParam
3945 level_mb_to_gain(gain_mb),
3946 minf(level_mb_to_gain(gain_hf_mb), 1.0f)
3949 return al_low_pass_param;
3952 void ALsource::eax_set_fx_slots()
3954 eax_uses_primary_id_ = false;
3955 eax_has_active_fx_slots_ = false;
3956 eax_active_fx_slots_.fill(false);
3958 for(const auto& eax_active_fx_slot_id : eax_.active_fx_slots.guidActiveFXSlots)
3960 auto fx_slot_index = EaxFxSlotIndex{};
3962 if(eax_active_fx_slot_id == EAX_PrimaryFXSlotID)
3964 eax_uses_primary_id_ = true;
3965 fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index();
3967 else
3969 fx_slot_index = eax_active_fx_slot_id;
3972 if(fx_slot_index.has_value())
3974 eax_has_active_fx_slots_ = true;
3975 eax_active_fx_slots_[*fx_slot_index] = true;
3979 for(auto i = 0u;i < eax_active_fx_slots_.size();++i)
3981 if(!eax_active_fx_slots_[i])
3982 eax_set_al_source_send(nullptr, i, EaxAlLowPassParam{1.0f, 1.0f});
3986 void ALsource::eax_initialize_fx_slots()
3988 eax_set_fx_slots();
3989 eax_update_filters_internal();
3992 void ALsource::eax_update_direct_filter_internal()
3994 const auto& direct_param = eax_create_direct_filter_param();
3996 Direct.Gain = direct_param.gain;
3997 Direct.GainHF = direct_param.gain_hf;
3998 Direct.HFReference = LOWPASSFREQREF;
3999 Direct.GainLF = 1.0f;
4000 Direct.LFReference = HIGHPASSFREQREF;
4001 mPropsDirty = true;
4004 void ALsource::eax_update_room_filters_internal()
4006 if (!eax_has_active_fx_slots_)
4008 return;
4011 for (auto i = 0u; i < EAX_MAX_FXSLOTS; ++i)
4013 if (eax_active_fx_slots_[i])
4015 auto& fx_slot = eax_al_context_->eax_get_fx_slot(static_cast<std::size_t>(i));
4016 const auto& send = eax_.sends[i];
4017 const auto& room_param = eax_create_room_filter_param(fx_slot, send);
4019 eax_set_al_source_send(&fx_slot, i, room_param);
4024 void ALsource::eax_update_filters_internal()
4026 eax_update_direct_filter_internal();
4027 eax_update_room_filters_internal();
4030 void ALsource::eax_update_primary_fx_slot_id()
4032 const auto& previous_primary_fx_slot_index = eax_al_context_->eax_get_previous_primary_fx_slot_index();
4033 const auto& primary_fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index();
4035 if (previous_primary_fx_slot_index == primary_fx_slot_index)
4037 return;
4040 if (previous_primary_fx_slot_index.has_value())
4042 const auto fx_slot_index = previous_primary_fx_slot_index.value();
4043 eax_active_fx_slots_[fx_slot_index] = false;
4045 eax_set_al_source_send(nullptr, fx_slot_index, EaxAlLowPassParam{1.0f, 1.0f});
4048 if (primary_fx_slot_index.has_value())
4050 const auto fx_slot_index = primary_fx_slot_index.value();
4051 eax_active_fx_slots_[fx_slot_index] = true;
4053 auto& fx_slot = eax_al_context_->eax_get_fx_slot(fx_slot_index);
4054 const auto& send = eax_.sends[fx_slot_index];
4055 const auto& room_param = eax_create_room_filter_param(fx_slot, send);
4057 eax_set_al_source_send(&fx_slot, fx_slot_index, room_param);
4060 eax_has_active_fx_slots_ = std::any_of(
4061 eax_active_fx_slots_.cbegin(),
4062 eax_active_fx_slots_.cend(),
4063 [](const auto& item)
4065 return item;
4070 void ALsource::eax_defer_active_fx_slots(
4071 const EaxEaxCall& eax_call)
4073 const auto active_fx_slots_span =
4074 eax_call.get_values<EaxSourceActiveFxSlotsException, const GUID>();
4076 const auto fx_slot_count = active_fx_slots_span.size();
4078 if (fx_slot_count <= 0 || fx_slot_count > EAX_MAX_FXSLOTS)
4080 throw EaxSourceActiveFxSlotsException{"Count out of range."};
4083 for (auto i = std::size_t{}; i < fx_slot_count; ++i)
4085 const auto& fx_slot_guid = active_fx_slots_span[i];
4087 if (fx_slot_guid != EAX_NULL_GUID &&
4088 fx_slot_guid != EAX_PrimaryFXSlotID &&
4089 fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot0 &&
4090 fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot0 &&
4091 fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot1 &&
4092 fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot1 &&
4093 fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot2 &&
4094 fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot2 &&
4095 fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot3 &&
4096 fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot3)
4098 throw EaxSourceActiveFxSlotsException{"Unsupported GUID."};
4102 for (auto i = std::size_t{}; i < fx_slot_count; ++i)
4104 eax_d_.active_fx_slots.guidActiveFXSlots[i] = active_fx_slots_span[i];
4107 for (auto i = fx_slot_count; i < EAX_MAX_FXSLOTS; ++i)
4109 eax_d_.active_fx_slots.guidActiveFXSlots[i] = EAX_NULL_GUID;
4112 eax_are_active_fx_slots_dirty_ = (eax_d_.active_fx_slots != eax_.active_fx_slots);
4116 const char* ALsource::eax_get_exclusion_name() noexcept
4118 return "Exclusion";
4121 const char* ALsource::eax_get_exclusion_lf_ratio_name() noexcept
4123 return "Exclusion LF Ratio";
4126 const char* ALsource::eax_get_occlusion_name() noexcept
4128 return "Occlusion";
4131 const char* ALsource::eax_get_occlusion_lf_ratio_name() noexcept
4133 return "Occlusion LF Ratio";
4136 const char* ALsource::eax_get_occlusion_direct_ratio_name() noexcept
4138 return "Occlusion Direct Ratio";
4141 const char* ALsource::eax_get_occlusion_room_ratio_name() noexcept
4143 return "Occlusion Room Ratio";
4146 void ALsource::eax1_validate_reverb_mix(float reverb_mix)
4148 if (reverb_mix == EAX_REVERBMIX_USEDISTANCE)
4149 return;
4151 eax_validate_range<EaxSourceSendException>("Reverb Mix", reverb_mix, EAX_BUFFER_MINREVERBMIX, EAX_BUFFER_MAXREVERBMIX);
4154 void ALsource::eax_validate_send_receiving_fx_slot_guid(
4155 const GUID& guidReceivingFXSlotID)
4157 if (guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 &&
4158 guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 &&
4159 guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 &&
4160 guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 &&
4161 guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 &&
4162 guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 &&
4163 guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot3 &&
4164 guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot3)
4166 throw EaxSourceSendException{"Unsupported receiving FX slot GUID."};
4170 void ALsource::eax_validate_send_send(
4171 long lSend)
4173 eax_validate_range<EaxSourceSendException>(
4174 "Send",
4175 lSend,
4176 EAXSOURCE_MINSEND,
4177 EAXSOURCE_MAXSEND);
4180 void ALsource::eax_validate_send_send_hf(
4181 long lSendHF)
4183 eax_validate_range<EaxSourceSendException>(
4184 "Send HF",
4185 lSendHF,
4186 EAXSOURCE_MINSENDHF,
4187 EAXSOURCE_MAXSENDHF);
4190 void ALsource::eax_validate_send_occlusion(
4191 long lOcclusion)
4193 eax_validate_range<EaxSourceSendException>(
4194 eax_get_occlusion_name(),
4195 lOcclusion,
4196 EAXSOURCE_MINOCCLUSION,
4197 EAXSOURCE_MAXOCCLUSION);
4200 void ALsource::eax_validate_send_occlusion_lf_ratio(
4201 float flOcclusionLFRatio)
4203 eax_validate_range<EaxSourceSendException>(
4204 eax_get_occlusion_lf_ratio_name(),
4205 flOcclusionLFRatio,
4206 EAXSOURCE_MINOCCLUSIONLFRATIO,
4207 EAXSOURCE_MAXOCCLUSIONLFRATIO);
4210 void ALsource::eax_validate_send_occlusion_room_ratio(
4211 float flOcclusionRoomRatio)
4213 eax_validate_range<EaxSourceSendException>(
4214 eax_get_occlusion_room_ratio_name(),
4215 flOcclusionRoomRatio,
4216 EAXSOURCE_MINOCCLUSIONROOMRATIO,
4217 EAXSOURCE_MAXOCCLUSIONROOMRATIO);
4220 void ALsource::eax_validate_send_occlusion_direct_ratio(
4221 float flOcclusionDirectRatio)
4223 eax_validate_range<EaxSourceSendException>(
4224 eax_get_occlusion_direct_ratio_name(),
4225 flOcclusionDirectRatio,
4226 EAXSOURCE_MINOCCLUSIONDIRECTRATIO,
4227 EAXSOURCE_MAXOCCLUSIONDIRECTRATIO);
4230 void ALsource::eax_validate_send_exclusion(
4231 long lExclusion)
4233 eax_validate_range<EaxSourceSendException>(
4234 eax_get_exclusion_name(),
4235 lExclusion,
4236 EAXSOURCE_MINEXCLUSION,
4237 EAXSOURCE_MAXEXCLUSION);
4240 void ALsource::eax_validate_send_exclusion_lf_ratio(
4241 float flExclusionLFRatio)
4243 eax_validate_range<EaxSourceSendException>(
4244 eax_get_exclusion_lf_ratio_name(),
4245 flExclusionLFRatio,
4246 EAXSOURCE_MINEXCLUSIONLFRATIO,
4247 EAXSOURCE_MAXEXCLUSIONLFRATIO);
4250 void ALsource::eax_validate_send(
4251 const EAXSOURCESENDPROPERTIES& all)
4253 eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID);
4254 eax_validate_send_send(all.lSend);
4255 eax_validate_send_send_hf(all.lSendHF);
4258 void ALsource::eax_validate_send_exclusion_all(
4259 const EAXSOURCEEXCLUSIONSENDPROPERTIES& all)
4261 eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID);
4262 eax_validate_send_exclusion(all.lExclusion);
4263 eax_validate_send_exclusion_lf_ratio(all.flExclusionLFRatio);
4266 void ALsource::eax_validate_send_occlusion_all(
4267 const EAXSOURCEOCCLUSIONSENDPROPERTIES& all)
4269 eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID);
4270 eax_validate_send_occlusion(all.lOcclusion);
4271 eax_validate_send_occlusion_lf_ratio(all.flOcclusionLFRatio);
4272 eax_validate_send_occlusion_room_ratio(all.flOcclusionRoomRatio);
4273 eax_validate_send_occlusion_direct_ratio(all.flOcclusionDirectRatio);
4276 void ALsource::eax_validate_send_all(
4277 const EAXSOURCEALLSENDPROPERTIES& all)
4279 eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID);
4280 eax_validate_send_send(all.lSend);
4281 eax_validate_send_send_hf(all.lSendHF);
4282 eax_validate_send_occlusion(all.lOcclusion);
4283 eax_validate_send_occlusion_lf_ratio(all.flOcclusionLFRatio);
4284 eax_validate_send_occlusion_room_ratio(all.flOcclusionRoomRatio);
4285 eax_validate_send_occlusion_direct_ratio(all.flOcclusionDirectRatio);
4286 eax_validate_send_exclusion(all.lExclusion);
4287 eax_validate_send_exclusion_lf_ratio(all.flExclusionLFRatio);
4290 EaxFxSlotIndexValue ALsource::eax_get_send_index(
4291 const GUID& send_guid)
4293 if (false)
4296 else if (send_guid == EAXPROPERTYID_EAX40_FXSlot0 || send_guid == EAXPROPERTYID_EAX50_FXSlot0)
4298 return 0;
4300 else if (send_guid == EAXPROPERTYID_EAX40_FXSlot1 || send_guid == EAXPROPERTYID_EAX50_FXSlot1)
4302 return 1;
4304 else if (send_guid == EAXPROPERTYID_EAX40_FXSlot2 || send_guid == EAXPROPERTYID_EAX50_FXSlot2)
4306 return 2;
4308 else if (send_guid == EAXPROPERTYID_EAX40_FXSlot3 || send_guid == EAXPROPERTYID_EAX50_FXSlot3)
4310 return 3;
4312 else
4314 throw EaxSourceSendException{"Unsupported receiving FX slot GUID."};
4318 void ALsource::eax_defer_send_send(
4319 long lSend,
4320 EaxFxSlotIndexValue index)
4322 eax_d_.sends[index].lSend = lSend;
4324 eax_sends_dirty_flags_.sends[index].lSend =
4325 (eax_.sends[index].lSend != eax_d_.sends[index].lSend);
4328 void ALsource::eax_defer_send_send_hf(
4329 long lSendHF,
4330 EaxFxSlotIndexValue index)
4332 eax_d_.sends[index].lSendHF = lSendHF;
4334 eax_sends_dirty_flags_.sends[index].lSendHF =
4335 (eax_.sends[index].lSendHF != eax_d_.sends[index].lSendHF);
4338 void ALsource::eax_defer_send_occlusion(
4339 long lOcclusion,
4340 EaxFxSlotIndexValue index)
4342 eax_d_.sends[index].lOcclusion = lOcclusion;
4344 eax_sends_dirty_flags_.sends[index].lOcclusion =
4345 (eax_.sends[index].lOcclusion != eax_d_.sends[index].lOcclusion);
4348 void ALsource::eax_defer_send_occlusion_lf_ratio(
4349 float flOcclusionLFRatio,
4350 EaxFxSlotIndexValue index)
4352 eax_d_.sends[index].flOcclusionLFRatio = flOcclusionLFRatio;
4354 eax_sends_dirty_flags_.sends[index].flOcclusionLFRatio =
4355 (eax_.sends[index].flOcclusionLFRatio != eax_d_.sends[index].flOcclusionLFRatio);
4358 void ALsource::eax_defer_send_occlusion_room_ratio(
4359 float flOcclusionRoomRatio,
4360 EaxFxSlotIndexValue index)
4362 eax_d_.sends[index].flOcclusionRoomRatio = flOcclusionRoomRatio;
4364 eax_sends_dirty_flags_.sends[index].flOcclusionRoomRatio =
4365 (eax_.sends[index].flOcclusionRoomRatio != eax_d_.sends[index].flOcclusionRoomRatio);
4368 void ALsource::eax_defer_send_occlusion_direct_ratio(
4369 float flOcclusionDirectRatio,
4370 EaxFxSlotIndexValue index)
4372 eax_d_.sends[index].flOcclusionDirectRatio = flOcclusionDirectRatio;
4374 eax_sends_dirty_flags_.sends[index].flOcclusionDirectRatio =
4375 (eax_.sends[index].flOcclusionDirectRatio != eax_d_.sends[index].flOcclusionDirectRatio);
4378 void ALsource::eax_defer_send_exclusion(
4379 long lExclusion,
4380 EaxFxSlotIndexValue index)
4382 eax_d_.sends[index].lExclusion = lExclusion;
4384 eax_sends_dirty_flags_.sends[index].lExclusion =
4385 (eax_.sends[index].lExclusion != eax_d_.sends[index].lExclusion);
4388 void ALsource::eax_defer_send_exclusion_lf_ratio(
4389 float flExclusionLFRatio,
4390 EaxFxSlotIndexValue index)
4392 eax_d_.sends[index].flExclusionLFRatio = flExclusionLFRatio;
4394 eax_sends_dirty_flags_.sends[index].flExclusionLFRatio =
4395 (eax_.sends[index].flExclusionLFRatio != eax_d_.sends[index].flExclusionLFRatio);
4398 void ALsource::eax_defer_send(
4399 const EAXSOURCESENDPROPERTIES& all,
4400 EaxFxSlotIndexValue index)
4402 eax_defer_send_send(all.lSend, index);
4403 eax_defer_send_send_hf(all.lSendHF, index);
4406 void ALsource::eax_defer_send_exclusion_all(
4407 const EAXSOURCEEXCLUSIONSENDPROPERTIES& all,
4408 EaxFxSlotIndexValue index)
4410 eax_defer_send_exclusion(all.lExclusion, index);
4411 eax_defer_send_exclusion_lf_ratio(all.flExclusionLFRatio, index);
4414 void ALsource::eax_defer_send_occlusion_all(
4415 const EAXSOURCEOCCLUSIONSENDPROPERTIES& all,
4416 EaxFxSlotIndexValue index)
4418 eax_defer_send_occlusion(all.lOcclusion, index);
4419 eax_defer_send_occlusion_lf_ratio(all.flOcclusionLFRatio, index);
4420 eax_defer_send_occlusion_room_ratio(all.flOcclusionRoomRatio, index);
4421 eax_defer_send_occlusion_direct_ratio(all.flOcclusionDirectRatio, index);
4424 void ALsource::eax_defer_send_all(
4425 const EAXSOURCEALLSENDPROPERTIES& all,
4426 EaxFxSlotIndexValue index)
4428 eax_defer_send_send(all.lSend, index);
4429 eax_defer_send_send_hf(all.lSendHF, index);
4430 eax_defer_send_occlusion(all.lOcclusion, index);
4431 eax_defer_send_occlusion_lf_ratio(all.flOcclusionLFRatio, index);
4432 eax_defer_send_occlusion_room_ratio(all.flOcclusionRoomRatio, index);
4433 eax_defer_send_occlusion_direct_ratio(all.flOcclusionDirectRatio, index);
4434 eax_defer_send_exclusion(all.lExclusion, index);
4435 eax_defer_send_exclusion_lf_ratio(all.flExclusionLFRatio, index);
4438 void ALsource::eax_defer_send(
4439 const EaxEaxCall& eax_call)
4441 const auto eax_all_span =
4442 eax_call.get_values<EaxSourceException, const EAXSOURCESENDPROPERTIES>();
4444 const auto count = eax_all_span.size();
4446 if (count <= 0 || count > EAX_MAX_FXSLOTS)
4448 throw EaxSourceSendException{"Send count out of range."};
4451 for (auto i = std::size_t{}; i < count; ++i)
4453 const auto& all = eax_all_span[i];
4454 eax_validate_send(all);
4457 for (auto i = std::size_t{}; i < count; ++i)
4459 const auto& all = eax_all_span[i];
4460 const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID);
4461 eax_defer_send(all, send_index);
4465 void ALsource::eax_defer_send_exclusion_all(
4466 const EaxEaxCall& eax_call)
4468 const auto eax_all_span =
4469 eax_call.get_values<EaxSourceException, const EAXSOURCEEXCLUSIONSENDPROPERTIES>();
4471 const auto count = eax_all_span.size();
4473 if (count <= 0 || count > EAX_MAX_FXSLOTS)
4475 throw EaxSourceSendException{"Send exclusion all count out of range."};
4478 for (auto i = std::size_t{}; i < count; ++i)
4480 const auto& all = eax_all_span[i];
4481 eax_validate_send_exclusion_all(all);
4484 for (auto i = std::size_t{}; i < count; ++i)
4486 const auto& all = eax_all_span[i];
4487 const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID);
4488 eax_defer_send_exclusion_all(all, send_index);
4492 void ALsource::eax_defer_send_occlusion_all(
4493 const EaxEaxCall& eax_call)
4495 const auto eax_all_span =
4496 eax_call.get_values<EaxSourceException, const EAXSOURCEOCCLUSIONSENDPROPERTIES>();
4498 const auto count = eax_all_span.size();
4500 if (count <= 0 || count > EAX_MAX_FXSLOTS)
4502 throw EaxSourceSendException{"Send occlusion all count out of range."};
4505 for (auto i = std::size_t{}; i < count; ++i)
4507 const auto& all = eax_all_span[i];
4508 eax_validate_send_occlusion_all(all);
4511 for (auto i = std::size_t{}; i < count; ++i)
4513 const auto& all = eax_all_span[i];
4514 const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID);
4515 eax_defer_send_occlusion_all(all, send_index);
4519 void ALsource::eax_defer_send_all(
4520 const EaxEaxCall& eax_call)
4522 const auto eax_all_span =
4523 eax_call.get_values<EaxSourceException, const EAXSOURCEALLSENDPROPERTIES>();
4525 const auto count = eax_all_span.size();
4527 if (count <= 0 || count > EAX_MAX_FXSLOTS)
4529 throw EaxSourceSendException{"Send all count out of range."};
4532 for (auto i = std::size_t{}; i < count; ++i)
4534 const auto& all = eax_all_span[i];
4535 eax_validate_send_all(all);
4538 for (auto i = std::size_t{}; i < count; ++i)
4540 const auto& all = eax_all_span[i];
4541 const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID);
4542 eax_defer_send_all(all, send_index);
4547 void ALsource::eax_validate_source_direct(
4548 long direct)
4550 eax_validate_range<EaxSourceException>(
4551 "Direct",
4552 direct,
4553 EAXSOURCE_MINDIRECT,
4554 EAXSOURCE_MAXDIRECT);
4557 void ALsource::eax_validate_source_direct_hf(
4558 long direct_hf)
4560 eax_validate_range<EaxSourceException>(
4561 "Direct HF",
4562 direct_hf,
4563 EAXSOURCE_MINDIRECTHF,
4564 EAXSOURCE_MAXDIRECTHF);
4567 void ALsource::eax_validate_source_room(
4568 long room)
4570 eax_validate_range<EaxSourceException>(
4571 "Room",
4572 room,
4573 EAXSOURCE_MINROOM,
4574 EAXSOURCE_MAXROOM);
4577 void ALsource::eax_validate_source_room_hf(
4578 long room_hf)
4580 eax_validate_range<EaxSourceException>(
4581 "Room HF",
4582 room_hf,
4583 EAXSOURCE_MINROOMHF,
4584 EAXSOURCE_MAXROOMHF);
4587 void ALsource::eax_validate_source_obstruction(
4588 long obstruction)
4590 eax_validate_range<EaxSourceException>(
4591 "Obstruction",
4592 obstruction,
4593 EAXSOURCE_MINOBSTRUCTION,
4594 EAXSOURCE_MAXOBSTRUCTION);
4597 void ALsource::eax_validate_source_obstruction_lf_ratio(
4598 float obstruction_lf_ratio)
4600 eax_validate_range<EaxSourceException>(
4601 "Obstruction LF Ratio",
4602 obstruction_lf_ratio,
4603 EAXSOURCE_MINOBSTRUCTIONLFRATIO,
4604 EAXSOURCE_MAXOBSTRUCTIONLFRATIO);
4607 void ALsource::eax_validate_source_occlusion(
4608 long occlusion)
4610 eax_validate_range<EaxSourceException>(
4611 eax_get_occlusion_name(),
4612 occlusion,
4613 EAXSOURCE_MINOCCLUSION,
4614 EAXSOURCE_MAXOCCLUSION);
4617 void ALsource::eax_validate_source_occlusion_lf_ratio(
4618 float occlusion_lf_ratio)
4620 eax_validate_range<EaxSourceException>(
4621 eax_get_occlusion_lf_ratio_name(),
4622 occlusion_lf_ratio,
4623 EAXSOURCE_MINOCCLUSIONLFRATIO,
4624 EAXSOURCE_MAXOCCLUSIONLFRATIO);
4627 void ALsource::eax_validate_source_occlusion_room_ratio(
4628 float occlusion_room_ratio)
4630 eax_validate_range<EaxSourceException>(
4631 eax_get_occlusion_room_ratio_name(),
4632 occlusion_room_ratio,
4633 EAXSOURCE_MINOCCLUSIONROOMRATIO,
4634 EAXSOURCE_MAXOCCLUSIONROOMRATIO);
4637 void ALsource::eax_validate_source_occlusion_direct_ratio(
4638 float occlusion_direct_ratio)
4640 eax_validate_range<EaxSourceException>(
4641 eax_get_occlusion_direct_ratio_name(),
4642 occlusion_direct_ratio,
4643 EAXSOURCE_MINOCCLUSIONDIRECTRATIO,
4644 EAXSOURCE_MAXOCCLUSIONDIRECTRATIO);
4647 void ALsource::eax_validate_source_exclusion(
4648 long exclusion)
4650 eax_validate_range<EaxSourceException>(
4651 eax_get_exclusion_name(),
4652 exclusion,
4653 EAXSOURCE_MINEXCLUSION,
4654 EAXSOURCE_MAXEXCLUSION);
4657 void ALsource::eax_validate_source_exclusion_lf_ratio(
4658 float exclusion_lf_ratio)
4660 eax_validate_range<EaxSourceException>(
4661 eax_get_exclusion_lf_ratio_name(),
4662 exclusion_lf_ratio,
4663 EAXSOURCE_MINEXCLUSIONLFRATIO,
4664 EAXSOURCE_MAXEXCLUSIONLFRATIO);
4667 void ALsource::eax_validate_source_outside_volume_hf(
4668 long outside_volume_hf)
4670 eax_validate_range<EaxSourceException>(
4671 "Outside Volume HF",
4672 outside_volume_hf,
4673 EAXSOURCE_MINOUTSIDEVOLUMEHF,
4674 EAXSOURCE_MAXOUTSIDEVOLUMEHF);
4677 void ALsource::eax_validate_source_doppler_factor(
4678 float doppler_factor)
4680 eax_validate_range<EaxSourceException>(
4681 "Doppler Factor",
4682 doppler_factor,
4683 EAXSOURCE_MINDOPPLERFACTOR,
4684 EAXSOURCE_MAXDOPPLERFACTOR);
4687 void ALsource::eax_validate_source_rolloff_factor(
4688 float rolloff_factor)
4690 eax_validate_range<EaxSourceException>(
4691 "Rolloff Factor",
4692 rolloff_factor,
4693 EAXSOURCE_MINROLLOFFFACTOR,
4694 EAXSOURCE_MAXROLLOFFFACTOR);
4697 void ALsource::eax_validate_source_room_rolloff_factor(
4698 float room_rolloff_factor)
4700 eax_validate_range<EaxSourceException>(
4701 "Room Rolloff Factor",
4702 room_rolloff_factor,
4703 EAXSOURCE_MINROOMROLLOFFFACTOR,
4704 EAXSOURCE_MAXROOMROLLOFFFACTOR);
4707 void ALsource::eax_validate_source_air_absorption_factor(
4708 float air_absorption_factor)
4710 eax_validate_range<EaxSourceException>(
4711 "Air Absorption Factor",
4712 air_absorption_factor,
4713 EAXSOURCE_MINAIRABSORPTIONFACTOR,
4714 EAXSOURCE_MAXAIRABSORPTIONFACTOR);
4717 void ALsource::eax_validate_source_flags(
4718 unsigned long flags,
4719 int eax_version)
4721 eax_validate_range<EaxSourceException>(
4722 "Flags",
4723 flags,
4724 0UL,
4725 ~((eax_version == 5) ? EAX50SOURCEFLAGS_RESERVED : EAX20SOURCEFLAGS_RESERVED));
4728 void ALsource::eax_validate_source_macro_fx_factor(
4729 float macro_fx_factor)
4731 eax_validate_range<EaxSourceException>(
4732 "Macro FX Factor",
4733 macro_fx_factor,
4734 EAXSOURCE_MINMACROFXFACTOR,
4735 EAXSOURCE_MAXMACROFXFACTOR);
4738 void ALsource::eax_validate_source_2d_all(
4739 const EAXSOURCE2DPROPERTIES& all,
4740 int eax_version)
4742 eax_validate_source_direct(all.lDirect);
4743 eax_validate_source_direct_hf(all.lDirectHF);
4744 eax_validate_source_room(all.lRoom);
4745 eax_validate_source_room_hf(all.lRoomHF);
4746 eax_validate_source_flags(all.ulFlags, eax_version);
4749 void ALsource::eax_validate_source_obstruction_all(
4750 const EAXOBSTRUCTIONPROPERTIES& all)
4752 eax_validate_source_obstruction(all.lObstruction);
4753 eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio);
4756 void ALsource::eax_validate_source_exclusion_all(
4757 const EAXEXCLUSIONPROPERTIES& all)
4759 eax_validate_source_exclusion(all.lExclusion);
4760 eax_validate_source_exclusion_lf_ratio(all.flExclusionLFRatio);
4763 void ALsource::eax_validate_source_occlusion_all(
4764 const EAXOCCLUSIONPROPERTIES& all)
4766 eax_validate_source_occlusion(all.lOcclusion);
4767 eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
4768 eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
4769 eax_validate_source_occlusion_direct_ratio(all.flOcclusionDirectRatio);
4772 void ALsource::eax_validate_source_all(
4773 const EAX20BUFFERPROPERTIES& all,
4774 int eax_version)
4776 eax_validate_source_direct(all.lDirect);
4777 eax_validate_source_direct_hf(all.lDirectHF);
4778 eax_validate_source_room(all.lRoom);
4779 eax_validate_source_room_hf(all.lRoomHF);
4780 eax_validate_source_obstruction(all.lObstruction);
4781 eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio);
4782 eax_validate_source_occlusion(all.lOcclusion);
4783 eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
4784 eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
4785 eax_validate_source_outside_volume_hf(all.lOutsideVolumeHF);
4786 eax_validate_source_room_rolloff_factor(all.flRoomRolloffFactor);
4787 eax_validate_source_air_absorption_factor(all.flAirAbsorptionFactor);
4788 eax_validate_source_flags(all.dwFlags, eax_version);
4791 void ALsource::eax_validate_source_all(
4792 const EAX30SOURCEPROPERTIES& all,
4793 int eax_version)
4795 eax_validate_source_direct(all.lDirect);
4796 eax_validate_source_direct_hf(all.lDirectHF);
4797 eax_validate_source_room(all.lRoom);
4798 eax_validate_source_room_hf(all.lRoomHF);
4799 eax_validate_source_obstruction(all.lObstruction);
4800 eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio);
4801 eax_validate_source_occlusion(all.lOcclusion);
4802 eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
4803 eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
4804 eax_validate_source_occlusion_direct_ratio(all.flOcclusionDirectRatio);
4805 eax_validate_source_exclusion(all.lExclusion);
4806 eax_validate_source_exclusion_lf_ratio(all.flExclusionLFRatio);
4807 eax_validate_source_outside_volume_hf(all.lOutsideVolumeHF);
4808 eax_validate_source_doppler_factor(all.flDopplerFactor);
4809 eax_validate_source_rolloff_factor(all.flRolloffFactor);
4810 eax_validate_source_room_rolloff_factor(all.flRoomRolloffFactor);
4811 eax_validate_source_air_absorption_factor(all.flAirAbsorptionFactor);
4812 eax_validate_source_flags(all.ulFlags, eax_version);
4815 void ALsource::eax_validate_source_all(
4816 const EAX50SOURCEPROPERTIES& all,
4817 int eax_version)
4819 eax_validate_source_all(static_cast<EAX30SOURCEPROPERTIES>(all), eax_version);
4820 eax_validate_source_macro_fx_factor(all.flMacroFXFactor);
4823 void ALsource::eax_validate_source_speaker_id(
4824 long speaker_id)
4826 eax_validate_range<EaxSourceException>(
4827 "Speaker Id",
4828 speaker_id,
4829 static_cast<long>(EAXSPEAKER_FRONT_LEFT),
4830 static_cast<long>(EAXSPEAKER_LOW_FREQUENCY));
4833 void ALsource::eax_validate_source_speaker_level(
4834 long speaker_level)
4836 eax_validate_range<EaxSourceException>(
4837 "Speaker Level",
4838 speaker_level,
4839 EAXSOURCE_MINSPEAKERLEVEL,
4840 EAXSOURCE_MAXSPEAKERLEVEL);
4843 void ALsource::eax_validate_source_speaker_level_all(
4844 const EAXSPEAKERLEVELPROPERTIES& all)
4846 eax_validate_source_speaker_id(all.lSpeakerID);
4847 eax_validate_source_speaker_level(all.lLevel);
4850 void ALsource::eax_defer_source_direct(
4851 long lDirect)
4853 eax_d_.source.lDirect = lDirect;
4854 eax_source_dirty_filter_flags_.lDirect = (eax_.source.lDirect != eax_d_.source.lDirect);
4857 void ALsource::eax_defer_source_direct_hf(
4858 long lDirectHF)
4860 eax_d_.source.lDirectHF = lDirectHF;
4861 eax_source_dirty_filter_flags_.lDirectHF = (eax_.source.lDirectHF != eax_d_.source.lDirectHF);
4864 void ALsource::eax_defer_source_room(
4865 long lRoom)
4867 eax_d_.source.lRoom = lRoom;
4868 eax_source_dirty_filter_flags_.lRoom = (eax_.source.lRoom != eax_d_.source.lRoom);
4871 void ALsource::eax_defer_source_room_hf(
4872 long lRoomHF)
4874 eax_d_.source.lRoomHF = lRoomHF;
4875 eax_source_dirty_filter_flags_.lRoomHF = (eax_.source.lRoomHF != eax_d_.source.lRoomHF);
4878 void ALsource::eax_defer_source_obstruction(
4879 long lObstruction)
4881 eax_d_.source.lObstruction = lObstruction;
4882 eax_source_dirty_filter_flags_.lObstruction = (eax_.source.lObstruction != eax_d_.source.lObstruction);
4885 void ALsource::eax_defer_source_obstruction_lf_ratio(
4886 float flObstructionLFRatio)
4888 eax_d_.source.flObstructionLFRatio = flObstructionLFRatio;
4889 eax_source_dirty_filter_flags_.flObstructionLFRatio = (eax_.source.flObstructionLFRatio != eax_d_.source.flObstructionLFRatio);
4892 void ALsource::eax_defer_source_occlusion(
4893 long lOcclusion)
4895 eax_d_.source.lOcclusion = lOcclusion;
4896 eax_source_dirty_filter_flags_.lOcclusion = (eax_.source.lOcclusion != eax_d_.source.lOcclusion);
4899 void ALsource::eax_defer_source_occlusion_lf_ratio(
4900 float flOcclusionLFRatio)
4902 eax_d_.source.flOcclusionLFRatio = flOcclusionLFRatio;
4903 eax_source_dirty_filter_flags_.flOcclusionLFRatio = (eax_.source.flOcclusionLFRatio != eax_d_.source.flOcclusionLFRatio);
4906 void ALsource::eax_defer_source_occlusion_room_ratio(
4907 float flOcclusionRoomRatio)
4909 eax_d_.source.flOcclusionRoomRatio = flOcclusionRoomRatio;
4910 eax_source_dirty_filter_flags_.flOcclusionRoomRatio = (eax_.source.flOcclusionRoomRatio != eax_d_.source.flOcclusionRoomRatio);
4913 void ALsource::eax_defer_source_occlusion_direct_ratio(
4914 float flOcclusionDirectRatio)
4916 eax_d_.source.flOcclusionDirectRatio = flOcclusionDirectRatio;
4917 eax_source_dirty_filter_flags_.flOcclusionDirectRatio = (eax_.source.flOcclusionDirectRatio != eax_d_.source.flOcclusionDirectRatio);
4920 void ALsource::eax_defer_source_exclusion(
4921 long lExclusion)
4923 eax_d_.source.lExclusion = lExclusion;
4924 eax_source_dirty_filter_flags_.lExclusion = (eax_.source.lExclusion != eax_d_.source.lExclusion);
4927 void ALsource::eax_defer_source_exclusion_lf_ratio(
4928 float flExclusionLFRatio)
4930 eax_d_.source.flExclusionLFRatio = flExclusionLFRatio;
4931 eax_source_dirty_filter_flags_.flExclusionLFRatio = (eax_.source.flExclusionLFRatio != eax_d_.source.flExclusionLFRatio);
4934 void ALsource::eax_defer_source_outside_volume_hf(
4935 long lOutsideVolumeHF)
4937 eax_d_.source.lOutsideVolumeHF = lOutsideVolumeHF;
4938 eax_source_dirty_misc_flags_.lOutsideVolumeHF = (eax_.source.lOutsideVolumeHF != eax_d_.source.lOutsideVolumeHF);
4941 void ALsource::eax_defer_source_doppler_factor(
4942 float flDopplerFactor)
4944 eax_d_.source.flDopplerFactor = flDopplerFactor;
4945 eax_source_dirty_misc_flags_.flDopplerFactor = (eax_.source.flDopplerFactor != eax_d_.source.flDopplerFactor);
4948 void ALsource::eax_defer_source_rolloff_factor(
4949 float flRolloffFactor)
4951 eax_d_.source.flRolloffFactor = flRolloffFactor;
4952 eax_source_dirty_misc_flags_.flRolloffFactor = (eax_.source.flRolloffFactor != eax_d_.source.flRolloffFactor);
4955 void ALsource::eax_defer_source_room_rolloff_factor(
4956 float flRoomRolloffFactor)
4958 eax_d_.source.flRoomRolloffFactor = flRoomRolloffFactor;
4959 eax_source_dirty_misc_flags_.flRoomRolloffFactor = (eax_.source.flRoomRolloffFactor != eax_d_.source.flRoomRolloffFactor);
4962 void ALsource::eax_defer_source_air_absorption_factor(
4963 float flAirAbsorptionFactor)
4965 eax_d_.source.flAirAbsorptionFactor = flAirAbsorptionFactor;
4966 eax_source_dirty_misc_flags_.flAirAbsorptionFactor = (eax_.source.flAirAbsorptionFactor != eax_d_.source.flAirAbsorptionFactor);
4969 void ALsource::eax_defer_source_flags(
4970 unsigned long ulFlags)
4972 eax_d_.source.ulFlags = ulFlags;
4973 eax_source_dirty_misc_flags_.ulFlags = (eax_.source.ulFlags != eax_d_.source.ulFlags);
4976 void ALsource::eax_defer_source_macro_fx_factor(
4977 float flMacroFXFactor)
4979 eax_d_.source.flMacroFXFactor = flMacroFXFactor;
4980 eax_source_dirty_misc_flags_.flMacroFXFactor = (eax_.source.flMacroFXFactor != eax_d_.source.flMacroFXFactor);
4983 void ALsource::eax_defer_source_2d_all(
4984 const EAXSOURCE2DPROPERTIES& all)
4986 eax_defer_source_direct(all.lDirect);
4987 eax_defer_source_direct_hf(all.lDirectHF);
4988 eax_defer_source_room(all.lRoom);
4989 eax_defer_source_room_hf(all.lRoomHF);
4990 eax_defer_source_flags(all.ulFlags);
4993 void ALsource::eax_defer_source_obstruction_all(
4994 const EAXOBSTRUCTIONPROPERTIES& all)
4996 eax_defer_source_obstruction(all.lObstruction);
4997 eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio);
5000 void ALsource::eax_defer_source_exclusion_all(
5001 const EAXEXCLUSIONPROPERTIES& all)
5003 eax_defer_source_exclusion(all.lExclusion);
5004 eax_defer_source_exclusion_lf_ratio(all.flExclusionLFRatio);
5007 void ALsource::eax_defer_source_occlusion_all(
5008 const EAXOCCLUSIONPROPERTIES& all)
5010 eax_defer_source_occlusion(all.lOcclusion);
5011 eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
5012 eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
5013 eax_defer_source_occlusion_direct_ratio(all.flOcclusionDirectRatio);
5016 void ALsource::eax_defer_source_all(
5017 const EAX20BUFFERPROPERTIES& all)
5019 eax_defer_source_direct(all.lDirect);
5020 eax_defer_source_direct_hf(all.lDirectHF);
5021 eax_defer_source_room(all.lRoom);
5022 eax_defer_source_room_hf(all.lRoomHF);
5023 eax_defer_source_obstruction(all.lObstruction);
5024 eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio);
5025 eax_defer_source_occlusion(all.lOcclusion);
5026 eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
5027 eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
5028 eax_defer_source_outside_volume_hf(all.lOutsideVolumeHF);
5029 eax_defer_source_room_rolloff_factor(all.flRoomRolloffFactor);
5030 eax_defer_source_air_absorption_factor(all.flAirAbsorptionFactor);
5031 eax_defer_source_flags(all.dwFlags);
5034 void ALsource::eax_defer_source_all(
5035 const EAX30SOURCEPROPERTIES& all)
5037 eax_defer_source_direct(all.lDirect);
5038 eax_defer_source_direct_hf(all.lDirectHF);
5039 eax_defer_source_room(all.lRoom);
5040 eax_defer_source_room_hf(all.lRoomHF);
5041 eax_defer_source_obstruction(all.lObstruction);
5042 eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio);
5043 eax_defer_source_occlusion(all.lOcclusion);
5044 eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio);
5045 eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio);
5046 eax_defer_source_occlusion_direct_ratio(all.flOcclusionDirectRatio);
5047 eax_defer_source_exclusion(all.lExclusion);
5048 eax_defer_source_exclusion_lf_ratio(all.flExclusionLFRatio);
5049 eax_defer_source_outside_volume_hf(all.lOutsideVolumeHF);
5050 eax_defer_source_doppler_factor(all.flDopplerFactor);
5051 eax_defer_source_rolloff_factor(all.flRolloffFactor);
5052 eax_defer_source_room_rolloff_factor(all.flRoomRolloffFactor);
5053 eax_defer_source_air_absorption_factor(all.flAirAbsorptionFactor);
5054 eax_defer_source_flags(all.ulFlags);
5057 void ALsource::eax_defer_source_all(
5058 const EAX50SOURCEPROPERTIES& all)
5060 eax_defer_source_all(static_cast<const EAX30SOURCEPROPERTIES&>(all));
5061 eax_defer_source_macro_fx_factor(all.flMacroFXFactor);
5064 void ALsource::eax_defer_source_speaker_level_all(
5065 const EAXSPEAKERLEVELPROPERTIES& all)
5067 const auto speaker_index = static_cast<std::size_t>(all.lSpeakerID - 1);
5068 auto& speaker_level_d = eax_d_.speaker_levels[speaker_index];
5069 const auto& speaker_level = eax_.speaker_levels[speaker_index];
5071 if (speaker_level != speaker_level_d)
5073 eax_source_dirty_misc_flags_.speaker_levels = true;
5077 void ALsource::eax1_set_efx()
5079 const auto primary_fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index();
5081 if (!primary_fx_slot_index.has_value())
5082 return;
5084 WetGainAuto = (eax1_.fMix == EAX_REVERBMIX_USEDISTANCE);
5085 const auto filter_gain = (WetGainAuto ? 1.0F : eax1_.fMix);
5086 auto& fx_slot = eax_al_context_->eax_get_fx_slot(*primary_fx_slot_index);
5087 eax_set_al_source_send(&fx_slot, *primary_fx_slot_index, EaxAlLowPassParam{filter_gain, 1.0F});
5088 mPropsDirty = true;
5091 void ALsource::eax1_set_reverb_mix(const EaxEaxCall& eax_call)
5093 const auto reverb_mix = eax_call.get_value<EaxSourceException, const decltype(EAXBUFFER_REVERBPROPERTIES::fMix)>();
5094 eax1_validate_reverb_mix(reverb_mix);
5096 if (eax1_.fMix == reverb_mix)
5097 return;
5099 eax1_.fMix = reverb_mix;
5100 eax1_set_efx();
5103 void ALsource::eax_defer_source_direct(
5104 const EaxEaxCall& eax_call)
5106 const auto direct =
5107 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lDirect)>();
5109 eax_validate_source_direct(direct);
5110 eax_defer_source_direct(direct);
5113 void ALsource::eax_defer_source_direct_hf(
5114 const EaxEaxCall& eax_call)
5116 const auto direct_hf =
5117 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lDirectHF)>();
5119 eax_validate_source_direct_hf(direct_hf);
5120 eax_defer_source_direct_hf(direct_hf);
5123 void ALsource::eax_defer_source_room(
5124 const EaxEaxCall& eax_call)
5126 const auto room =
5127 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lRoom)>();
5129 eax_validate_source_room(room);
5130 eax_defer_source_room(room);
5133 void ALsource::eax_defer_source_room_hf(
5134 const EaxEaxCall& eax_call)
5136 const auto room_hf =
5137 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lRoomHF)>();
5139 eax_validate_source_room_hf(room_hf);
5140 eax_defer_source_room_hf(room_hf);
5143 void ALsource::eax_defer_source_obstruction(
5144 const EaxEaxCall& eax_call)
5146 const auto obstruction =
5147 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lObstruction)>();
5149 eax_validate_source_obstruction(obstruction);
5150 eax_defer_source_obstruction(obstruction);
5153 void ALsource::eax_defer_source_obstruction_lf_ratio(
5154 const EaxEaxCall& eax_call)
5156 const auto obstruction_lf_ratio =
5157 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flObstructionLFRatio)>();
5159 eax_validate_source_obstruction_lf_ratio(obstruction_lf_ratio);
5160 eax_defer_source_obstruction_lf_ratio(obstruction_lf_ratio);
5163 void ALsource::eax_defer_source_occlusion(
5164 const EaxEaxCall& eax_call)
5166 const auto occlusion =
5167 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lOcclusion)>();
5169 eax_validate_source_occlusion(occlusion);
5170 eax_defer_source_occlusion(occlusion);
5173 void ALsource::eax_defer_source_occlusion_lf_ratio(
5174 const EaxEaxCall& eax_call)
5176 const auto occlusion_lf_ratio =
5177 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flOcclusionLFRatio)>();
5179 eax_validate_source_occlusion_lf_ratio(occlusion_lf_ratio);
5180 eax_defer_source_occlusion_lf_ratio(occlusion_lf_ratio);
5183 void ALsource::eax_defer_source_occlusion_room_ratio(
5184 const EaxEaxCall& eax_call)
5186 const auto occlusion_room_ratio =
5187 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flOcclusionRoomRatio)>();
5189 eax_validate_source_occlusion_room_ratio(occlusion_room_ratio);
5190 eax_defer_source_occlusion_room_ratio(occlusion_room_ratio);
5193 void ALsource::eax_defer_source_occlusion_direct_ratio(
5194 const EaxEaxCall& eax_call)
5196 const auto occlusion_direct_ratio =
5197 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flOcclusionDirectRatio)>();
5199 eax_validate_source_occlusion_direct_ratio(occlusion_direct_ratio);
5200 eax_defer_source_occlusion_direct_ratio(occlusion_direct_ratio);
5203 void ALsource::eax_defer_source_exclusion(
5204 const EaxEaxCall& eax_call)
5206 const auto exclusion =
5207 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lExclusion)>();
5209 eax_validate_source_exclusion(exclusion);
5210 eax_defer_source_exclusion(exclusion);
5213 void ALsource::eax_defer_source_exclusion_lf_ratio(
5214 const EaxEaxCall& eax_call)
5216 const auto exclusion_lf_ratio =
5217 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flExclusionLFRatio)>();
5219 eax_validate_source_exclusion_lf_ratio(exclusion_lf_ratio);
5220 eax_defer_source_exclusion_lf_ratio(exclusion_lf_ratio);
5223 void ALsource::eax_defer_source_outside_volume_hf(
5224 const EaxEaxCall& eax_call)
5226 const auto outside_volume_hf =
5227 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lOutsideVolumeHF)>();
5229 eax_validate_source_outside_volume_hf(outside_volume_hf);
5230 eax_defer_source_outside_volume_hf(outside_volume_hf);
5233 void ALsource::eax_defer_source_doppler_factor(
5234 const EaxEaxCall& eax_call)
5236 const auto doppler_factor =
5237 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flDopplerFactor)>();
5239 eax_validate_source_doppler_factor(doppler_factor);
5240 eax_defer_source_doppler_factor(doppler_factor);
5243 void ALsource::eax_defer_source_rolloff_factor(
5244 const EaxEaxCall& eax_call)
5246 const auto rolloff_factor =
5247 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flRolloffFactor)>();
5249 eax_validate_source_rolloff_factor(rolloff_factor);
5250 eax_defer_source_rolloff_factor(rolloff_factor);
5253 void ALsource::eax_defer_source_room_rolloff_factor(
5254 const EaxEaxCall& eax_call)
5256 const auto room_rolloff_factor =
5257 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flRoomRolloffFactor)>();
5259 eax_validate_source_room_rolloff_factor(room_rolloff_factor);
5260 eax_defer_source_room_rolloff_factor(room_rolloff_factor);
5263 void ALsource::eax_defer_source_air_absorption_factor(
5264 const EaxEaxCall& eax_call)
5266 const auto air_absorption_factor =
5267 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flAirAbsorptionFactor)>();
5269 eax_validate_source_air_absorption_factor(air_absorption_factor);
5270 eax_defer_source_air_absorption_factor(air_absorption_factor);
5273 void ALsource::eax_defer_source_flags(
5274 const EaxEaxCall& eax_call)
5276 const auto flags =
5277 eax_call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::ulFlags)>();
5279 eax_validate_source_flags(flags, eax_call.get_version());
5280 eax_defer_source_flags(flags);
5283 void ALsource::eax_defer_source_macro_fx_factor(
5284 const EaxEaxCall& eax_call)
5286 const auto macro_fx_factor =
5287 eax_call.get_value<EaxSourceException, const decltype(EAX50SOURCEPROPERTIES::flMacroFXFactor)>();
5289 eax_validate_source_macro_fx_factor(macro_fx_factor);
5290 eax_defer_source_macro_fx_factor(macro_fx_factor);
5293 void ALsource::eax_defer_source_2d_all(
5294 const EaxEaxCall& eax_call)
5296 const auto all = eax_call.get_value<EaxSourceException, const EAXSOURCE2DPROPERTIES>();
5298 eax_validate_source_2d_all(all, eax_call.get_version());
5299 eax_defer_source_2d_all(all);
5302 void ALsource::eax_defer_source_obstruction_all(
5303 const EaxEaxCall& eax_call)
5305 const auto all = eax_call.get_value<EaxSourceException, const EAXOBSTRUCTIONPROPERTIES>();
5307 eax_validate_source_obstruction_all(all);
5308 eax_defer_source_obstruction_all(all);
5311 void ALsource::eax_defer_source_exclusion_all(
5312 const EaxEaxCall& eax_call)
5314 const auto all = eax_call.get_value<EaxSourceException, const EAXEXCLUSIONPROPERTIES>();
5316 eax_validate_source_exclusion_all(all);
5317 eax_defer_source_exclusion_all(all);
5320 void ALsource::eax_defer_source_occlusion_all(
5321 const EaxEaxCall& eax_call)
5323 const auto all = eax_call.get_value<EaxSourceException, const EAXOCCLUSIONPROPERTIES>();
5325 eax_validate_source_occlusion_all(all);
5326 eax_defer_source_occlusion_all(all);
5329 void ALsource::eax_defer_source_all(
5330 const EaxEaxCall& eax_call)
5332 const auto eax_version = eax_call.get_version();
5334 if (eax_version == 2)
5336 const auto all = eax_call.get_value<EaxSourceException, const EAX20BUFFERPROPERTIES>();
5338 eax_validate_source_all(all, eax_version);
5339 eax_defer_source_all(all);
5341 else if (eax_version < 5)
5343 const auto all = eax_call.get_value<EaxSourceException, const EAX30SOURCEPROPERTIES>();
5345 eax_validate_source_all(all, eax_version);
5346 eax_defer_source_all(all);
5348 else
5350 const auto all = eax_call.get_value<EaxSourceException, const EAX50SOURCEPROPERTIES>();
5352 eax_validate_source_all(all, eax_version);
5353 eax_defer_source_all(all);
5357 void ALsource::eax_defer_source_speaker_level_all(
5358 const EaxEaxCall& eax_call)
5360 const auto speaker_level_properties = eax_call.get_value<EaxSourceException, const EAXSPEAKERLEVELPROPERTIES>();
5362 eax_validate_source_speaker_level_all(speaker_level_properties);
5363 eax_defer_source_speaker_level_all(speaker_level_properties);
5366 void ALsource::eax_set_outside_volume_hf()
5368 const auto efx_gain_hf = clamp(
5369 level_mb_to_gain(static_cast<float>(eax_.source.lOutsideVolumeHF)),
5370 AL_MIN_CONE_OUTER_GAINHF,
5371 AL_MAX_CONE_OUTER_GAINHF
5374 OuterGainHF = efx_gain_hf;
5377 void ALsource::eax_set_doppler_factor()
5379 DopplerFactor = eax_.source.flDopplerFactor;
5382 void ALsource::eax_set_rolloff_factor()
5384 RolloffFactor2 = eax_.source.flRolloffFactor;
5387 void ALsource::eax_set_room_rolloff_factor()
5389 RoomRolloffFactor = eax_.source.flRoomRolloffFactor;
5392 void ALsource::eax_set_air_absorption_factor()
5394 AirAbsorptionFactor = eax_.source.flAirAbsorptionFactor;
5397 void ALsource::eax_set_direct_hf_auto_flag()
5399 const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_DIRECTHFAUTO) != 0;
5401 DryGainHFAuto = is_enable;
5404 void ALsource::eax_set_room_auto_flag()
5406 const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMAUTO) != 0;
5408 WetGainAuto = is_enable;
5411 void ALsource::eax_set_room_hf_auto_flag()
5413 const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMHFAUTO) != 0;
5415 WetGainHFAuto = is_enable;
5418 void ALsource::eax_set_flags()
5420 eax_set_direct_hf_auto_flag();
5421 eax_set_room_auto_flag();
5422 eax_set_room_hf_auto_flag();
5423 eax_set_speaker_levels();
5426 void ALsource::eax_set_macro_fx_factor()
5428 // TODO
5431 void ALsource::eax_set_speaker_levels()
5433 // TODO
5436 void ALsource::eax1_set(const EaxEaxCall& eax_call)
5438 switch (eax_call.get_property_id())
5440 case DSPROPERTY_EAXBUFFER_ALL:
5441 case DSPROPERTY_EAXBUFFER_REVERBMIX:
5442 eax1_set_reverb_mix(eax_call);
5443 break;
5445 default:
5446 eax_fail("Unsupported property id.");
5450 void ALsource::eax_apply_deferred()
5452 if (!eax_are_active_fx_slots_dirty_ &&
5453 eax_sends_dirty_flags_ == EaxSourceSendsDirtyFlags{} &&
5454 eax_source_dirty_filter_flags_ == EaxSourceSourceFilterDirtyFlags{} &&
5455 eax_source_dirty_misc_flags_ == EaxSourceSourceMiscDirtyFlags{})
5457 return;
5460 eax_ = eax_d_;
5462 if (eax_are_active_fx_slots_dirty_)
5464 eax_are_active_fx_slots_dirty_ = false;
5465 eax_set_fx_slots();
5466 eax_update_filters_internal();
5468 else if (eax_has_active_fx_slots_)
5470 if (eax_source_dirty_filter_flags_ != EaxSourceSourceFilterDirtyFlags{})
5472 eax_update_filters_internal();
5474 else if (eax_sends_dirty_flags_ != EaxSourceSendsDirtyFlags{})
5476 for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i)
5478 if (eax_active_fx_slots_[i])
5480 if (eax_sends_dirty_flags_.sends[i] != EaxSourceSendDirtyFlags{})
5482 eax_update_filters_internal();
5483 break;
5490 if (eax_source_dirty_misc_flags_ != EaxSourceSourceMiscDirtyFlags{})
5492 if (eax_source_dirty_misc_flags_.lOutsideVolumeHF)
5494 eax_set_outside_volume_hf();
5497 if (eax_source_dirty_misc_flags_.flDopplerFactor)
5499 eax_set_doppler_factor();
5502 if (eax_source_dirty_misc_flags_.flRolloffFactor)
5504 eax_set_rolloff_factor();
5507 if (eax_source_dirty_misc_flags_.flRoomRolloffFactor)
5509 eax_set_room_rolloff_factor();
5512 if (eax_source_dirty_misc_flags_.flAirAbsorptionFactor)
5514 eax_set_air_absorption_factor();
5517 if (eax_source_dirty_misc_flags_.ulFlags)
5519 eax_set_flags();
5522 if (eax_source_dirty_misc_flags_.flMacroFXFactor)
5524 eax_set_macro_fx_factor();
5527 mPropsDirty = true;
5529 eax_source_dirty_misc_flags_ = EaxSourceSourceMiscDirtyFlags{};
5532 eax_sends_dirty_flags_ = EaxSourceSendsDirtyFlags{};
5533 eax_source_dirty_filter_flags_ = EaxSourceSourceFilterDirtyFlags{};
5536 void ALsource::eax_set(
5537 const EaxEaxCall& eax_call)
5539 if (eax_call.get_version() == 1)
5541 eax1_set(eax_call);
5542 return;
5545 switch (eax_call.get_property_id())
5547 case EAXSOURCE_NONE:
5548 break;
5550 case EAXSOURCE_ALLPARAMETERS:
5551 eax_defer_source_all(eax_call);
5552 break;
5554 case EAXSOURCE_OBSTRUCTIONPARAMETERS:
5555 eax_defer_source_obstruction_all(eax_call);
5556 break;
5558 case EAXSOURCE_OCCLUSIONPARAMETERS:
5559 eax_defer_source_occlusion_all(eax_call);
5560 break;
5562 case EAXSOURCE_EXCLUSIONPARAMETERS:
5563 eax_defer_source_exclusion_all(eax_call);
5564 break;
5566 case EAXSOURCE_DIRECT:
5567 eax_defer_source_direct(eax_call);
5568 break;
5570 case EAXSOURCE_DIRECTHF:
5571 eax_defer_source_direct_hf(eax_call);
5572 break;
5574 case EAXSOURCE_ROOM:
5575 eax_defer_source_room(eax_call);
5576 break;
5578 case EAXSOURCE_ROOMHF:
5579 eax_defer_source_room_hf(eax_call);
5580 break;
5582 case EAXSOURCE_OBSTRUCTION:
5583 eax_defer_source_obstruction(eax_call);
5584 break;
5586 case EAXSOURCE_OBSTRUCTIONLFRATIO:
5587 eax_defer_source_obstruction_lf_ratio(eax_call);
5588 break;
5590 case EAXSOURCE_OCCLUSION:
5591 eax_defer_source_occlusion(eax_call);
5592 break;
5594 case EAXSOURCE_OCCLUSIONLFRATIO:
5595 eax_defer_source_occlusion_lf_ratio(eax_call);
5596 break;
5598 case EAXSOURCE_OCCLUSIONROOMRATIO:
5599 eax_defer_source_occlusion_room_ratio(eax_call);
5600 break;
5602 case EAXSOURCE_OCCLUSIONDIRECTRATIO:
5603 eax_defer_source_occlusion_direct_ratio(eax_call);
5604 break;
5606 case EAXSOURCE_EXCLUSION:
5607 eax_defer_source_exclusion(eax_call);
5608 break;
5610 case EAXSOURCE_EXCLUSIONLFRATIO:
5611 eax_defer_source_exclusion_lf_ratio(eax_call);
5612 break;
5614 case EAXSOURCE_OUTSIDEVOLUMEHF:
5615 eax_defer_source_outside_volume_hf(eax_call);
5616 break;
5618 case EAXSOURCE_DOPPLERFACTOR:
5619 eax_defer_source_doppler_factor(eax_call);
5620 break;
5622 case EAXSOURCE_ROLLOFFFACTOR:
5623 eax_defer_source_rolloff_factor(eax_call);
5624 break;
5626 case EAXSOURCE_ROOMROLLOFFFACTOR:
5627 eax_defer_source_room_rolloff_factor(eax_call);
5628 break;
5630 case EAXSOURCE_AIRABSORPTIONFACTOR:
5631 eax_defer_source_air_absorption_factor(eax_call);
5632 break;
5634 case EAXSOURCE_FLAGS:
5635 eax_defer_source_flags(eax_call);
5636 break;
5638 case EAXSOURCE_SENDPARAMETERS:
5639 eax_defer_send(eax_call);
5640 break;
5642 case EAXSOURCE_ALLSENDPARAMETERS:
5643 eax_defer_send_all(eax_call);
5644 break;
5646 case EAXSOURCE_OCCLUSIONSENDPARAMETERS:
5647 eax_defer_send_occlusion_all(eax_call);
5648 break;
5650 case EAXSOURCE_EXCLUSIONSENDPARAMETERS:
5651 eax_defer_send_exclusion_all(eax_call);
5652 break;
5654 case EAXSOURCE_ACTIVEFXSLOTID:
5655 eax_defer_active_fx_slots(eax_call);
5656 break;
5658 case EAXSOURCE_MACROFXFACTOR:
5659 eax_defer_source_macro_fx_factor(eax_call);
5660 break;
5662 case EAXSOURCE_SPEAKERLEVELS:
5663 eax_defer_source_speaker_level_all(eax_call);
5664 break;
5666 case EAXSOURCE_ALL2DPARAMETERS:
5667 eax_defer_source_2d_all(eax_call);
5668 break;
5670 default:
5671 eax_fail("Unsupported property id.");
5675 const GUID& ALsource::eax_get_send_fx_slot_guid(
5676 int eax_version,
5677 EaxFxSlotIndexValue fx_slot_index)
5679 switch (eax_version)
5681 case 4:
5682 switch (fx_slot_index)
5684 case 0:
5685 return EAXPROPERTYID_EAX40_FXSlot0;
5687 case 1:
5688 return EAXPROPERTYID_EAX40_FXSlot1;
5690 case 2:
5691 return EAXPROPERTYID_EAX40_FXSlot2;
5693 case 3:
5694 return EAXPROPERTYID_EAX40_FXSlot3;
5696 default:
5697 eax_fail("FX slot index out of range.");
5700 case 5:
5701 switch (fx_slot_index)
5703 case 0:
5704 return EAXPROPERTYID_EAX50_FXSlot0;
5706 case 1:
5707 return EAXPROPERTYID_EAX50_FXSlot1;
5709 case 2:
5710 return EAXPROPERTYID_EAX50_FXSlot2;
5712 case 3:
5713 return EAXPROPERTYID_EAX50_FXSlot3;
5715 default:
5716 eax_fail("FX slot index out of range.");
5719 default:
5720 eax_fail("Unsupported EAX version.");
5724 void ALsource::eax_copy_send(
5725 const EAXSOURCEALLSENDPROPERTIES& src_send,
5726 EAXSOURCESENDPROPERTIES& dst_send)
5728 dst_send.lSend = src_send.lSend;
5729 dst_send.lSendHF = src_send.lSendHF;
5732 void ALsource::eax_copy_send(
5733 const EAXSOURCEALLSENDPROPERTIES& src_send,
5734 EAXSOURCEALLSENDPROPERTIES& dst_send)
5736 dst_send = src_send;
5739 void ALsource::eax_copy_send(
5740 const EAXSOURCEALLSENDPROPERTIES& src_send,
5741 EAXSOURCEOCCLUSIONSENDPROPERTIES& dst_send)
5743 dst_send.lOcclusion = src_send.lOcclusion;
5744 dst_send.flOcclusionLFRatio = src_send.flOcclusionLFRatio;
5745 dst_send.flOcclusionRoomRatio = src_send.flOcclusionRoomRatio;
5746 dst_send.flOcclusionDirectRatio = src_send.flOcclusionDirectRatio;
5749 void ALsource::eax_copy_send(
5750 const EAXSOURCEALLSENDPROPERTIES& src_send,
5751 EAXSOURCEEXCLUSIONSENDPROPERTIES& dst_send)
5753 dst_send.lExclusion = src_send.lExclusion;
5754 dst_send.flExclusionLFRatio = src_send.flExclusionLFRatio;
5757 void ALsource::eax1_get(const EaxEaxCall& eax_call)
5759 switch (eax_call.get_property_id())
5761 case DSPROPERTY_EAXBUFFER_ALL:
5762 case DSPROPERTY_EAXBUFFER_REVERBMIX:
5763 eax_call.set_value<EaxSourceException>(eax1_);
5764 break;
5766 default:
5767 eax_fail("Unsupported property id.");
5771 void ALsource::eax_api_get_source_all_v2(
5772 const EaxEaxCall& eax_call)
5774 auto eax_2_all = EAX20BUFFERPROPERTIES{};
5775 eax_2_all.lDirect = eax_.source.lDirect;
5776 eax_2_all.lDirectHF = eax_.source.lDirectHF;
5777 eax_2_all.lRoom = eax_.source.lRoom;
5778 eax_2_all.lRoomHF = eax_.source.lRoomHF;
5779 eax_2_all.flRoomRolloffFactor = eax_.source.flRoomRolloffFactor;
5780 eax_2_all.lObstruction = eax_.source.lObstruction;
5781 eax_2_all.flObstructionLFRatio = eax_.source.flObstructionLFRatio;
5782 eax_2_all.lOcclusion = eax_.source.lOcclusion;
5783 eax_2_all.flOcclusionLFRatio = eax_.source.flOcclusionLFRatio;
5784 eax_2_all.flOcclusionRoomRatio = eax_.source.flOcclusionRoomRatio;
5785 eax_2_all.lOutsideVolumeHF = eax_.source.lOutsideVolumeHF;
5786 eax_2_all.flAirAbsorptionFactor = eax_.source.flAirAbsorptionFactor;
5787 eax_2_all.dwFlags = eax_.source.ulFlags;
5789 eax_call.set_value<EaxSourceException>(eax_2_all);
5792 void ALsource::eax_api_get_source_all_v3(
5793 const EaxEaxCall& eax_call)
5795 eax_call.set_value<EaxSourceException>(static_cast<const EAX30SOURCEPROPERTIES&>(eax_.source));
5798 void ALsource::eax_api_get_source_all_v5(
5799 const EaxEaxCall& eax_call)
5801 eax_call.set_value<EaxSourceException>(eax_.source);
5804 void ALsource::eax_api_get_source_all(
5805 const EaxEaxCall& eax_call)
5807 switch (eax_call.get_version())
5809 case 2:
5810 eax_api_get_source_all_v2(eax_call);
5811 break;
5813 case 3:
5814 case 4:
5815 eax_api_get_source_all_v3(eax_call);
5816 break;
5818 case 5:
5819 eax_api_get_source_all_v5(eax_call);
5820 break;
5822 default:
5823 eax_fail("Unsupported EAX version.");
5827 void ALsource::eax_api_get_source_all_obstruction(
5828 const EaxEaxCall& eax_call)
5830 auto eax_obstruction_all = EAXOBSTRUCTIONPROPERTIES{};
5831 eax_obstruction_all.lObstruction = eax_.source.lObstruction;
5832 eax_obstruction_all.flObstructionLFRatio = eax_.source.flObstructionLFRatio;
5834 eax_call.set_value<EaxSourceException>(eax_obstruction_all);
5837 void ALsource::eax_api_get_source_all_occlusion(
5838 const EaxEaxCall& eax_call)
5840 auto eax_occlusion_all = EAXOCCLUSIONPROPERTIES{};
5841 eax_occlusion_all.lOcclusion = eax_.source.lOcclusion;
5842 eax_occlusion_all.flOcclusionLFRatio = eax_.source.flOcclusionLFRatio;
5843 eax_occlusion_all.flOcclusionRoomRatio = eax_.source.flOcclusionRoomRatio;
5844 eax_occlusion_all.flOcclusionDirectRatio = eax_.source.flOcclusionDirectRatio;
5846 eax_call.set_value<EaxSourceException>(eax_occlusion_all);
5849 void ALsource::eax_api_get_source_all_exclusion(
5850 const EaxEaxCall& eax_call)
5852 auto eax_exclusion_all = EAXEXCLUSIONPROPERTIES{};
5853 eax_exclusion_all.lExclusion = eax_.source.lExclusion;
5854 eax_exclusion_all.flExclusionLFRatio = eax_.source.flExclusionLFRatio;
5856 eax_call.set_value<EaxSourceException>(eax_exclusion_all);
5859 void ALsource::eax_api_get_source_active_fx_slot_id(
5860 const EaxEaxCall& eax_call)
5862 switch (eax_call.get_version())
5864 case 4:
5866 const auto& active_fx_slots = reinterpret_cast<const EAX40ACTIVEFXSLOTS&>(eax_.active_fx_slots);
5867 eax_call.set_value<EaxSourceException>(active_fx_slots);
5869 break;
5871 case 5:
5873 const auto& active_fx_slots = reinterpret_cast<const EAX50ACTIVEFXSLOTS&>(eax_.active_fx_slots);
5874 eax_call.set_value<EaxSourceException>(active_fx_slots);
5876 break;
5878 default:
5879 eax_fail("Unsupported EAX version.");
5883 void ALsource::eax_api_get_source_all_2d(
5884 const EaxEaxCall& eax_call)
5886 auto eax_2d_all = EAXSOURCE2DPROPERTIES{};
5887 eax_2d_all.lDirect = eax_.source.lDirect;
5888 eax_2d_all.lDirectHF = eax_.source.lDirectHF;
5889 eax_2d_all.lRoom = eax_.source.lRoom;
5890 eax_2d_all.lRoomHF = eax_.source.lRoomHF;
5891 eax_2d_all.ulFlags = eax_.source.ulFlags;
5893 eax_call.set_value<EaxSourceException>(eax_2d_all);
5896 void ALsource::eax_api_get_source_speaker_level_all(
5897 const EaxEaxCall& eax_call)
5899 auto& all = eax_call.get_value<EaxSourceException, EAXSPEAKERLEVELPROPERTIES>();
5901 eax_validate_source_speaker_id(all.lSpeakerID);
5902 const auto speaker_index = static_cast<std::size_t>(all.lSpeakerID - 1);
5903 all.lLevel = eax_.speaker_levels[speaker_index];
5906 void ALsource::eax_get(
5907 const EaxEaxCall& eax_call)
5909 if (eax_call.get_version() == 1)
5911 eax1_get(eax_call);
5912 return;
5915 switch (eax_call.get_property_id())
5917 case EAXSOURCE_NONE:
5918 break;
5920 case EAXSOURCE_ALLPARAMETERS:
5921 eax_api_get_source_all(eax_call);
5922 break;
5924 case EAXSOURCE_OBSTRUCTIONPARAMETERS:
5925 eax_api_get_source_all_obstruction(eax_call);
5926 break;
5928 case EAXSOURCE_OCCLUSIONPARAMETERS:
5929 eax_api_get_source_all_occlusion(eax_call);
5930 break;
5932 case EAXSOURCE_EXCLUSIONPARAMETERS:
5933 eax_api_get_source_all_exclusion(eax_call);
5934 break;
5936 case EAXSOURCE_DIRECT:
5937 eax_call.set_value<EaxSourceException>(eax_.source.lDirect);
5938 break;
5940 case EAXSOURCE_DIRECTHF:
5941 eax_call.set_value<EaxSourceException>(eax_.source.lDirectHF);
5942 break;
5944 case EAXSOURCE_ROOM:
5945 eax_call.set_value<EaxSourceException>(eax_.source.lRoom);
5946 break;
5948 case EAXSOURCE_ROOMHF:
5949 eax_call.set_value<EaxSourceException>(eax_.source.lRoomHF);
5950 break;
5952 case EAXSOURCE_OBSTRUCTION:
5953 eax_call.set_value<EaxSourceException>(eax_.source.lObstruction);
5954 break;
5956 case EAXSOURCE_OBSTRUCTIONLFRATIO:
5957 eax_call.set_value<EaxSourceException>(eax_.source.flObstructionLFRatio);
5958 break;
5960 case EAXSOURCE_OCCLUSION:
5961 eax_call.set_value<EaxSourceException>(eax_.source.lOcclusion);
5962 break;
5964 case EAXSOURCE_OCCLUSIONLFRATIO:
5965 eax_call.set_value<EaxSourceException>(eax_.source.flOcclusionLFRatio);
5966 break;
5968 case EAXSOURCE_OCCLUSIONROOMRATIO:
5969 eax_call.set_value<EaxSourceException>(eax_.source.flOcclusionRoomRatio);
5970 break;
5972 case EAXSOURCE_OCCLUSIONDIRECTRATIO:
5973 eax_call.set_value<EaxSourceException>(eax_.source.flOcclusionDirectRatio);
5974 break;
5976 case EAXSOURCE_EXCLUSION:
5977 eax_call.set_value<EaxSourceException>(eax_.source.lExclusion);
5978 break;
5980 case EAXSOURCE_EXCLUSIONLFRATIO:
5981 eax_call.set_value<EaxSourceException>(eax_.source.flExclusionLFRatio);
5982 break;
5984 case EAXSOURCE_OUTSIDEVOLUMEHF:
5985 eax_call.set_value<EaxSourceException>(eax_.source.lOutsideVolumeHF);
5986 break;
5988 case EAXSOURCE_DOPPLERFACTOR:
5989 eax_call.set_value<EaxSourceException>(eax_.source.flDopplerFactor);
5990 break;
5992 case EAXSOURCE_ROLLOFFFACTOR:
5993 eax_call.set_value<EaxSourceException>(eax_.source.flRolloffFactor);
5994 break;
5996 case EAXSOURCE_ROOMROLLOFFFACTOR:
5997 eax_call.set_value<EaxSourceException>(eax_.source.flRoomRolloffFactor);
5998 break;
6000 case EAXSOURCE_AIRABSORPTIONFACTOR:
6001 eax_call.set_value<EaxSourceException>(eax_.source.flAirAbsorptionFactor);
6002 break;
6004 case EAXSOURCE_FLAGS:
6005 eax_call.set_value<EaxSourceException>(eax_.source.ulFlags);
6006 break;
6008 case EAXSOURCE_SENDPARAMETERS:
6009 eax_api_get_send_properties<EaxSourceException, EAXSOURCESENDPROPERTIES>(eax_call);
6010 break;
6012 case EAXSOURCE_ALLSENDPARAMETERS:
6013 eax_api_get_send_properties<EaxSourceException, EAXSOURCEALLSENDPROPERTIES>(eax_call);
6014 break;
6016 case EAXSOURCE_OCCLUSIONSENDPARAMETERS:
6017 eax_api_get_send_properties<EaxSourceException, EAXSOURCEOCCLUSIONSENDPROPERTIES>(eax_call);
6018 break;
6020 case EAXSOURCE_EXCLUSIONSENDPARAMETERS:
6021 eax_api_get_send_properties<EaxSourceException, EAXSOURCEEXCLUSIONSENDPROPERTIES>(eax_call);
6022 break;
6024 case EAXSOURCE_ACTIVEFXSLOTID:
6025 eax_api_get_source_active_fx_slot_id(eax_call);
6026 break;
6028 case EAXSOURCE_MACROFXFACTOR:
6029 eax_call.set_value<EaxSourceException>(eax_.source.flMacroFXFactor);
6030 break;
6032 case EAXSOURCE_SPEAKERLEVELS:
6033 eax_api_get_source_speaker_level_all(eax_call);
6034 break;
6036 case EAXSOURCE_ALL2DPARAMETERS:
6037 eax_api_get_source_all_2d(eax_call);
6038 break;
6040 default:
6041 eax_fail("Unsupported property id.");
6045 void ALsource::eax_set_al_source_send(
6046 ALeffectslot *slot,
6047 size_t sendidx,
6048 const EaxAlLowPassParam &filter)
6050 if(sendidx >= EAX_MAX_FXSLOTS)
6051 return;
6053 auto &send = Send[sendidx];
6054 send.Gain = filter.gain;
6055 send.GainHF = filter.gain_hf;
6056 send.HFReference = LOWPASSFREQREF;
6057 send.GainLF = 1.0f;
6058 send.LFReference = HIGHPASSFREQREF;
6060 if(slot) IncrementRef(slot->ref);
6061 if(auto *oldslot = send.Slot)
6062 DecrementRef(oldslot->ref);
6063 send.Slot = slot;
6065 mPropsDirty = true;
6068 #endif // ALSOFT_EAX