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