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