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
44 #include <type_traits>
45 #include <unordered_map>
55 #include "alc/backends/base.h"
56 #include "alc/context.h"
57 #include "alc/device.h"
58 #include "alc/inprogext.h"
60 #include "alnumeric.h"
63 #include "auxeffectslot.h"
65 #include "core/buffer_storage.h"
66 #include "core/logging.h"
67 #include "core/mixer/defs.h"
68 #include "core/voice_change.h"
69 #include "direct_defs.h"
72 #include "flexarray.h"
73 #include "intrusive_ptr.h"
74 #include "opthelpers.h"
79 #include "eax/fx_slot_index.h"
80 #include "eax/utils.h"
85 using SubListAllocator
= al::allocator
<std::array
<ALsource
,64>>;
86 using std::chrono::nanoseconds
;
87 using seconds_d
= std::chrono::duration
<double>;
88 using source_store_array
= std::array
<ALsource
*,3>;
89 using source_store_vector
= std::vector
<ALsource
*>;
90 using source_store_variant
= std::variant
<std::monostate
,source_store_array
,source_store_vector
>;
93 Voice
*GetSourceVoice(ALsource
*source
, ALCcontext
*context
)
95 auto voicelist
= context
->getVoicesSpan();
96 ALuint idx
{source
->VoiceIdx
};
97 if(idx
< voicelist
.size())
99 ALuint sid
{source
->id
};
100 Voice
*voice
= voicelist
[idx
];
101 if(voice
->mSourceID
.load(std::memory_order_acquire
) == sid
)
104 source
->VoiceIdx
= InvalidVoiceIndex
;
109 void UpdateSourceProps(const ALsource
*source
, Voice
*voice
, ALCcontext
*context
)
111 /* Get an unused property container, or allocate a new one as needed. */
112 VoicePropsItem
*props
{context
->mFreeVoiceProps
.load(std::memory_order_acquire
)};
115 context
->allocVoiceProps();
116 props
= context
->mFreeVoiceProps
.load(std::memory_order_acquire
);
118 VoicePropsItem
*next
;
120 next
= props
->next
.load(std::memory_order_relaxed
);
121 } while(context
->mFreeVoiceProps
.compare_exchange_weak(props
, next
,
122 std::memory_order_acq_rel
, std::memory_order_acquire
) == false);
124 props
->Pitch
= source
->Pitch
;
125 props
->Gain
= source
->Gain
;
126 props
->OuterGain
= source
->OuterGain
;
127 props
->MinGain
= source
->MinGain
;
128 props
->MaxGain
= source
->MaxGain
;
129 props
->InnerAngle
= source
->InnerAngle
;
130 props
->OuterAngle
= source
->OuterAngle
;
131 props
->RefDistance
= source
->RefDistance
;
132 props
->MaxDistance
= source
->MaxDistance
;
133 props
->RolloffFactor
= source
->RolloffFactor
135 + source
->RolloffFactor2
138 props
->Position
= source
->Position
;
139 props
->Velocity
= source
->Velocity
;
140 props
->Direction
= source
->Direction
;
141 props
->OrientAt
= source
->OrientAt
;
142 props
->OrientUp
= source
->OrientUp
;
143 props
->HeadRelative
= source
->HeadRelative
;
144 props
->mDistanceModel
= source
->mDistanceModel
;
145 props
->mResampler
= source
->mResampler
;
146 props
->DirectChannels
= source
->DirectChannels
;
147 props
->mSpatializeMode
= source
->mSpatialize
;
149 props
->DryGainHFAuto
= source
->DryGainHFAuto
;
150 props
->WetGainAuto
= source
->WetGainAuto
;
151 props
->WetGainHFAuto
= source
->WetGainHFAuto
;
152 props
->OuterGainHF
= source
->OuterGainHF
;
154 props
->AirAbsorptionFactor
= source
->AirAbsorptionFactor
;
155 props
->RoomRolloffFactor
= source
->RoomRolloffFactor
;
156 props
->DopplerFactor
= source
->DopplerFactor
;
158 props
->StereoPan
= source
->StereoPan
;
160 props
->Radius
= source
->Radius
;
161 props
->EnhWidth
= source
->EnhWidth
;
162 props
->Panning
= source
->mPanningEnabled
? source
->mPan
: 0.0f
;
164 props
->Direct
.Gain
= source
->Direct
.Gain
;
165 props
->Direct
.GainHF
= source
->Direct
.GainHF
;
166 props
->Direct
.HFReference
= source
->Direct
.HFReference
;
167 props
->Direct
.GainLF
= source
->Direct
.GainLF
;
168 props
->Direct
.LFReference
= source
->Direct
.LFReference
;
170 auto copy_send
= [](const ALsource::SendData
&srcsend
) noexcept
-> VoiceProps::SendData
172 VoiceProps::SendData ret
{};
173 ret
.Slot
= srcsend
.Slot
? srcsend
.Slot
->mSlot
: nullptr;
174 ret
.Gain
= srcsend
.Gain
;
175 ret
.GainHF
= srcsend
.GainHF
;
176 ret
.HFReference
= srcsend
.HFReference
;
177 ret
.GainLF
= srcsend
.GainLF
;
178 ret
.LFReference
= srcsend
.LFReference
;
181 std::transform(source
->Send
.cbegin(), source
->Send
.cend(), props
->Send
.begin(), copy_send
);
182 if(!props
->Send
[0].Slot
&& context
->mDefaultSlot
)
183 props
->Send
[0].Slot
= context
->mDefaultSlot
->mSlot
;
185 /* Set the new container for updating internal parameters. */
186 props
= voice
->mUpdate
.exchange(props
, std::memory_order_acq_rel
);
189 /* If there was an unused update container, put it back in the
192 AtomicReplaceHead(context
->mFreeVoiceProps
, props
);
196 /* GetSourceSampleOffset
198 * Gets the current read offset for the given Source, in 32.32 fixed-point
199 * samples. The offset is relative to the start of the queue (not the start of
200 * the current buffer).
202 int64_t GetSourceSampleOffset(ALsource
*Source
, ALCcontext
*context
, nanoseconds
*clocktime
)
204 auto *device
= context
->mALDevice
.get();
205 const VoiceBufferItem
*Current
{};
211 refcount
= device
->waitForMix();
212 *clocktime
= device
->getClockTime();
213 voice
= GetSourceVoice(Source
, context
);
216 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
218 readPos
= int64_t{voice
->mPosition
.load(std::memory_order_relaxed
)} << MixerFracBits
;
219 readPos
+= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
221 std::atomic_thread_fence(std::memory_order_acquire
);
222 } while(refcount
!= device
->mMixCount
.load(std::memory_order_relaxed
));
227 for(auto &item
: Source
->mQueue
)
229 if(&item
== Current
) break;
230 readPos
+= int64_t{item
.mSampleLen
} << MixerFracBits
;
232 if(readPos
> std::numeric_limits
<int64_t>::max() >> (32-MixerFracBits
))
233 return std::numeric_limits
<int64_t>::max();
234 return readPos
<< (32-MixerFracBits
);
237 /* GetSourceSecOffset
239 * Gets the current read offset for the given Source, in seconds. The offset is
240 * relative to the start of the queue (not the start of the current buffer).
242 double GetSourceSecOffset(ALsource
*Source
, ALCcontext
*context
, nanoseconds
*clocktime
)
244 auto *device
= context
->mALDevice
.get();
245 const VoiceBufferItem
*Current
{};
251 refcount
= device
->waitForMix();
252 *clocktime
= device
->getClockTime();
253 voice
= GetSourceVoice(Source
, context
);
256 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
258 readPos
= int64_t{voice
->mPosition
.load(std::memory_order_relaxed
)} << MixerFracBits
;
259 readPos
+= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
261 std::atomic_thread_fence(std::memory_order_acquire
);
262 } while(refcount
!= device
->mMixCount
.load(std::memory_order_relaxed
));
267 const ALbuffer
*BufferFmt
{nullptr};
268 auto BufferList
= Source
->mQueue
.cbegin();
269 while(BufferList
!= Source
->mQueue
.cend() && al::to_address(BufferList
) != Current
)
271 if(!BufferFmt
) BufferFmt
= BufferList
->mBuffer
;
272 readPos
+= int64_t{BufferList
->mSampleLen
} << MixerFracBits
;
275 while(BufferList
!= Source
->mQueue
.cend() && !BufferFmt
)
277 BufferFmt
= BufferList
->mBuffer
;
280 ASSUME(BufferFmt
!= nullptr);
282 return static_cast<double>(readPos
) / double{MixerFracOne
} / BufferFmt
->mSampleRate
;
287 * Gets the current read offset for the given Source, in the appropriate format
288 * (Bytes, Samples or Seconds). The offset is relative to the start of the
289 * queue (not the start of the current buffer).
292 NOINLINE T
GetSourceOffset(ALsource
*Source
, ALenum name
, ALCcontext
*context
)
294 auto *device
= context
->mALDevice
.get();
295 const VoiceBufferItem
*Current
{};
302 refcount
= device
->waitForMix();
303 voice
= GetSourceVoice(Source
, context
);
306 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
308 readPos
= voice
->mPosition
.load(std::memory_order_relaxed
);
309 readPosFrac
= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
311 std::atomic_thread_fence(std::memory_order_acquire
);
312 } while(refcount
!= device
->mMixCount
.load(std::memory_order_relaxed
));
317 const ALbuffer
*BufferFmt
{nullptr};
318 auto BufferList
= Source
->mQueue
.cbegin();
319 while(BufferList
!= Source
->mQueue
.cend() && al::to_address(BufferList
) != Current
)
321 if(!BufferFmt
) BufferFmt
= BufferList
->mBuffer
;
322 readPos
+= BufferList
->mSampleLen
;
325 while(BufferList
!= Source
->mQueue
.cend() && !BufferFmt
)
327 BufferFmt
= BufferList
->mBuffer
;
330 ASSUME(BufferFmt
!= nullptr);
336 if constexpr(std::is_floating_point_v
<T
>)
338 offset
= static_cast<T
>(readPos
) + static_cast<T
>(readPosFrac
)/T
{MixerFracOne
};
339 offset
/= static_cast<T
>(BufferFmt
->mSampleRate
);
343 readPos
/= BufferFmt
->mSampleRate
;
344 offset
= static_cast<T
>(std::clamp
<int64_t>(readPos
, std::numeric_limits
<T
>::min(),
345 std::numeric_limits
<T
>::max()));
349 case AL_SAMPLE_OFFSET
:
350 if constexpr(std::is_floating_point_v
<T
>)
351 offset
= static_cast<T
>(readPos
) + static_cast<T
>(readPosFrac
)/T
{MixerFracOne
};
353 offset
= static_cast<T
>(std::clamp
<int64_t>(readPos
, std::numeric_limits
<T
>::min(),
354 std::numeric_limits
<T
>::max()));
358 const ALuint BlockSamples
{BufferFmt
->mBlockAlign
};
359 const ALuint BlockSize
{BufferFmt
->blockSizeFromFmt()};
360 /* Round down to the block boundary. */
361 readPos
= readPos
/ BlockSamples
* BlockSize
;
363 if constexpr(std::is_floating_point_v
<T
>)
364 offset
= static_cast<T
>(readPos
);
367 if(readPos
> std::numeric_limits
<T
>::max())
368 offset
= RoundDown(std::numeric_limits
<T
>::max(), static_cast<T
>(BlockSize
));
369 else if(readPos
< std::numeric_limits
<T
>::min())
370 offset
= RoundUp(std::numeric_limits
<T
>::min(), static_cast<T
>(BlockSize
));
372 offset
= static_cast<T
>(readPos
);
381 * Gets the length of the given Source's buffer queue, in the appropriate
382 * format (Bytes, Samples or Seconds).
385 NOINLINE T
GetSourceLength(const ALsource
*source
, ALenum name
)
388 const ALbuffer
*BufferFmt
{nullptr};
389 for(auto &listitem
: source
->mQueue
)
392 BufferFmt
= listitem
.mBuffer
;
393 length
+= listitem
.mSampleLen
;
398 ASSUME(BufferFmt
!= nullptr);
401 case AL_SEC_LENGTH_SOFT
:
402 if constexpr(std::is_floating_point_v
<T
>)
403 return static_cast<T
>(length
) / static_cast<T
>(BufferFmt
->mSampleRate
);
405 return static_cast<T
>(std::min
<uint64_t>(length
/BufferFmt
->mSampleRate
,
406 std::numeric_limits
<T
>::max()));
408 case AL_SAMPLE_LENGTH_SOFT
:
409 if constexpr(std::is_floating_point_v
<T
>)
410 return static_cast<T
>(length
);
412 return static_cast<T
>(std::min
<uint64_t>(length
, std::numeric_limits
<T
>::max()));
414 case AL_BYTE_LENGTH_SOFT
:
415 const ALuint BlockSamples
{BufferFmt
->mBlockAlign
};
416 const ALuint BlockSize
{BufferFmt
->blockSizeFromFmt()};
417 /* Round down to the block boundary. */
418 length
= length
/ BlockSamples
* BlockSize
;
420 if constexpr(std::is_floating_point_v
<T
>)
421 return static_cast<T
>(length
);
424 if(length
> uint64_t{std::numeric_limits
<T
>::max()})
425 return RoundDown(std::numeric_limits
<T
>::max(), static_cast<T
>(BlockSize
));
426 return static_cast<T
>(length
);
436 ALbufferQueueItem
*bufferitem
;
442 * Retrieves the voice position, fixed-point fraction, and bufferlist item
443 * using the given offset type and offset. If the offset is out of range,
444 * returns an empty optional.
446 std::optional
<VoicePos
> GetSampleOffset(std::deque
<ALbufferQueueItem
> &BufferList
,
447 ALenum OffsetType
, double Offset
)
449 /* Find the first valid Buffer in the Queue */
450 const ALbuffer
*BufferFmt
{nullptr};
451 for(auto &item
: BufferList
)
453 BufferFmt
= item
.mBuffer
;
456 if(!BufferFmt
) UNLIKELY
459 /* Get sample frame offset */
462 double dbloff
, dblfrac
;
466 dblfrac
= std::modf(Offset
*BufferFmt
->mSampleRate
, &dbloff
);
469 /* If there's a negative fraction, reduce the offset to "floor" it,
470 * and convert the fraction to a percentage to the next value (e.g.
471 * -2.75 -> -3 + 0.25).
476 offset
= static_cast<int64_t>(dbloff
);
477 frac
= static_cast<uint
>(std::min(dblfrac
*MixerFracOne
, MixerFracOne
-1.0));
480 case AL_SAMPLE_OFFSET
:
481 dblfrac
= std::modf(Offset
, &dbloff
);
487 offset
= static_cast<int64_t>(dbloff
);
488 frac
= static_cast<uint
>(std::min(dblfrac
*MixerFracOne
, MixerFracOne
-1.0));
492 /* Determine the ByteOffset (and ensure it is block aligned) */
493 Offset
= std::floor(Offset
/ BufferFmt
->blockSizeFromFmt());
494 offset
= static_cast<int64_t>(Offset
) * BufferFmt
->mBlockAlign
;
499 /* Find the bufferlist item this offset belongs to. */
502 if(offset
< std::numeric_limits
<int>::min())
504 return VoicePos
{static_cast<int>(offset
), frac
, &BufferList
.front()};
507 if(BufferFmt
->mCallback
)
510 int64_t totalBufferLen
{0};
511 for(auto &item
: BufferList
)
513 if(totalBufferLen
> offset
)
515 if(item
.mSampleLen
> offset
-totalBufferLen
)
517 /* Offset is in this buffer */
518 return VoicePos
{static_cast<int>(offset
-totalBufferLen
), frac
, &item
};
520 totalBufferLen
+= item
.mSampleLen
;
523 /* Offset is out of range of the queue */
528 void InitVoice(Voice
*voice
, ALsource
*source
, ALbufferQueueItem
*BufferList
, ALCcontext
*context
,
531 voice
->mLoopBuffer
.store(source
->Looping
? &source
->mQueue
.front() : nullptr,
532 std::memory_order_relaxed
);
534 ALbuffer
*buffer
{BufferList
->mBuffer
};
535 voice
->mFrequency
= buffer
->mSampleRate
;
536 if(buffer
->mChannels
== FmtMono
&& source
->mPanningEnabled
)
537 voice
->mFmtChannels
= FmtMonoDup
;
538 else if(buffer
->mChannels
== FmtStereo
&& source
->mStereoMode
== SourceStereo::Enhanced
)
539 voice
->mFmtChannels
= FmtSuperStereo
;
541 voice
->mFmtChannels
= buffer
->mChannels
;
542 voice
->mFmtType
= buffer
->mType
;
543 voice
->mFrameStep
= buffer
->channelsFromFmt();
544 voice
->mBytesPerBlock
= buffer
->blockSizeFromFmt();
545 voice
->mSamplesPerBlock
= buffer
->mBlockAlign
;
546 voice
->mAmbiLayout
= IsUHJ(voice
->mFmtChannels
) ? AmbiLayout::FuMa
: buffer
->mAmbiLayout
;
547 voice
->mAmbiScaling
= IsUHJ(voice
->mFmtChannels
) ? AmbiScaling::UHJ
: buffer
->mAmbiScaling
;
548 voice
->mAmbiOrder
= (voice
->mFmtChannels
== FmtSuperStereo
) ? 1 : buffer
->mAmbiOrder
;
550 if(buffer
->mCallback
) voice
->mFlags
.set(VoiceIsCallback
);
551 else if(source
->SourceType
== AL_STATIC
) voice
->mFlags
.set(VoiceIsStatic
);
552 voice
->mNumCallbackBlocks
= 0;
553 voice
->mCallbackBlockBase
= 0;
555 voice
->prepare(device
);
557 source
->mPropsDirty
= false;
558 UpdateSourceProps(source
, voice
, context
);
560 voice
->mSourceID
.store(source
->id
, std::memory_order_release
);
564 VoiceChange
*GetVoiceChanger(ALCcontext
*ctx
)
566 VoiceChange
*vchg
{ctx
->mVoiceChangeTail
};
567 if(vchg
== ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)) UNLIKELY
569 ctx
->allocVoiceChanges();
570 vchg
= ctx
->mVoiceChangeTail
;
573 ctx
->mVoiceChangeTail
= vchg
->mNext
.exchange(nullptr, std::memory_order_relaxed
);
578 void SendVoiceChanges(ALCcontext
*ctx
, VoiceChange
*tail
)
580 auto *device
= ctx
->mALDevice
.get();
582 VoiceChange
*oldhead
{ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)};
583 while(VoiceChange
*next
{oldhead
->mNext
.load(std::memory_order_relaxed
)})
585 oldhead
->mNext
.store(tail
, std::memory_order_release
);
587 const bool connected
{device
->Connected
.load(std::memory_order_acquire
)};
588 std::ignore
= device
->waitForMix();
589 if(!connected
) UNLIKELY
591 if(ctx
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
593 /* If the device is disconnected and voices are stopped, just
594 * ignore all pending changes.
596 VoiceChange
*cur
{ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)};
597 while(VoiceChange
*next
{cur
->mNext
.load(std::memory_order_acquire
)})
600 if(Voice
*voice
{cur
->mVoice
})
601 voice
->mSourceID
.store(0, std::memory_order_relaxed
);
603 ctx
->mCurrentVoiceChange
.store(cur
, std::memory_order_release
);
609 auto SetVoiceOffset(Voice
*oldvoice
, const VoicePos
&vpos
, ALsource
*source
, ALCcontext
*context
,
610 al::Device
*device
) -> bool
612 /* First, get a free voice to start at the new offset. */
613 auto voicelist
= context
->getVoicesSpan();
616 for(Voice
*voice
: voicelist
)
618 if(voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
619 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
620 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false)
627 if(!newvoice
) UNLIKELY
629 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
630 if(allvoices
.size() == voicelist
.size())
631 context
->allocVoices(1);
632 context
->mActiveVoiceCount
.fetch_add(1, std::memory_order_release
);
633 voicelist
= context
->getVoicesSpan();
636 for(Voice
*voice
: voicelist
)
638 if(voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
639 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
640 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false)
647 ASSUME(newvoice
!= nullptr);
650 /* Initialize the new voice and set its starting offset.
651 * TODO: It might be better to have the VoiceChange processing copy the old
652 * voice's mixing parameters (and pending update) insead of initializing it
653 * all here. This would just need to set the minimum properties to link the
654 * voice to the source and its position-dependent properties (including the
657 newvoice
->mPlayState
.store(Voice::Pending
, std::memory_order_relaxed
);
658 newvoice
->mPosition
.store(vpos
.pos
, std::memory_order_relaxed
);
659 newvoice
->mPositionFrac
.store(vpos
.frac
, std::memory_order_relaxed
);
660 newvoice
->mCurrentBuffer
.store(vpos
.bufferitem
, std::memory_order_relaxed
);
661 newvoice
->mStartTime
= oldvoice
->mStartTime
;
662 newvoice
->mFlags
.reset();
663 if(vpos
.pos
> 0 || (vpos
.pos
== 0 && vpos
.frac
> 0)
664 || vpos
.bufferitem
!= &source
->mQueue
.front())
665 newvoice
->mFlags
.set(VoiceIsFading
);
666 InitVoice(newvoice
, source
, vpos
.bufferitem
, context
, device
);
667 source
->VoiceIdx
= vidx
;
669 /* Set the old voice as having a pending change, and send it off with the
670 * new one with a new offset voice change.
672 oldvoice
->mPendingChange
.store(true, std::memory_order_relaxed
);
674 VoiceChange
*vchg
{GetVoiceChanger(context
)};
675 vchg
->mOldVoice
= oldvoice
;
676 vchg
->mVoice
= newvoice
;
677 vchg
->mSourceID
= source
->id
;
678 vchg
->mState
= VChangeState::Restart
;
679 SendVoiceChanges(context
, vchg
);
681 /* If the old voice still has a sourceID, it's still active and the change-
682 * over will work on the next update.
684 if(oldvoice
->mSourceID
.load(std::memory_order_acquire
) != 0u) LIKELY
687 /* Otherwise, if the new voice's state is not pending, the change-over
690 if(newvoice
->mPlayState
.load(std::memory_order_acquire
) != Voice::Pending
)
693 /* Otherwise, wait for any current mix to finish and check one last time. */
694 std::ignore
= device
->waitForMix();
695 if(newvoice
->mPlayState
.load(std::memory_order_acquire
) != Voice::Pending
)
697 /* The change-over failed because the old voice stopped before the new
698 * voice could start at the new offset. Let go of the new voice and have
699 * the caller store the source offset since it's stopped.
701 newvoice
->mCurrentBuffer
.store(nullptr, std::memory_order_relaxed
);
702 newvoice
->mLoopBuffer
.store(nullptr, std::memory_order_relaxed
);
703 newvoice
->mSourceID
.store(0u, std::memory_order_relaxed
);
704 newvoice
->mPlayState
.store(Voice::Stopped
, std::memory_order_relaxed
);
710 * Returns if the last known state for the source was playing or paused. Does
711 * not sync with the mixer voice.
713 inline bool IsPlayingOrPaused(ALsource
*source
)
714 { return source
->state
== AL_PLAYING
|| source
->state
== AL_PAUSED
; }
717 * Returns an updated source state using the matching voice's status (or lack
720 inline ALenum
GetSourceState(ALsource
*source
, Voice
*voice
)
722 if(!voice
&& source
->state
== AL_PLAYING
)
723 source
->state
= AL_STOPPED
;
724 return source
->state
;
728 bool EnsureSources(ALCcontext
*context
, size_t needed
)
730 size_t count
{std::accumulate(context
->mSourceList
.cbegin(), context
->mSourceList
.cend(), 0_uz
,
731 [](size_t cur
, const SourceSubList
&sublist
) noexcept
-> size_t
732 { return cur
+ static_cast<ALuint
>(al::popcount(sublist
.FreeMask
)); })};
735 while(needed
> count
)
737 if(context
->mSourceList
.size() >= 1<<25) UNLIKELY
740 SourceSubList sublist
{};
741 sublist
.FreeMask
= ~0_u64
;
742 sublist
.Sources
= SubListAllocator
{}.allocate(1);
743 context
->mSourceList
.emplace_back(std::move(sublist
));
744 count
+= std::tuple_size_v
<SubListAllocator::value_type
>;
753 ALsource
*AllocSource(ALCcontext
*context
) noexcept
755 auto sublist
= std::find_if(context
->mSourceList
.begin(), context
->mSourceList
.end(),
756 [](const SourceSubList
&entry
) noexcept
-> bool
757 { return entry
.FreeMask
!= 0; });
758 auto lidx
= static_cast<ALuint
>(std::distance(context
->mSourceList
.begin(), sublist
));
759 auto slidx
= static_cast<ALuint
>(al::countr_zero(sublist
->FreeMask
));
762 ALsource
*source
{al::construct_at(al::to_address(sublist
->Sources
->begin() + slidx
))};
764 source
->eaxInitialize(context
);
767 /* Add 1 to avoid source ID 0. */
768 source
->id
= ((lidx
<<6) | slidx
) + 1;
770 context
->mNumSources
+= 1;
771 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
776 void FreeSource(ALCcontext
*context
, ALsource
*source
)
778 context
->mSourceNames
.erase(source
->id
);
780 const ALuint id
{source
->id
- 1};
781 const size_t lidx
{id
>> 6};
782 const ALuint slidx
{id
& 0x3f};
784 if(Voice
*voice
{GetSourceVoice(source
, context
)})
786 VoiceChange
*vchg
{GetVoiceChanger(context
)};
788 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
789 vchg
->mVoice
= voice
;
790 vchg
->mSourceID
= source
->id
;
791 vchg
->mState
= VChangeState::Stop
;
793 SendVoiceChanges(context
, vchg
);
796 std::destroy_at(source
);
798 context
->mSourceList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
799 context
->mNumSources
--;
803 inline ALsource
*LookupSource(ALCcontext
*context
, ALuint id
) noexcept
805 const size_t lidx
{(id
-1) >> 6};
806 const ALuint slidx
{(id
-1) & 0x3f};
808 if(lidx
>= context
->mSourceList
.size()) UNLIKELY
810 SourceSubList
&sublist
{context
->mSourceList
[lidx
]};
811 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
813 return al::to_address(sublist
.Sources
->begin() + slidx
);
816 auto LookupBuffer
= [](al::Device
*device
, auto id
) noexcept
-> ALbuffer
*
818 const auto lidx
{(id
-1) >> 6};
819 const auto slidx
{(id
-1) & 0x3f};
821 if(lidx
>= device
->BufferList
.size()) UNLIKELY
823 BufferSubList
&sublist
= device
->BufferList
[static_cast<size_t>(lidx
)];
824 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
826 return al::to_address(sublist
.Buffers
->begin() + static_cast<size_t>(slidx
));
829 auto LookupFilter
= [](al::Device
*device
, auto id
) noexcept
-> ALfilter
*
831 const auto lidx
{(id
-1) >> 6};
832 const auto slidx
{(id
-1) & 0x3f};
834 if(lidx
>= device
->FilterList
.size()) UNLIKELY
836 FilterSubList
&sublist
= device
->FilterList
[static_cast<size_t>(lidx
)];
837 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
839 return al::to_address(sublist
.Filters
->begin() + static_cast<size_t>(slidx
));
842 auto LookupEffectSlot
= [](ALCcontext
*context
, auto id
) noexcept
-> ALeffectslot
*
844 const auto lidx
{(id
-1) >> 6};
845 const auto slidx
{(id
-1) & 0x3f};
847 if(lidx
>= context
->mEffectSlotList
.size()) UNLIKELY
849 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[static_cast<size_t>(lidx
)]};
850 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
852 return al::to_address(sublist
.EffectSlots
->begin() + static_cast<size_t>(slidx
));
856 auto StereoModeFromEnum
= [](auto mode
) noexcept
-> std::optional
<SourceStereo
>
860 case AL_NORMAL_SOFT
: return SourceStereo::Normal
;
861 case AL_SUPER_STEREO_SOFT
: return SourceStereo::Enhanced
;
865 ALenum
EnumFromStereoMode(SourceStereo mode
)
869 case SourceStereo::Normal
: return AL_NORMAL_SOFT
;
870 case SourceStereo::Enhanced
: return AL_SUPER_STEREO_SOFT
;
872 throw std::runtime_error
{"Invalid SourceStereo: "+std::to_string(int(mode
))};
875 auto SpatializeModeFromEnum
= [](auto mode
) noexcept
-> std::optional
<SpatializeMode
>
879 case AL_FALSE
: return SpatializeMode::Off
;
880 case AL_TRUE
: return SpatializeMode::On
;
881 case AL_AUTO_SOFT
: return SpatializeMode::Auto
;
885 ALenum
EnumFromSpatializeMode(SpatializeMode mode
)
889 case SpatializeMode::Off
: return AL_FALSE
;
890 case SpatializeMode::On
: return AL_TRUE
;
891 case SpatializeMode::Auto
: return AL_AUTO_SOFT
;
893 throw std::runtime_error
{"Invalid SpatializeMode: "+std::to_string(int(mode
))};
896 auto DirectModeFromEnum
= [](auto mode
) noexcept
-> std::optional
<DirectMode
>
900 case AL_FALSE
: return DirectMode::Off
;
901 case AL_DROP_UNMATCHED_SOFT
: return DirectMode::DropMismatch
;
902 case AL_REMIX_UNMATCHED_SOFT
: return DirectMode::RemixMismatch
;
906 ALenum
EnumFromDirectMode(DirectMode mode
)
910 case DirectMode::Off
: return AL_FALSE
;
911 case DirectMode::DropMismatch
: return AL_DROP_UNMATCHED_SOFT
;
912 case DirectMode::RemixMismatch
: return AL_REMIX_UNMATCHED_SOFT
;
914 throw std::runtime_error
{"Invalid DirectMode: "+std::to_string(int(mode
))};
917 auto DistanceModelFromALenum
= [](auto model
) noexcept
-> std::optional
<DistanceModel
>
921 case AL_NONE
: return DistanceModel::Disable
;
922 case AL_INVERSE_DISTANCE
: return DistanceModel::Inverse
;
923 case AL_INVERSE_DISTANCE_CLAMPED
: return DistanceModel::InverseClamped
;
924 case AL_LINEAR_DISTANCE
: return DistanceModel::Linear
;
925 case AL_LINEAR_DISTANCE_CLAMPED
: return DistanceModel::LinearClamped
;
926 case AL_EXPONENT_DISTANCE
: return DistanceModel::Exponent
;
927 case AL_EXPONENT_DISTANCE_CLAMPED
: return DistanceModel::ExponentClamped
;
931 ALenum
ALenumFromDistanceModel(DistanceModel model
)
935 case DistanceModel::Disable
: return AL_NONE
;
936 case DistanceModel::Inverse
: return AL_INVERSE_DISTANCE
;
937 case DistanceModel::InverseClamped
: return AL_INVERSE_DISTANCE_CLAMPED
;
938 case DistanceModel::Linear
: return AL_LINEAR_DISTANCE
;
939 case DistanceModel::LinearClamped
: return AL_LINEAR_DISTANCE_CLAMPED
;
940 case DistanceModel::Exponent
: return AL_EXPONENT_DISTANCE
;
941 case DistanceModel::ExponentClamped
: return AL_EXPONENT_DISTANCE_CLAMPED
;
943 throw std::runtime_error
{"Unexpected distance model "+std::to_string(static_cast<int>(model
))};
946 enum SourceProp
: ALenum
{
949 srcMinGain
= AL_MIN_GAIN
,
950 srcMaxGain
= AL_MAX_GAIN
,
951 srcMaxDistance
= AL_MAX_DISTANCE
,
952 srcRolloffFactor
= AL_ROLLOFF_FACTOR
,
953 srcDopplerFactor
= AL_DOPPLER_FACTOR
,
954 srcConeOuterGain
= AL_CONE_OUTER_GAIN
,
955 srcSecOffset
= AL_SEC_OFFSET
,
956 srcSampleOffset
= AL_SAMPLE_OFFSET
,
957 srcByteOffset
= AL_BYTE_OFFSET
,
958 srcConeInnerAngle
= AL_CONE_INNER_ANGLE
,
959 srcConeOuterAngle
= AL_CONE_OUTER_ANGLE
,
960 srcRefDistance
= AL_REFERENCE_DISTANCE
,
962 srcPosition
= AL_POSITION
,
963 srcVelocity
= AL_VELOCITY
,
964 srcDirection
= AL_DIRECTION
,
966 srcSourceRelative
= AL_SOURCE_RELATIVE
,
967 srcLooping
= AL_LOOPING
,
968 srcBuffer
= AL_BUFFER
,
969 srcSourceState
= AL_SOURCE_STATE
,
970 srcBuffersQueued
= AL_BUFFERS_QUEUED
,
971 srcBuffersProcessed
= AL_BUFFERS_PROCESSED
,
972 srcSourceType
= AL_SOURCE_TYPE
,
975 srcConeOuterGainHF
= AL_CONE_OUTER_GAINHF
,
976 srcAirAbsorptionFactor
= AL_AIR_ABSORPTION_FACTOR
,
977 srcRoomRolloffFactor
= AL_ROOM_ROLLOFF_FACTOR
,
978 srcDirectFilterGainHFAuto
= AL_DIRECT_FILTER_GAINHF_AUTO
,
979 srcAuxSendFilterGainAuto
= AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
,
980 srcAuxSendFilterGainHFAuto
= AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
,
981 srcDirectFilter
= AL_DIRECT_FILTER
,
982 srcAuxSendFilter
= AL_AUXILIARY_SEND_FILTER
,
984 /* AL_SOFT_direct_channels */
985 srcDirectChannelsSOFT
= AL_DIRECT_CHANNELS_SOFT
,
987 /* AL_EXT_source_distance_model */
988 srcDistanceModel
= AL_DISTANCE_MODEL
,
990 /* AL_SOFT_source_latency */
991 srcSampleOffsetLatencySOFT
= AL_SAMPLE_OFFSET_LATENCY_SOFT
,
992 srcSecOffsetLatencySOFT
= AL_SEC_OFFSET_LATENCY_SOFT
,
994 /* AL_EXT_STEREO_ANGLES */
995 srcAngles
= AL_STEREO_ANGLES
,
997 /* AL_EXT_SOURCE_RADIUS */
998 srcRadius
= AL_SOURCE_RADIUS
,
1000 /* AL_EXT_BFORMAT */
1001 srcOrientation
= AL_ORIENTATION
,
1003 /* AL_SOFT_source_length */
1004 srcByteLength
= AL_BYTE_LENGTH_SOFT
,
1005 srcSampleLength
= AL_SAMPLE_LENGTH_SOFT
,
1006 srcSecLength
= AL_SEC_LENGTH_SOFT
,
1008 /* AL_SOFT_source_resampler */
1009 srcResampler
= AL_SOURCE_RESAMPLER_SOFT
,
1011 /* AL_SOFT_source_spatialize */
1012 srcSpatialize
= AL_SOURCE_SPATIALIZE_SOFT
,
1014 /* ALC_SOFT_device_clock */
1015 srcSampleOffsetClockSOFT
= AL_SAMPLE_OFFSET_CLOCK_SOFT
,
1016 srcSecOffsetClockSOFT
= AL_SEC_OFFSET_CLOCK_SOFT
,
1019 srcStereoMode
= AL_STEREO_MODE_SOFT
,
1020 srcSuperStereoWidth
= AL_SUPER_STEREO_WIDTH_SOFT
,
1022 /* AL_SOFT_buffer_sub_data */
1023 srcByteRWOffsetsSOFT
= AL_BYTE_RW_OFFSETS_SOFT
,
1024 srcSampleRWOffsetsSOFT
= AL_SAMPLE_RW_OFFSETS_SOFT
,
1026 /* AL_SOFT_source_panning */
1027 srcPanningEnabledSOFT
= AL_PANNING_ENABLED_SOFT
,
1028 srcPanSOFT
= AL_PAN_SOFT
,
1032 constexpr ALuint
IntValsByProp(ALenum prop
)
1034 switch(static_cast<SourceProp
>(prop
))
1036 case AL_SOURCE_STATE
:
1037 case AL_SOURCE_TYPE
:
1038 case AL_BUFFERS_QUEUED
:
1039 case AL_BUFFERS_PROCESSED
:
1040 case AL_BYTE_LENGTH_SOFT
:
1041 case AL_SAMPLE_LENGTH_SOFT
:
1042 case AL_SOURCE_RELATIVE
:
1045 case AL_SAMPLE_OFFSET
:
1046 case AL_BYTE_OFFSET
:
1047 case AL_DIRECT_FILTER
:
1048 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1049 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1050 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1051 case AL_DIRECT_CHANNELS_SOFT
:
1052 case AL_DISTANCE_MODEL
:
1053 case AL_SOURCE_RESAMPLER_SOFT
:
1054 case AL_SOURCE_SPATIALIZE_SOFT
:
1055 case AL_STEREO_MODE_SOFT
:
1056 case AL_PANNING_ENABLED_SOFT
:
1060 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1061 if(sBufferSubDataCompat
)
1064 case AL_CONE_INNER_ANGLE
:
1065 case AL_CONE_OUTER_ANGLE
:
1070 case AL_REFERENCE_DISTANCE
:
1071 case AL_ROLLOFF_FACTOR
:
1072 case AL_CONE_OUTER_GAIN
:
1073 case AL_MAX_DISTANCE
:
1075 case AL_DOPPLER_FACTOR
:
1076 case AL_CONE_OUTER_GAINHF
:
1077 case AL_AIR_ABSORPTION_FACTOR
:
1078 case AL_ROOM_ROLLOFF_FACTOR
:
1079 case AL_SEC_LENGTH_SOFT
:
1080 case AL_SUPER_STEREO_WIDTH_SOFT
:
1081 return 1; /* 1x float */
1083 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1084 if(sBufferSubDataCompat
)
1088 case AL_AUXILIARY_SEND_FILTER
:
1094 return 3; /* 3x float */
1096 case AL_ORIENTATION
:
1097 return 6; /* 6x float */
1099 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1100 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1101 case AL_STEREO_ANGLES
:
1102 break; /* i64 only */
1103 case AL_SEC_OFFSET_LATENCY_SOFT
:
1104 case AL_SEC_OFFSET_CLOCK_SOFT
:
1105 break; /* double only */
1111 constexpr ALuint
Int64ValsByProp(ALenum prop
)
1113 switch(static_cast<SourceProp
>(prop
))
1115 case AL_SOURCE_STATE
:
1116 case AL_SOURCE_TYPE
:
1117 case AL_BUFFERS_QUEUED
:
1118 case AL_BUFFERS_PROCESSED
:
1119 case AL_BYTE_LENGTH_SOFT
:
1120 case AL_SAMPLE_LENGTH_SOFT
:
1121 case AL_SOURCE_RELATIVE
:
1124 case AL_SAMPLE_OFFSET
:
1125 case AL_BYTE_OFFSET
:
1126 case AL_DIRECT_FILTER
:
1127 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1128 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1129 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1130 case AL_DIRECT_CHANNELS_SOFT
:
1131 case AL_DISTANCE_MODEL
:
1132 case AL_SOURCE_RESAMPLER_SOFT
:
1133 case AL_SOURCE_SPATIALIZE_SOFT
:
1134 case AL_STEREO_MODE_SOFT
:
1135 case AL_PANNING_ENABLED_SOFT
:
1139 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1140 if(sBufferSubDataCompat
)
1143 case AL_CONE_INNER_ANGLE
:
1144 case AL_CONE_OUTER_ANGLE
:
1149 case AL_REFERENCE_DISTANCE
:
1150 case AL_ROLLOFF_FACTOR
:
1151 case AL_CONE_OUTER_GAIN
:
1152 case AL_MAX_DISTANCE
:
1154 case AL_DOPPLER_FACTOR
:
1155 case AL_CONE_OUTER_GAINHF
:
1156 case AL_AIR_ABSORPTION_FACTOR
:
1157 case AL_ROOM_ROLLOFF_FACTOR
:
1158 case AL_SEC_LENGTH_SOFT
:
1159 case AL_SUPER_STEREO_WIDTH_SOFT
:
1160 return 1; /* 1x float */
1162 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1163 if(sBufferSubDataCompat
)
1167 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1168 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1169 case AL_STEREO_ANGLES
:
1172 case AL_AUXILIARY_SEND_FILTER
:
1178 return 3; /* 3x float */
1180 case AL_ORIENTATION
:
1181 return 6; /* 6x float */
1183 case AL_SEC_OFFSET_LATENCY_SOFT
:
1184 case AL_SEC_OFFSET_CLOCK_SOFT
:
1185 break; /* double only */
1191 constexpr ALuint
FloatValsByProp(ALenum prop
)
1193 switch(static_cast<SourceProp
>(prop
))
1199 case AL_MAX_DISTANCE
:
1200 case AL_ROLLOFF_FACTOR
:
1201 case AL_DOPPLER_FACTOR
:
1202 case AL_CONE_OUTER_GAIN
:
1204 case AL_SAMPLE_OFFSET
:
1205 case AL_BYTE_OFFSET
:
1206 case AL_CONE_INNER_ANGLE
:
1207 case AL_CONE_OUTER_ANGLE
:
1208 case AL_REFERENCE_DISTANCE
:
1209 case AL_CONE_OUTER_GAINHF
:
1210 case AL_AIR_ABSORPTION_FACTOR
:
1211 case AL_ROOM_ROLLOFF_FACTOR
:
1212 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1213 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1214 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1215 case AL_DIRECT_CHANNELS_SOFT
:
1216 case AL_DISTANCE_MODEL
:
1217 case AL_SOURCE_RELATIVE
:
1219 case AL_SOURCE_STATE
:
1220 case AL_BUFFERS_QUEUED
:
1221 case AL_BUFFERS_PROCESSED
:
1222 case AL_SOURCE_TYPE
:
1223 case AL_SOURCE_RESAMPLER_SOFT
:
1224 case AL_SOURCE_SPATIALIZE_SOFT
:
1225 case AL_BYTE_LENGTH_SOFT
:
1226 case AL_SAMPLE_LENGTH_SOFT
:
1227 case AL_SEC_LENGTH_SOFT
:
1228 case AL_STEREO_MODE_SOFT
:
1229 case AL_SUPER_STEREO_WIDTH_SOFT
:
1230 case AL_PANNING_ENABLED_SOFT
:
1234 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1235 if(!sBufferSubDataCompat
)
1238 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1241 case AL_STEREO_ANGLES
:
1249 case AL_ORIENTATION
:
1252 case AL_SEC_OFFSET_LATENCY_SOFT
:
1253 case AL_SEC_OFFSET_CLOCK_SOFT
:
1254 break; /* Double only */
1257 case AL_DIRECT_FILTER
:
1258 case AL_AUXILIARY_SEND_FILTER
:
1259 break; /* i/i64 only */
1260 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1261 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1262 break; /* i64 only */
1266 constexpr ALuint
DoubleValsByProp(ALenum prop
)
1268 switch(static_cast<SourceProp
>(prop
))
1274 case AL_MAX_DISTANCE
:
1275 case AL_ROLLOFF_FACTOR
:
1276 case AL_DOPPLER_FACTOR
:
1277 case AL_CONE_OUTER_GAIN
:
1279 case AL_SAMPLE_OFFSET
:
1280 case AL_BYTE_OFFSET
:
1281 case AL_CONE_INNER_ANGLE
:
1282 case AL_CONE_OUTER_ANGLE
:
1283 case AL_REFERENCE_DISTANCE
:
1284 case AL_CONE_OUTER_GAINHF
:
1285 case AL_AIR_ABSORPTION_FACTOR
:
1286 case AL_ROOM_ROLLOFF_FACTOR
:
1287 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1288 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1289 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1290 case AL_DIRECT_CHANNELS_SOFT
:
1291 case AL_DISTANCE_MODEL
:
1292 case AL_SOURCE_RELATIVE
:
1294 case AL_SOURCE_STATE
:
1295 case AL_BUFFERS_QUEUED
:
1296 case AL_BUFFERS_PROCESSED
:
1297 case AL_SOURCE_TYPE
:
1298 case AL_SOURCE_RESAMPLER_SOFT
:
1299 case AL_SOURCE_SPATIALIZE_SOFT
:
1300 case AL_BYTE_LENGTH_SOFT
:
1301 case AL_SAMPLE_LENGTH_SOFT
:
1302 case AL_SEC_LENGTH_SOFT
:
1303 case AL_STEREO_MODE_SOFT
:
1304 case AL_SUPER_STEREO_WIDTH_SOFT
:
1305 case AL_PANNING_ENABLED_SOFT
:
1309 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1310 if(!sBufferSubDataCompat
)
1313 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1316 case AL_SEC_OFFSET_LATENCY_SOFT
:
1317 case AL_SEC_OFFSET_CLOCK_SOFT
:
1318 case AL_STEREO_ANGLES
:
1326 case AL_ORIENTATION
:
1330 case AL_DIRECT_FILTER
:
1331 case AL_AUXILIARY_SEND_FILTER
:
1332 break; /* i/i64 only */
1333 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1334 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1335 break; /* i64 only */
1341 void UpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1343 if(!context
->mDeferUpdates
)
1345 if(Voice
*voice
{GetSourceVoice(source
, context
)})
1347 UpdateSourceProps(source
, voice
, context
);
1351 source
->mPropsDirty
= true;
1354 void CommitAndUpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1356 if(!context
->mDeferUpdates
)
1358 if(context
->hasEax())
1359 source
->eaxCommit();
1360 if(Voice
*voice
{GetSourceVoice(source
, context
)})
1362 UpdateSourceProps(source
, voice
, context
);
1366 source
->mPropsDirty
= true;
1371 inline void CommitAndUpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1372 { UpdateSourceProps(source
, context
); }
1376 template<typename T
>
1377 struct PropType
{ };
1379 struct PropType
<ALint
> { static const char *Name() { return "integer"; } };
1381 struct PropType
<ALint64SOFT
> { static const char *Name() { return "int64"; } };
1383 struct PropType
<ALfloat
> { static const char *Name() { return "float"; } };
1385 struct PropType
<ALdouble
> { static const char *Name() { return "double"; } };
1388 std::array
<char,32> mStr
{};
1390 template<typename T
>
1393 using ST
= std::make_signed_t
<std::remove_cv_t
<T
>>;
1394 if constexpr(std::is_same_v
<ST
,int>)
1395 std::snprintf(mStr
.data(), mStr
.size(), "0x%x", value
);
1396 else if constexpr(std::is_same_v
<ST
,long>)
1397 std::snprintf(mStr
.data(), mStr
.size(), "0x%lx", value
);
1398 else if constexpr(std::is_same_v
<ST
,long long>)
1399 std::snprintf(mStr
.data(), mStr
.size(), "0x%llx", value
);
1402 [[nodiscard
]] auto c_str() const noexcept
-> const char* { return mStr
.data(); }
1407 * Returns a pair of lambdas to check the following setter.
1409 * The first lambda checks the size of the span is valid for the required size,
1410 * throwing a context error if it fails.
1412 * The second lambda tests the validity of the value check, throwing a context
1413 * error if it failed.
1415 template<typename T
, size_t N
>
1416 auto GetCheckers(const SourceProp prop
, const al::span
<T
,N
> values
)
1418 return std::make_pair(
1419 [=](size_t expect
) -> void
1421 if(values
.size() == expect
) return;
1422 throw al::context_error
{AL_INVALID_ENUM
,
1423 "Property 0x%04x expects %zu value(s), got %zu", prop
, expect
, values
.size()};
1425 [](bool passed
) -> void
1428 throw al::context_error
{AL_INVALID_VALUE
, "Value out of range"};
1433 template<typename T
>
1434 NOINLINE
void SetProperty(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
1435 const al::span
<const T
> values
)
1437 auto [CheckSize
, CheckValue
] = GetCheckers(prop
, values
);
1438 auto *device
= Context
->mALDevice
.get();
1442 case AL_SOURCE_STATE
:
1443 case AL_SOURCE_TYPE
:
1444 case AL_BUFFERS_QUEUED
:
1445 case AL_BUFFERS_PROCESSED
:
1446 if constexpr(std::is_integral_v
<T
>)
1449 throw al::context_error
{AL_INVALID_OPERATION
,
1450 "Setting read-only source property 0x%04x", prop
};
1454 case AL_BYTE_LENGTH_SOFT
:
1455 case AL_SAMPLE_LENGTH_SOFT
:
1456 case AL_SEC_LENGTH_SOFT
:
1457 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1458 case AL_SEC_OFFSET_LATENCY_SOFT
:
1459 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1460 case AL_SEC_OFFSET_CLOCK_SOFT
:
1462 throw al::context_error
{AL_INVALID_OPERATION
, "Setting read-only source property 0x%04x",
1467 if constexpr(std::is_floating_point_v
<T
>)
1468 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1470 CheckValue(values
[0] >= T
{0});
1472 Source
->Pitch
= static_cast<float>(values
[0]);
1473 return UpdateSourceProps(Source
, Context
);
1475 case AL_CONE_INNER_ANGLE
:
1477 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{360});
1479 Source
->InnerAngle
= static_cast<float>(values
[0]);
1480 return CommitAndUpdateSourceProps(Source
, Context
);
1482 case AL_CONE_OUTER_ANGLE
:
1484 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{360});
1486 Source
->OuterAngle
= static_cast<float>(values
[0]);
1487 return CommitAndUpdateSourceProps(Source
, Context
);
1491 if constexpr(std::is_floating_point_v
<T
>)
1492 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1494 CheckValue(values
[0] >= T
{0});
1496 Source
->Gain
= static_cast<float>(values
[0]);
1497 return UpdateSourceProps(Source
, Context
);
1499 case AL_MAX_DISTANCE
:
1501 if constexpr(std::is_floating_point_v
<T
>)
1502 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1504 CheckValue(values
[0] >= T
{0});
1506 Source
->MaxDistance
= static_cast<float>(values
[0]);
1507 return CommitAndUpdateSourceProps(Source
, Context
);
1509 case AL_ROLLOFF_FACTOR
:
1511 if constexpr(std::is_floating_point_v
<T
>)
1512 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1514 CheckValue(values
[0] >= T
{0});
1516 Source
->RolloffFactor
= static_cast<float>(values
[0]);
1517 return CommitAndUpdateSourceProps(Source
, Context
);
1519 case AL_REFERENCE_DISTANCE
:
1521 if constexpr(std::is_floating_point_v
<T
>)
1522 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1524 CheckValue(values
[0] >= T
{0});
1526 Source
->RefDistance
= static_cast<float>(values
[0]);
1527 return CommitAndUpdateSourceProps(Source
, Context
);
1531 if constexpr(std::is_floating_point_v
<T
>)
1532 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1534 CheckValue(values
[0] >= T
{0});
1536 Source
->MinGain
= static_cast<float>(values
[0]);
1537 return UpdateSourceProps(Source
, Context
);
1541 if constexpr(std::is_floating_point_v
<T
>)
1542 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1544 CheckValue(values
[0] >= T
{0});
1546 Source
->MaxGain
= static_cast<float>(values
[0]);
1547 return UpdateSourceProps(Source
, Context
);
1549 case AL_CONE_OUTER_GAIN
:
1551 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1553 Source
->OuterGain
= static_cast<float>(values
[0]);
1554 return UpdateSourceProps(Source
, Context
);
1556 case AL_CONE_OUTER_GAINHF
:
1558 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1560 Source
->OuterGainHF
= static_cast<float>(values
[0]);
1561 return UpdateSourceProps(Source
, Context
);
1563 case AL_AIR_ABSORPTION_FACTOR
:
1565 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{10});
1567 Source
->AirAbsorptionFactor
= static_cast<float>(values
[0]);
1568 return UpdateSourceProps(Source
, Context
);
1570 case AL_ROOM_ROLLOFF_FACTOR
:
1572 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1574 Source
->RoomRolloffFactor
= static_cast<float>(values
[0]);
1575 return UpdateSourceProps(Source
, Context
);
1577 case AL_DOPPLER_FACTOR
:
1579 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1581 Source
->DopplerFactor
= static_cast<float>(values
[0]);
1582 return UpdateSourceProps(Source
, Context
);
1585 case AL_SOURCE_RELATIVE
:
1586 if constexpr(std::is_integral_v
<T
>)
1589 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1591 Source
->HeadRelative
= values
[0] != AL_FALSE
;
1592 return CommitAndUpdateSourceProps(Source
, Context
);
1597 if constexpr(std::is_integral_v
<T
>)
1600 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1602 Source
->Looping
= values
[0] != AL_FALSE
;
1603 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1606 voice
->mLoopBuffer
.store(&Source
->mQueue
.front(), std::memory_order_release
);
1608 voice
->mLoopBuffer
.store(nullptr, std::memory_order_release
);
1610 /* If the source is playing, wait for the current mix to finish
1611 * to ensure it isn't currently looping back or reaching the
1614 std::ignore
= device
->waitForMix();
1621 if constexpr(std::is_integral_v
<T
>)
1624 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1625 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1626 throw al::context_error
{AL_INVALID_OPERATION
,
1627 "Setting buffer on playing or paused source %u", Source
->id
};
1629 std::deque
<ALbufferQueueItem
> oldlist
;
1632 using UT
= std::make_unsigned_t
<T
>;
1633 std::lock_guard
<std::mutex
> buflock
{device
->BufferLock
};
1634 ALbuffer
*buffer
{LookupBuffer(device
, static_cast<UT
>(values
[0]))};
1636 throw al::context_error
{AL_INVALID_VALUE
, "Invalid buffer ID %s",
1637 std::to_string(values
[0]).c_str()};
1638 if(buffer
->MappedAccess
&& !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
1639 throw al::context_error
{AL_INVALID_OPERATION
,
1640 "Setting non-persistently mapped buffer %u", buffer
->id
};
1641 if(buffer
->mCallback
&& buffer
->ref
.load(std::memory_order_relaxed
) != 0)
1642 throw al::context_error
{AL_INVALID_OPERATION
,
1643 "Setting already-set callback buffer %u", buffer
->id
};
1645 /* Add the selected buffer to a one-item queue */
1646 std::deque
<ALbufferQueueItem
> newlist
;
1647 newlist
.emplace_back();
1648 newlist
.back().mCallback
= buffer
->mCallback
;
1649 newlist
.back().mUserData
= buffer
->mUserData
;
1650 newlist
.back().mBlockAlign
= buffer
->mBlockAlign
;
1651 newlist
.back().mSampleLen
= buffer
->mSampleLen
;
1652 newlist
.back().mLoopStart
= buffer
->mLoopStart
;
1653 newlist
.back().mLoopEnd
= buffer
->mLoopEnd
;
1654 newlist
.back().mSamples
= buffer
->mData
;
1655 newlist
.back().mBuffer
= buffer
;
1656 IncrementRef(buffer
->ref
);
1658 /* Source is now Static */
1659 Source
->SourceType
= AL_STATIC
;
1660 Source
->mQueue
.swap(oldlist
);
1661 Source
->mQueue
.swap(newlist
);
1665 /* Source is now Undetermined */
1666 Source
->SourceType
= AL_UNDETERMINED
;
1667 Source
->mQueue
.swap(oldlist
);
1670 /* Delete all elements in the previous queue */
1671 for(auto &item
: oldlist
)
1673 if(ALbuffer
*buffer
{item
.mBuffer
})
1674 DecrementRef(buffer
->ref
);
1682 case AL_SAMPLE_OFFSET
:
1683 case AL_BYTE_OFFSET
:
1685 if constexpr(std::is_floating_point_v
<T
>)
1686 CheckValue(std::isfinite(values
[0]));
1688 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1690 auto vpos
= GetSampleOffset(Source
->mQueue
, prop
, static_cast<double>(values
[0]));
1691 if(!vpos
) throw al::context_error
{AL_INVALID_VALUE
, "Invalid offset"};
1693 if(SetVoiceOffset(voice
, *vpos
, Source
, Context
, Context
->mALDevice
.get()))
1696 Source
->OffsetType
= prop
;
1697 Source
->Offset
= static_cast<double>(values
[0]);
1700 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1701 if(sBufferSubDataCompat
)
1703 if constexpr(std::is_integral_v
<T
>)
1706 throw al::context_error
{AL_INVALID_OPERATION
,
1707 "Setting read-only source property 0x%04x", prop
};
1712 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1713 if(sBufferSubDataCompat
)
1715 if constexpr(std::is_integral_v
<T
>)
1718 throw al::context_error
{AL_INVALID_OPERATION
,
1719 "Setting read-only source property 0x%04x", prop
};
1724 if constexpr(std::is_floating_point_v
<T
>)
1725 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1727 CheckValue(values
[0] >= T
{0});
1729 Source
->Radius
= static_cast<float>(values
[0]);
1730 return UpdateSourceProps(Source
, Context
);
1732 case AL_SUPER_STEREO_WIDTH_SOFT
:
1734 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1736 Source
->EnhWidth
= static_cast<float>(values
[0]);
1737 return UpdateSourceProps(Source
, Context
);
1739 case AL_PANNING_ENABLED_SOFT
:
1741 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1742 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1743 throw al::context_error
{AL_INVALID_OPERATION
,
1744 "Modifying panning enabled on playing or paused source %u", Source
->id
};
1746 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1748 Source
->mPanningEnabled
= values
[0] != AL_FALSE
;
1749 return UpdateSourceProps(Source
, Context
);
1753 CheckValue(values
[0] >= T
{-1} && values
[0] <= T
{1});
1755 Source
->mPan
= static_cast<float>(values
[0]);
1756 return UpdateSourceProps(Source
, Context
);
1758 case AL_STEREO_ANGLES
:
1760 if constexpr(std::is_floating_point_v
<T
>)
1761 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1762 && std::isfinite(static_cast<float>(values
[1])));
1764 Source
->StereoPan
[0] = static_cast<float>(values
[0]);
1765 Source
->StereoPan
[1] = static_cast<float>(values
[1]);
1766 return UpdateSourceProps(Source
, Context
);
1771 if constexpr(std::is_floating_point_v
<T
>)
1772 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1773 && std::isfinite(static_cast<float>(values
[1]))
1774 && std::isfinite(static_cast<float>(values
[2])));
1776 Source
->Position
[0] = static_cast<float>(values
[0]);
1777 Source
->Position
[1] = static_cast<float>(values
[1]);
1778 Source
->Position
[2] = static_cast<float>(values
[2]);
1779 return CommitAndUpdateSourceProps(Source
, Context
);
1783 if constexpr(std::is_floating_point_v
<T
>)
1784 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1785 && std::isfinite(static_cast<float>(values
[1]))
1786 && std::isfinite(static_cast<float>(values
[2])));
1788 Source
->Velocity
[0] = static_cast<float>(values
[0]);
1789 Source
->Velocity
[1] = static_cast<float>(values
[1]);
1790 Source
->Velocity
[2] = static_cast<float>(values
[2]);
1791 return CommitAndUpdateSourceProps(Source
, Context
);
1795 if constexpr(std::is_floating_point_v
<T
>)
1796 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1797 && std::isfinite(static_cast<float>(values
[1]))
1798 && std::isfinite(static_cast<float>(values
[2])));
1800 Source
->Direction
[0] = static_cast<float>(values
[0]);
1801 Source
->Direction
[1] = static_cast<float>(values
[1]);
1802 Source
->Direction
[2] = static_cast<float>(values
[2]);
1803 return CommitAndUpdateSourceProps(Source
, Context
);
1805 case AL_ORIENTATION
:
1807 if constexpr(std::is_floating_point_v
<T
>)
1808 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1809 && std::isfinite(static_cast<float>(values
[1]))
1810 && std::isfinite(static_cast<float>(values
[2]))
1811 && std::isfinite(static_cast<float>(values
[3]))
1812 && std::isfinite(static_cast<float>(values
[4]))
1813 && std::isfinite(static_cast<float>(values
[5])));
1815 Source
->OrientAt
[0] = static_cast<float>(values
[0]);
1816 Source
->OrientAt
[1] = static_cast<float>(values
[1]);
1817 Source
->OrientAt
[2] = static_cast<float>(values
[2]);
1818 Source
->OrientUp
[0] = static_cast<float>(values
[3]);
1819 Source
->OrientUp
[1] = static_cast<float>(values
[4]);
1820 Source
->OrientUp
[2] = static_cast<float>(values
[5]);
1821 return UpdateSourceProps(Source
, Context
);
1824 case AL_DIRECT_FILTER
:
1825 if constexpr(std::is_integral_v
<T
>)
1828 const auto filterid
= static_cast<std::make_unsigned_t
<T
>>(values
[0]);
1831 std::lock_guard
<std::mutex
> filterlock
{device
->FilterLock
};
1832 ALfilter
*filter
{LookupFilter(device
, filterid
)};
1834 throw al::context_error
{AL_INVALID_VALUE
, "Invalid filter ID %s",
1835 std::to_string(filterid
).c_str()};
1836 Source
->Direct
.Gain
= filter
->Gain
;
1837 Source
->Direct
.GainHF
= filter
->GainHF
;
1838 Source
->Direct
.HFReference
= filter
->HFReference
;
1839 Source
->Direct
.GainLF
= filter
->GainLF
;
1840 Source
->Direct
.LFReference
= filter
->LFReference
;
1844 Source
->Direct
.Gain
= 1.0f
;
1845 Source
->Direct
.GainHF
= 1.0f
;
1846 Source
->Direct
.HFReference
= LowPassFreqRef
;
1847 Source
->Direct
.GainLF
= 1.0f
;
1848 Source
->Direct
.LFReference
= HighPassFreqRef
;
1850 return UpdateSourceProps(Source
, Context
);
1854 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1855 if constexpr(std::is_integral_v
<T
>)
1858 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1860 Source
->DryGainHFAuto
= values
[0] != AL_FALSE
;
1861 return UpdateSourceProps(Source
, Context
);
1865 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1866 if constexpr(std::is_integral_v
<T
>)
1869 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1871 Source
->WetGainAuto
= values
[0] != AL_FALSE
;
1872 return UpdateSourceProps(Source
, Context
);
1876 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1877 if constexpr(std::is_integral_v
<T
>)
1880 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1882 Source
->WetGainHFAuto
= values
[0] != AL_FALSE
;
1883 return UpdateSourceProps(Source
, Context
);
1887 case AL_DIRECT_CHANNELS_SOFT
:
1888 if constexpr(std::is_integral_v
<T
>)
1891 if(auto mode
= DirectModeFromEnum(values
[0]))
1893 Source
->DirectChannels
= *mode
;
1894 return UpdateSourceProps(Source
, Context
);
1896 throw al::context_error
{AL_INVALID_VALUE
, "Invalid direct channels mode: %s\n",
1897 HexPrinter
{values
[0]}.c_str()};
1901 case AL_DISTANCE_MODEL
:
1902 if constexpr(std::is_integral_v
<T
>)
1905 if(auto model
= DistanceModelFromALenum(values
[0]))
1907 Source
->mDistanceModel
= *model
;
1908 if(Context
->mSourceDistanceModel
)
1909 UpdateSourceProps(Source
, Context
);
1912 throw al::context_error
{AL_INVALID_VALUE
, "Invalid distance model: %s\n",
1913 HexPrinter
{values
[0]}.c_str()};
1917 case AL_SOURCE_RESAMPLER_SOFT
:
1918 if constexpr(std::is_integral_v
<T
>)
1921 CheckValue(values
[0] >= 0 && values
[0] <= static_cast<int>(Resampler::Max
));
1923 Source
->mResampler
= static_cast<Resampler
>(values
[0]);
1924 return UpdateSourceProps(Source
, Context
);
1928 case AL_SOURCE_SPATIALIZE_SOFT
:
1929 if constexpr(std::is_integral_v
<T
>)
1932 if(auto mode
= SpatializeModeFromEnum(values
[0]))
1934 Source
->mSpatialize
= *mode
;
1935 return UpdateSourceProps(Source
, Context
);
1937 throw al::context_error
{AL_INVALID_VALUE
, "Invalid source spatialize mode: %s\n",
1938 HexPrinter
{values
[0]}.c_str()};
1942 case AL_STEREO_MODE_SOFT
:
1943 if constexpr(std::is_integral_v
<T
>)
1946 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1947 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1948 throw al::context_error
{AL_INVALID_OPERATION
,
1949 "Modifying stereo mode on playing or paused source %u", Source
->id
};
1951 if(auto mode
= StereoModeFromEnum(values
[0]))
1953 Source
->mStereoMode
= *mode
;
1956 throw al::context_error
{AL_INVALID_VALUE
, "Invalid stereo mode: %s\n",
1957 HexPrinter
{values
[0]}.c_str()};
1961 case AL_AUXILIARY_SEND_FILTER
:
1962 if constexpr(std::is_integral_v
<T
>)
1965 const auto slotid
= static_cast<std::make_unsigned_t
<T
>>(values
[0]);
1966 const auto sendidx
= static_cast<std::make_unsigned_t
<T
>>(values
[1]);
1967 const auto filterid
= static_cast<std::make_unsigned_t
<T
>>(values
[2]);
1969 std::unique_lock slotlock
{Context
->mEffectSlotLock
};
1970 ALeffectslot
*slot
{};
1973 slot
= LookupEffectSlot(Context
, slotid
);
1975 throw al::context_error
{AL_INVALID_VALUE
, "Invalid effect ID %s",
1976 std::to_string(slotid
).c_str()};
1979 if(sendidx
>= device
->NumAuxSends
)
1980 throw al::context_error
{AL_INVALID_VALUE
, "Invalid send %s",
1981 std::to_string(sendidx
).c_str()};
1982 auto &send
= Source
->Send
[static_cast<size_t>(sendidx
)];
1986 std::lock_guard
<std::mutex
> filterlock
{device
->FilterLock
};
1987 ALfilter
*filter
{LookupFilter(device
, filterid
)};
1989 throw al::context_error
{AL_INVALID_VALUE
, "Invalid filter ID %s",
1990 std::to_string(filterid
).c_str()};
1992 send
.Gain
= filter
->Gain
;
1993 send
.GainHF
= filter
->GainHF
;
1994 send
.HFReference
= filter
->HFReference
;
1995 send
.GainLF
= filter
->GainLF
;
1996 send
.LFReference
= filter
->LFReference
;
2000 /* Disable filter */
2003 send
.HFReference
= LowPassFreqRef
;
2005 send
.LFReference
= HighPassFreqRef
;
2008 /* We must force an update if the current auxiliary slot is valid
2009 * and about to be changed on an active source, in case the old
2010 * slot is about to be deleted.
2012 if(send
.Slot
&& slot
!= send
.Slot
&& IsPlayingOrPaused(Source
))
2014 /* Add refcount on the new slot, and release the previous slot */
2015 if(slot
) IncrementRef(slot
->ref
);
2016 if(auto *oldslot
= send
.Slot
)
2017 DecrementRef(oldslot
->ref
);
2020 Voice
*voice
{GetSourceVoice(Source
, Context
)};
2021 if(voice
) UpdateSourceProps(Source
, voice
, Context
);
2022 else Source
->mPropsDirty
= true;
2026 if(slot
) IncrementRef(slot
->ref
);
2027 if(auto *oldslot
= send
.Slot
)
2028 DecrementRef(oldslot
->ref
);
2030 UpdateSourceProps(Source
, Context
);
2037 ERR("Unexpected %s property: 0x%04x\n", PropType
<T
>::Name(), prop
);
2038 throw al::context_error
{AL_INVALID_ENUM
, "Invalid source %s property 0x%04x",
2039 PropType
<T
>::Name(), prop
};
2043 template<typename T
, size_t N
>
2044 auto GetSizeChecker(const SourceProp prop
, const al::span
<T
,N
> values
)
2046 return [=](size_t expect
) -> void
2048 if(values
.size() == expect
) LIKELY
return;
2049 throw al::context_error
{AL_INVALID_ENUM
, "Property 0x%04x expects %zu value(s), got %zu",
2050 prop
, expect
, values
.size()};
2054 template<typename T
>
2055 NOINLINE
void GetProperty(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
2056 const al::span
<T
> values
)
2058 using std::chrono::duration_cast
;
2059 auto CheckSize
= GetSizeChecker(prop
, values
);
2060 auto *device
= Context
->mALDevice
.get();
2066 values
[0] = static_cast<T
>(Source
->Gain
);
2071 values
[0] = static_cast<T
>(Source
->Pitch
);
2074 case AL_MAX_DISTANCE
:
2076 values
[0] = static_cast<T
>(Source
->MaxDistance
);
2079 case AL_ROLLOFF_FACTOR
:
2081 values
[0] = static_cast<T
>(Source
->RolloffFactor
);
2084 case AL_REFERENCE_DISTANCE
:
2086 values
[0] = static_cast<T
>(Source
->RefDistance
);
2089 case AL_CONE_INNER_ANGLE
:
2091 values
[0] = static_cast<T
>(Source
->InnerAngle
);
2094 case AL_CONE_OUTER_ANGLE
:
2096 values
[0] = static_cast<T
>(Source
->OuterAngle
);
2101 values
[0] = static_cast<T
>(Source
->MinGain
);
2106 values
[0] = static_cast<T
>(Source
->MaxGain
);
2109 case AL_CONE_OUTER_GAIN
:
2111 values
[0] = static_cast<T
>(Source
->OuterGain
);
2115 case AL_SAMPLE_OFFSET
:
2116 case AL_BYTE_OFFSET
:
2118 values
[0] = GetSourceOffset
<T
>(Source
, prop
, Context
);
2121 case AL_CONE_OUTER_GAINHF
:
2123 values
[0] = static_cast<T
>(Source
->OuterGainHF
);
2126 case AL_AIR_ABSORPTION_FACTOR
:
2128 values
[0] = static_cast<T
>(Source
->AirAbsorptionFactor
);
2131 case AL_ROOM_ROLLOFF_FACTOR
:
2133 values
[0] = static_cast<T
>(Source
->RoomRolloffFactor
);
2136 case AL_DOPPLER_FACTOR
:
2138 values
[0] = static_cast<T
>(Source
->DopplerFactor
);
2141 case AL_SAMPLE_RW_OFFSETS_SOFT
:
2142 if constexpr(std::is_integral_v
<T
>)
2144 if(sBufferSubDataCompat
)
2147 values
[0] = GetSourceOffset
<T
>(Source
, AL_SAMPLE_OFFSET
, Context
);
2148 /* FIXME: values[1] should be ahead of values[0] by the device
2149 * update time. It needs to clamp or wrap the length of the
2152 values
[1] = values
[0];
2157 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
2158 if constexpr(std::is_floating_point_v
<T
>)
2160 if(sBufferSubDataCompat
)
2164 values
[0] = static_cast<T
>(Source
->Radius
);
2169 if(sBufferSubDataCompat
)
2172 values
[0] = GetSourceOffset
<T
>(Source
, AL_BYTE_OFFSET
, Context
);
2173 /* FIXME: values[1] should be ahead of values[0] by the device
2174 * update time. It needs to clamp or wrap the length of the
2177 values
[1] = values
[0];
2183 case AL_SUPER_STEREO_WIDTH_SOFT
:
2185 values
[0] = static_cast<T
>(Source
->EnhWidth
);
2188 case AL_BYTE_LENGTH_SOFT
:
2189 case AL_SAMPLE_LENGTH_SOFT
:
2190 case AL_SEC_LENGTH_SOFT
:
2192 values
[0] = GetSourceLength
<T
>(Source
, prop
);
2195 case AL_PANNING_ENABLED_SOFT
:
2197 values
[0] = Source
->mPanningEnabled
;
2202 values
[0] = static_cast<T
>(Source
->mPan
);
2205 case AL_STEREO_ANGLES
:
2206 if constexpr(std::is_floating_point_v
<T
>)
2209 values
[0] = static_cast<T
>(Source
->StereoPan
[0]);
2210 values
[1] = static_cast<T
>(Source
->StereoPan
[1]);
2215 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
2216 if constexpr(std::is_same_v
<T
,int64_t>)
2219 /* Get the source offset with the clock time first. Then get the
2220 * clock time with the device latency. Order is important.
2222 ClockLatency clocktime
{};
2223 nanoseconds srcclock
{};
2224 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2226 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2227 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2229 if(srcclock
== clocktime
.ClockTime
)
2230 values
[1] = nanoseconds
{clocktime
.Latency
}.count();
2233 /* If the clock time incremented, reduce the latency by that
2234 * much since it's that much closer to the source offset it got
2237 const auto diff
= std::min(clocktime
.Latency
, clocktime
.ClockTime
-srcclock
);
2238 values
[1] = nanoseconds
{clocktime
.Latency
- diff
}.count();
2244 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
2245 if constexpr(std::is_same_v
<T
,int64_t>)
2248 nanoseconds srcclock
{};
2249 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2250 values
[1] = srcclock
.count();
2255 case AL_SEC_OFFSET_LATENCY_SOFT
:
2256 if constexpr(std::is_same_v
<T
,double>)
2259 /* Get the source offset with the clock time first. Then get the
2260 * clock time with the device latency. Order is important.
2262 ClockLatency clocktime
{};
2263 nanoseconds srcclock
{};
2264 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2266 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2267 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2269 if(srcclock
== clocktime
.ClockTime
)
2270 values
[1] = duration_cast
<seconds_d
>(clocktime
.Latency
).count();
2273 /* If the clock time incremented, reduce the latency by that
2274 * much since it's that much closer to the source offset it got
2277 const auto diff
= std::min(clocktime
.Latency
, clocktime
.ClockTime
-srcclock
);
2278 values
[1] = duration_cast
<seconds_d
>(clocktime
.Latency
- diff
).count();
2284 case AL_SEC_OFFSET_CLOCK_SOFT
:
2285 if constexpr(std::is_same_v
<T
,double>)
2288 nanoseconds srcclock
{};
2289 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2290 values
[1] = duration_cast
<seconds_d
>(srcclock
).count();
2297 values
[0] = static_cast<T
>(Source
->Position
[0]);
2298 values
[1] = static_cast<T
>(Source
->Position
[1]);
2299 values
[2] = static_cast<T
>(Source
->Position
[2]);
2304 values
[0] = static_cast<T
>(Source
->Velocity
[0]);
2305 values
[1] = static_cast<T
>(Source
->Velocity
[1]);
2306 values
[2] = static_cast<T
>(Source
->Velocity
[2]);
2311 values
[0] = static_cast<T
>(Source
->Direction
[0]);
2312 values
[1] = static_cast<T
>(Source
->Direction
[1]);
2313 values
[2] = static_cast<T
>(Source
->Direction
[2]);
2316 case AL_ORIENTATION
:
2318 values
[0] = static_cast<T
>(Source
->OrientAt
[0]);
2319 values
[1] = static_cast<T
>(Source
->OrientAt
[1]);
2320 values
[2] = static_cast<T
>(Source
->OrientAt
[2]);
2321 values
[3] = static_cast<T
>(Source
->OrientUp
[0]);
2322 values
[4] = static_cast<T
>(Source
->OrientUp
[1]);
2323 values
[5] = static_cast<T
>(Source
->OrientUp
[2]);
2327 case AL_SOURCE_RELATIVE
:
2328 if constexpr(std::is_integral_v
<T
>)
2331 values
[0] = Source
->HeadRelative
;
2337 if constexpr(std::is_integral_v
<T
>)
2340 values
[0] = Source
->Looping
;
2346 if constexpr(std::is_integral_v
<T
>)
2349 const ALbufferQueueItem
*BufferList
{};
2350 /* HACK: This query should technically only return the buffer set
2351 * on a static source. However, some apps had used it to detect
2352 * when a streaming source changed buffers, so report the current
2353 * buffer's ID when playing.
2355 if(Source
->SourceType
== AL_STATIC
|| Source
->state
== AL_INITIAL
)
2357 if(!Source
->mQueue
.empty())
2358 BufferList
= &Source
->mQueue
.front();
2360 else if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2362 VoiceBufferItem
*Current
{voice
->mCurrentBuffer
.load(std::memory_order_relaxed
)};
2363 const auto iter
= std::find_if(Source
->mQueue
.cbegin(), Source
->mQueue
.cend(),
2364 [Current
](const ALbufferQueueItem
&item
) noexcept
-> bool
2365 { return &item
== Current
; });
2366 BufferList
= (iter
!= Source
->mQueue
.cend()) ? al::to_address(iter
) : nullptr;
2368 ALbuffer
*buffer
{BufferList
? BufferList
->mBuffer
: nullptr};
2369 values
[0] = buffer
? static_cast<T
>(buffer
->id
) : T
{0};
2374 case AL_SOURCE_STATE
:
2375 if constexpr(std::is_integral_v
<T
>)
2378 values
[0] = GetSourceState(Source
, GetSourceVoice(Source
, Context
));
2383 case AL_BUFFERS_QUEUED
:
2384 if constexpr(std::is_integral_v
<T
>)
2387 values
[0] = static_cast<T
>(Source
->mQueue
.size());
2392 case AL_BUFFERS_PROCESSED
:
2393 if constexpr(std::is_integral_v
<T
>)
2396 if(Source
->Looping
|| Source
->SourceType
!= AL_STREAMING
)
2398 /* Buffers on a looping source are in a perpetual state of
2399 * PENDING, so don't report any as PROCESSED
2406 if(Source
->state
!= AL_INITIAL
)
2408 const VoiceBufferItem
*Current
{nullptr};
2409 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2410 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
2411 for(auto &item
: Source
->mQueue
)
2413 if(&item
== Current
)
2424 case AL_SOURCE_TYPE
:
2425 if constexpr(std::is_integral_v
<T
>)
2428 values
[0] = Source
->SourceType
;
2433 case AL_DIRECT_FILTER_GAINHF_AUTO
:
2434 if constexpr(std::is_integral_v
<T
>)
2437 values
[0] = Source
->DryGainHFAuto
;
2442 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
2443 if constexpr(std::is_integral_v
<T
>)
2446 values
[0] = Source
->WetGainAuto
;
2451 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
2452 if constexpr(std::is_integral_v
<T
>)
2455 values
[0] = Source
->WetGainHFAuto
;
2460 case AL_DIRECT_CHANNELS_SOFT
:
2461 if constexpr(std::is_integral_v
<T
>)
2464 values
[0] = EnumFromDirectMode(Source
->DirectChannels
);
2469 case AL_DISTANCE_MODEL
:
2470 if constexpr(std::is_integral_v
<T
>)
2473 values
[0] = ALenumFromDistanceModel(Source
->mDistanceModel
);
2478 case AL_SOURCE_RESAMPLER_SOFT
:
2479 if constexpr(std::is_integral_v
<T
>)
2482 values
[0] = static_cast<T
>(Source
->mResampler
);
2487 case AL_SOURCE_SPATIALIZE_SOFT
:
2488 if constexpr(std::is_integral_v
<T
>)
2491 values
[0] = EnumFromSpatializeMode(Source
->mSpatialize
);
2496 case AL_STEREO_MODE_SOFT
:
2497 if constexpr(std::is_integral_v
<T
>)
2500 values
[0] = EnumFromStereoMode(Source
->mStereoMode
);
2505 case AL_DIRECT_FILTER
:
2506 case AL_AUXILIARY_SEND_FILTER
:
2510 ERR("Unexpected %s query property: 0x%04x\n", PropType
<T
>::Name(), prop
);
2511 throw al::context_error
{AL_INVALID_ENUM
, "Invalid source %s query property 0x%04x",
2512 PropType
<T
>::Name(), prop
};
2516 void StartSources(ALCcontext
*const context
, const al::span
<ALsource
*> srchandles
,
2517 const nanoseconds start_time
=nanoseconds::min())
2519 auto *device
= context
->mALDevice
.get();
2520 /* If the device is disconnected, and voices stop on disconnect, go right
2523 if(!device
->Connected
.load(std::memory_order_acquire
)) UNLIKELY
2525 if(context
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
2527 for(ALsource
*source
: srchandles
)
2529 /* TODO: Send state change event? */
2530 source
->Offset
= 0.0;
2531 source
->OffsetType
= AL_NONE
;
2532 source
->state
= AL_STOPPED
;
2538 /* Count the number of reusable voices. */
2539 auto voicelist
= context
->getVoicesSpan();
2540 size_t free_voices
{0};
2541 for(const Voice
*voice
: voicelist
)
2543 free_voices
+= (voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2544 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2545 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false);
2546 if(free_voices
== srchandles
.size())
2549 if(srchandles
.size() != free_voices
) UNLIKELY
2551 const size_t inc_amount
{srchandles
.size() - free_voices
};
2552 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
2553 if(inc_amount
> allvoices
.size() - voicelist
.size())
2555 /* Increase the number of voices to handle the request. */
2556 context
->allocVoices(inc_amount
- (allvoices
.size() - voicelist
.size()));
2558 context
->mActiveVoiceCount
.fetch_add(inc_amount
, std::memory_order_release
);
2559 voicelist
= context
->getVoicesSpan();
2562 auto voiceiter
= voicelist
.begin();
2564 VoiceChange
*tail
{}, *cur
{};
2565 for(ALsource
*source
: srchandles
)
2567 /* Check that there is a queue containing at least one valid, non zero
2570 auto find_buffer
= [](ALbufferQueueItem
&entry
) noexcept
2571 { return entry
.mSampleLen
!= 0 || entry
.mCallback
!= nullptr; };
2572 auto BufferList
= std::find_if(source
->mQueue
.begin(), source
->mQueue
.end(), find_buffer
);
2574 /* If there's nothing to play, go right to stopped. */
2575 if(BufferList
== source
->mQueue
.end()) UNLIKELY
2577 /* NOTE: A source without any playable buffers should not have a
2578 * Voice since it shouldn't be in a playing or paused state. So
2579 * there's no need to look up its voice and clear the source.
2581 source
->Offset
= 0.0;
2582 source
->OffsetType
= AL_NONE
;
2583 source
->state
= AL_STOPPED
;
2588 cur
= tail
= GetVoiceChanger(context
);
2591 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
2592 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
2595 Voice
*voice
{GetSourceVoice(source
, context
)};
2596 switch(GetSourceState(source
, voice
))
2599 /* A source that's paused simply resumes. If there's no voice, it
2600 * was lost from a disconnect, so just start over with a new one.
2602 cur
->mOldVoice
= nullptr;
2604 cur
->mVoice
= voice
;
2605 cur
->mSourceID
= source
->id
;
2606 cur
->mState
= VChangeState::Play
;
2607 source
->state
= AL_PLAYING
;
2609 if(context
->hasEax())
2610 source
->eaxCommit();
2611 #endif // ALSOFT_EAX
2615 /* A source that's already playing is restarted from the beginning.
2616 * Stop the current voice and start a new one so it properly cross-
2617 * fades back to the beginning.
2620 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
2621 cur
->mOldVoice
= voice
;
2626 assert(voice
== nullptr);
2627 cur
->mOldVoice
= nullptr;
2629 if(context
->hasEax())
2630 source
->eaxCommit();
2631 #endif // ALSOFT_EAX
2635 /* Find the next unused voice to play this source with. */
2636 for(;voiceiter
!= voicelist
.end();++voiceiter
,++vidx
)
2638 Voice
*v
{*voiceiter
};
2639 if(v
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2640 && v
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2641 && v
->mPendingChange
.load(std::memory_order_relaxed
) == false)
2647 ASSUME(voice
!= nullptr);
2649 voice
->mPosition
.store(0, std::memory_order_relaxed
);
2650 voice
->mPositionFrac
.store(0, std::memory_order_relaxed
);
2651 voice
->mCurrentBuffer
.store(&source
->mQueue
.front(), std::memory_order_relaxed
);
2652 voice
->mStartTime
= start_time
;
2653 voice
->mFlags
.reset();
2654 /* A source that's not playing or paused has any offset applied when it
2657 if(const ALenum offsettype
{source
->OffsetType
})
2659 const double offset
{source
->Offset
};
2660 source
->OffsetType
= AL_NONE
;
2661 source
->Offset
= 0.0;
2662 if(auto vpos
= GetSampleOffset(source
->mQueue
, offsettype
, offset
))
2664 voice
->mPosition
.store(vpos
->pos
, std::memory_order_relaxed
);
2665 voice
->mPositionFrac
.store(vpos
->frac
, std::memory_order_relaxed
);
2666 voice
->mCurrentBuffer
.store(vpos
->bufferitem
, std::memory_order_relaxed
);
2667 if(vpos
->pos
> 0 || (vpos
->pos
== 0 && vpos
->frac
> 0)
2668 || vpos
->bufferitem
!= &source
->mQueue
.front())
2669 voice
->mFlags
.set(VoiceIsFading
);
2672 InitVoice(voice
, source
, al::to_address(BufferList
), context
, device
);
2674 source
->VoiceIdx
= vidx
;
2675 source
->state
= AL_PLAYING
;
2677 cur
->mVoice
= voice
;
2678 cur
->mSourceID
= source
->id
;
2679 cur
->mState
= VChangeState::Play
;
2682 SendVoiceChanges(context
, tail
);
2687 AL_API
DECL_FUNC2(void, alGenSources
, ALsizei
,n
, ALuint
*,sources
)
2688 FORCE_ALIGN
void AL_APIENTRY
alGenSourcesDirect(ALCcontext
*context
, ALsizei n
, ALuint
*sources
) noexcept
2691 throw al::context_error
{AL_INVALID_VALUE
, "Generating %d sources", n
};
2692 if(n
<= 0) UNLIKELY
return;
2694 auto srclock
= std::unique_lock
{context
->mSourceLock
};
2695 auto *device
= context
->mALDevice
.get();
2697 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2698 if(context
->mNumSources
> device
->SourcesMax
2699 || sids
.size() > device
->SourcesMax
-context
->mNumSources
)
2700 throw al::context_error
{AL_OUT_OF_MEMORY
, "Exceeding %u source limit (%u + %d)",
2701 device
->SourcesMax
, context
->mNumSources
, n
};
2702 if(!EnsureSources(context
, sids
.size()))
2703 throw al::context_error
{AL_OUT_OF_MEMORY
, "Failed to allocate %d source%s", n
,
2704 (n
== 1) ? "" : "s"};
2706 std::generate(sids
.begin(), sids
.end(), [context
]{ return AllocSource(context
)->id
; });
2708 catch(al::context_error
& e
) {
2709 context
->setError(e
.errorCode(), "%s", e
.what());
2712 AL_API
DECL_FUNC2(void, alDeleteSources
, ALsizei
,n
, const ALuint
*,sources
)
2713 FORCE_ALIGN
void AL_APIENTRY
alDeleteSourcesDirect(ALCcontext
*context
, ALsizei n
,
2714 const ALuint
*sources
) noexcept
2717 throw al::context_error
{AL_INVALID_VALUE
, "Deleting %d sources", n
};
2718 if(n
<= 0) UNLIKELY
return;
2720 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2722 /* Check that all Sources are valid */
2723 auto validate_source
= [context
](const ALuint sid
) -> bool
2724 { return LookupSource(context
, sid
) != nullptr; };
2726 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2727 auto invsrc
= std::find_if_not(sids
.begin(), sids
.end(), validate_source
);
2728 if(invsrc
!= sids
.end())
2729 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", *invsrc
};
2731 /* All good. Delete source IDs. */
2732 auto delete_source
= [&context
](const ALuint sid
) -> void
2734 if(ALsource
*src
{LookupSource(context
, sid
)})
2735 FreeSource(context
, src
);
2737 std::for_each(sids
.begin(), sids
.end(), delete_source
);
2739 catch(al::context_error
& e
) {
2740 context
->setError(e
.errorCode(), "%s", e
.what());
2743 AL_API
DECL_FUNC1(ALboolean
, alIsSource
, ALuint
,source
)
2744 FORCE_ALIGN ALboolean AL_APIENTRY
alIsSourceDirect(ALCcontext
*context
, ALuint source
) noexcept
2746 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2747 if(LookupSource(context
, source
) != nullptr)
2753 AL_API
DECL_FUNC3(void, alSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
,value
)
2754 FORCE_ALIGN
void AL_APIENTRY
alSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2755 ALfloat value
) noexcept
2757 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2758 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2759 ALsource
*Source
{LookupSource(context
, source
)};
2761 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2763 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2765 catch(al::context_error
& e
) {
2766 context
->setError(e
.errorCode(), "%s", e
.what());
2769 AL_API
DECL_FUNC5(void, alSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
,value1
, ALfloat
,value2
, ALfloat
,value3
)
2770 FORCE_ALIGN
void AL_APIENTRY
alSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2771 ALfloat value1
, ALfloat value2
, ALfloat value3
) noexcept
2773 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2774 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2775 ALsource
*Source
{LookupSource(context
, source
)};
2777 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2779 const std::array fvals
{value1
, value2
, value3
};
2780 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
2782 catch(al::context_error
& e
) {
2783 context
->setError(e
.errorCode(), "%s", e
.what());
2786 AL_API
DECL_FUNC3(void, alSourcefv
, ALuint
,source
, ALenum
,param
, const ALfloat
*,values
)
2787 FORCE_ALIGN
void AL_APIENTRY
alSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2788 const ALfloat
*values
) noexcept
2790 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2791 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2792 ALsource
*Source
{LookupSource(context
, source
)};
2794 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2796 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2798 const ALuint count
{FloatValsByProp(param
)};
2799 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2801 catch(al::context_error
& e
) {
2802 context
->setError(e
.errorCode(), "%s", e
.what());
2806 AL_API
DECL_FUNCEXT3(void, alSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value
)
2807 FORCE_ALIGN
void AL_APIENTRY
alSourcedDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2808 ALdouble value
) noexcept
2810 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2811 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2812 ALsource
*Source
{LookupSource(context
, source
)};
2814 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2816 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1});
2818 catch(al::context_error
& e
) {
2819 context
->setError(e
.errorCode(), "%s", e
.what());
2822 AL_API
DECL_FUNCEXT5(void, alSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value1
, ALdouble
,value2
, ALdouble
,value3
)
2823 FORCE_ALIGN
void AL_APIENTRY
alSource3dDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2824 ALdouble value1
, ALdouble value2
, ALdouble value3
) noexcept
2826 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2827 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2828 ALsource
*Source
{LookupSource(context
, source
)};
2830 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2832 const std::array dvals
{value1
, value2
, value3
};
2833 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
2835 catch(al::context_error
& e
) {
2836 context
->setError(e
.errorCode(), "%s", e
.what());
2839 AL_API
DECL_FUNCEXT3(void, alSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, const ALdouble
*,values
)
2840 FORCE_ALIGN
void AL_APIENTRY
alSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2841 const ALdouble
*values
) noexcept
2843 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2844 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2845 ALsource
*Source
{LookupSource(context
, source
)};
2847 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2849 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2851 const ALuint count
{DoubleValsByProp(param
)};
2852 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2854 catch(al::context_error
& e
) {
2855 context
->setError(e
.errorCode(), "%s", e
.what());
2859 AL_API
DECL_FUNC3(void, alSourcei
, ALuint
,source
, ALenum
,param
, ALint
,value
)
2860 FORCE_ALIGN
void AL_APIENTRY
alSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2861 ALint value
) noexcept
2863 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2864 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2865 ALsource
*Source
{LookupSource(context
, source
)};
2867 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2869 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2871 catch(al::context_error
& e
) {
2872 context
->setError(e
.errorCode(), "%s", e
.what());
2875 AL_API
DECL_FUNC5(void, alSource3i
, ALuint
,buffer
, ALenum
,param
, ALint
,value1
, ALint
,value2
, ALint
,value3
)
2876 FORCE_ALIGN
void AL_APIENTRY
alSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2877 ALint value1
, ALint value2
, ALint value3
) noexcept
2879 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2880 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2881 ALsource
*Source
{LookupSource(context
, source
)};
2883 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2885 const std::array ivals
{value1
, value2
, value3
};
2886 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
2888 catch(al::context_error
& e
) {
2889 context
->setError(e
.errorCode(), "%s", e
.what());
2892 AL_API
DECL_FUNC3(void, alSourceiv
, ALuint
,source
, ALenum
,param
, const ALint
*,values
)
2893 FORCE_ALIGN
void AL_APIENTRY
alSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2894 const ALint
*values
) noexcept
2896 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2897 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2898 ALsource
*Source
{LookupSource(context
, source
)};
2900 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2902 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2904 const ALuint count
{IntValsByProp(param
)};
2905 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2907 catch(al::context_error
& e
) {
2908 context
->setError(e
.errorCode(), "%s", e
.what());
2912 AL_API
DECL_FUNCEXT3(void, alSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value
)
2913 FORCE_ALIGN
void AL_APIENTRY
alSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
,
2914 ALenum param
, ALint64SOFT value
) noexcept
2916 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2917 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2918 ALsource
*Source
{LookupSource(context
, source
)};
2920 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2922 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2924 catch(al::context_error
& e
) {
2925 context
->setError(e
.errorCode(), "%s", e
.what());
2928 AL_API
DECL_FUNCEXT5(void, alSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value1
, ALint64SOFT
,value2
, ALint64SOFT
,value3
)
2929 FORCE_ALIGN
void AL_APIENTRY
alSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
2930 ALenum param
, ALint64SOFT value1
, ALint64SOFT value2
, ALint64SOFT value3
) noexcept
2932 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2933 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2934 ALsource
*Source
{LookupSource(context
, source
)};
2936 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2938 const std::array i64vals
{value1
, value2
, value3
};
2939 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
2941 catch(al::context_error
& e
) {
2942 context
->setError(e
.errorCode(), "%s", e
.what());
2945 AL_API
DECL_FUNCEXT3(void, alSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, const ALint64SOFT
*,values
)
2946 FORCE_ALIGN
void AL_APIENTRY
alSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
2947 ALenum param
, const ALint64SOFT
*values
) noexcept
2949 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2950 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2951 ALsource
*Source
{LookupSource(context
, source
)};
2953 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2955 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2957 const ALuint count
{Int64ValsByProp(param
)};
2958 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2960 catch(al::context_error
& e
) {
2961 context
->setError(e
.errorCode(), "%s", e
.what());
2965 AL_API
DECL_FUNC3(void, alGetSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
*,value
)
2966 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2967 ALfloat
*value
) noexcept
2969 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2970 ALsource
*Source
{LookupSource(context
, source
)};
2972 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2974 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2976 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
2978 catch(al::context_error
& e
) {
2979 context
->setError(e
.errorCode(), "%s", e
.what());
2982 AL_API
DECL_FUNC5(void, alGetSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
*,value1
, ALfloat
*,value2
, ALfloat
*,value3
)
2983 FORCE_ALIGN
void AL_APIENTRY
alGetSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2984 ALfloat
*value1
, ALfloat
*value2
, ALfloat
*value3
) noexcept
2986 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2987 ALsource
*Source
{LookupSource(context
, source
)};
2989 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2990 if(!(value1
&& value2
&& value3
))
2991 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2993 std::array
<float,3> fvals
{};
2994 GetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
2999 catch(al::context_error
& e
) {
3000 context
->setError(e
.errorCode(), "%s", e
.what());
3003 AL_API
DECL_FUNC3(void, alGetSourcefv
, ALuint
,source
, ALenum
,param
, ALfloat
*,values
)
3004 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3005 ALfloat
*values
) noexcept
3007 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3008 ALsource
*Source
{LookupSource(context
, source
)};
3010 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3012 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3014 const ALuint count
{FloatValsByProp(param
)};
3015 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3017 catch(al::context_error
& e
) {
3018 context
->setError(e
.errorCode(), "%s", e
.what());
3022 AL_API
DECL_FUNCEXT3(void, alGetSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value
)
3023 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedDirectSOFT(ALCcontext
*context
, ALuint source
,
3024 ALenum param
, ALdouble
*value
) noexcept
3026 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3027 ALsource
*Source
{LookupSource(context
, source
)};
3029 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3031 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3033 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3035 catch(al::context_error
& e
) {
3036 context
->setError(e
.errorCode(), "%s", e
.what());
3039 AL_API
DECL_FUNCEXT5(void, alGetSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value1
, ALdouble
*,value2
, ALdouble
*,value3
)
3040 FORCE_ALIGN
void AL_APIENTRY
alGetSource3dDirectSOFT(ALCcontext
*context
, ALuint source
,
3041 ALenum param
, ALdouble
*value1
, ALdouble
*value2
, ALdouble
*value3
) noexcept
3043 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3044 ALsource
*Source
{LookupSource(context
, source
)};
3046 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3047 if(!(value1
&& value2
&& value3
))
3048 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3050 std::array
<double,3> dvals
{};
3051 GetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
3056 catch(al::context_error
& e
) {
3057 context
->setError(e
.errorCode(), "%s", e
.what());
3060 AL_API
DECL_FUNCEXT3(void, alGetSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,values
)
3061 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
,
3062 ALenum param
, ALdouble
*values
) noexcept
3064 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3065 ALsource
*Source
{LookupSource(context
, source
)};
3067 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3069 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3071 const ALuint count
{DoubleValsByProp(param
)};
3072 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3074 catch(al::context_error
& e
) {
3075 context
->setError(e
.errorCode(), "%s", e
.what());
3079 AL_API
DECL_FUNC3(void, alGetSourcei
, ALuint
,source
, ALenum
,param
, ALint
*,value
)
3080 FORCE_ALIGN
void AL_APIENTRY
alGetSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3081 ALint
*value
) noexcept
3083 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3084 ALsource
*Source
{LookupSource(context
, source
)};
3086 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3088 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3090 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3092 catch(al::context_error
& e
) {
3093 context
->setError(e
.errorCode(), "%s", e
.what());
3096 AL_API
DECL_FUNC5(void, alGetSource3i
, ALuint
,source
, ALenum
,param
, ALint
*,value1
, ALint
*,value2
, ALint
*,value3
)
3097 FORCE_ALIGN
void AL_APIENTRY
alGetSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3098 ALint
*value1
, ALint
*value2
, ALint
*value3
) noexcept
3100 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3101 ALsource
*Source
{LookupSource(context
, source
)};
3103 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3104 if(!(value1
&& value2
&& value3
))
3105 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3107 std::array
<int,3> ivals
{};
3108 GetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
3113 catch(al::context_error
& e
) {
3114 context
->setError(e
.errorCode(), "%s", e
.what());
3117 AL_API
DECL_FUNC3(void, alGetSourceiv
, ALuint
,source
, ALenum
,param
, ALint
*,values
)
3118 FORCE_ALIGN
void AL_APIENTRY
alGetSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3119 ALint
*values
) noexcept
3121 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3122 ALsource
*Source
{LookupSource(context
, source
)};
3124 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3126 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3128 const ALuint count
{IntValsByProp(param
)};
3129 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3131 catch(al::context_error
& e
) {
3132 context
->setError(e
.errorCode(), "%s", e
.what());
3136 AL_API
DECL_FUNCEXT3(void, alGetSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value
)
3137 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
, ALint64SOFT
*value
) noexcept
3139 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3140 ALsource
*Source
{LookupSource(context
, source
)};
3142 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3144 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3146 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3148 catch(al::context_error
& e
) {
3149 context
->setError(e
.errorCode(), "%s", e
.what());
3152 AL_API
DECL_FUNCEXT5(void, alGetSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value1
, ALint64SOFT
*,value2
, ALint64SOFT
*,value3
)
3153 FORCE_ALIGN
void AL_APIENTRY
alGetSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
3154 ALenum param
, ALint64SOFT
*value1
, ALint64SOFT
*value2
, ALint64SOFT
*value3
) noexcept
3156 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3157 ALsource
*Source
{LookupSource(context
, source
)};
3159 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3160 if(!(value1
&& value2
&& value3
))
3161 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3163 std::array
<int64_t,3> i64vals
{};
3164 GetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
3165 *value1
= i64vals
[0];
3166 *value2
= i64vals
[1];
3167 *value3
= i64vals
[2];
3169 catch(al::context_error
& e
) {
3170 context
->setError(e
.errorCode(), "%s", e
.what());
3173 AL_API
DECL_FUNCEXT3(void, alGetSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,values
)
3174 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
3175 ALenum param
, ALint64SOFT
*values
) noexcept
3177 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3178 ALsource
*Source
{LookupSource(context
, source
)};
3180 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3182 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3184 const ALuint count
{Int64ValsByProp(param
)};
3185 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3187 catch(al::context_error
& e
) {
3188 context
->setError(e
.errorCode(), "%s", e
.what());
3192 AL_API
DECL_FUNC1(void, alSourcePlay
, ALuint
,source
)
3193 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayDirect(ALCcontext
*context
, ALuint source
) noexcept
3195 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3196 ALsource
*Source
{LookupSource(context
, source
)};
3198 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3200 StartSources(context
, {&Source
, 1});
3202 catch(al::context_error
& e
) {
3203 context
->setError(e
.errorCode(), "%s", e
.what());
3206 FORCE_ALIGN
DECL_FUNCEXT2(void, alSourcePlayAtTime
,SOFT
, ALuint
,source
, ALint64SOFT
,start_time
)
3207 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimeDirectSOFT(ALCcontext
*context
, ALuint source
,
3208 ALint64SOFT start_time
) noexcept
3211 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3213 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3214 ALsource
*Source
{LookupSource(context
, source
)};
3216 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3218 StartSources(context
, {&Source
, 1}, nanoseconds
{start_time
});
3220 catch(al::context_error
& e
) {
3221 context
->setError(e
.errorCode(), "%s", e
.what());
3224 AL_API
DECL_FUNC2(void, alSourcePlayv
, ALsizei
,n
, const ALuint
*,sources
)
3225 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayvDirect(ALCcontext
*context
, ALsizei n
,
3226 const ALuint
*sources
) noexcept
3229 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3230 if(n
<= 0) UNLIKELY
return;
3232 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3233 source_store_variant source_store
;
3234 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3236 if(count
> std::tuple_size_v
<source_store_array
>)
3237 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3238 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3241 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3242 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3244 if(ALsource
*src
{LookupSource(context
, sid
)})
3246 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3248 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3250 StartSources(context
, srchandles
);
3252 catch(al::context_error
& e
) {
3253 context
->setError(e
.errorCode(), "%s", e
.what());
3256 FORCE_ALIGN
DECL_FUNCEXT3(void, alSourcePlayAtTimev
,SOFT
, ALsizei
,n
, const ALuint
*,sources
, ALint64SOFT
,start_time
)
3257 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimevDirectSOFT(ALCcontext
*context
, ALsizei n
,
3258 const ALuint
*sources
, ALint64SOFT start_time
) noexcept
3261 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3262 if(n
<= 0) UNLIKELY
return;
3265 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3267 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3268 source_store_variant source_store
;
3269 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3271 if(count
> std::tuple_size_v
<source_store_array
>)
3272 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3273 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3276 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3277 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3279 if(ALsource
*src
{LookupSource(context
, sid
)})
3281 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3283 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3285 StartSources(context
, srchandles
, nanoseconds
{start_time
});
3287 catch(al::context_error
& e
) {
3288 context
->setError(e
.errorCode(), "%s", e
.what());
3292 AL_API
DECL_FUNC1(void, alSourcePause
, ALuint
,source
)
3293 FORCE_ALIGN
void AL_APIENTRY
alSourcePauseDirect(ALCcontext
*context
, ALuint source
) noexcept
3294 { alSourcePausevDirect(context
, 1, &source
); }
3296 AL_API
DECL_FUNC2(void, alSourcePausev
, ALsizei
,n
, const ALuint
*,sources
)
3297 FORCE_ALIGN
void AL_APIENTRY
alSourcePausevDirect(ALCcontext
*context
, ALsizei n
,
3298 const ALuint
*sources
) noexcept
3301 throw al::context_error
{AL_INVALID_VALUE
, "Pausing %d sources", n
};
3302 if(n
<= 0) UNLIKELY
return;
3304 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3305 source_store_variant source_store
;
3306 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3308 if(count
> std::tuple_size_v
<source_store_array
>)
3309 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3310 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3313 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3314 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3316 if(ALsource
*src
{LookupSource(context
, sid
)})
3318 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3320 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3322 /* Pausing has to be done in two steps. First, for each source that's
3323 * detected to be playing, chamge the voice (asynchronously) to
3326 VoiceChange
*tail
{}, *cur
{};
3327 for(ALsource
*source
: srchandles
)
3329 Voice
*voice
{GetSourceVoice(source
, context
)};
3330 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3333 cur
= tail
= GetVoiceChanger(context
);
3336 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3337 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3339 cur
->mVoice
= voice
;
3340 cur
->mSourceID
= source
->id
;
3341 cur
->mState
= VChangeState::Pause
;
3346 SendVoiceChanges(context
, tail
);
3347 /* Second, now that the voice changes have been sent, because it's
3348 * possible that the voice stopped after it was detected playing and
3349 * before the voice got paused, recheck that the source is still
3350 * considered playing and set it to paused if so.
3352 for(ALsource
*source
: srchandles
)
3354 Voice
*voice
{GetSourceVoice(source
, context
)};
3355 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3356 source
->state
= AL_PAUSED
;
3360 catch(al::context_error
& e
) {
3361 context
->setError(e
.errorCode(), "%s", e
.what());
3365 AL_API
DECL_FUNC1(void, alSourceStop
, ALuint
,source
)
3366 FORCE_ALIGN
void AL_APIENTRY
alSourceStopDirect(ALCcontext
*context
, ALuint source
) noexcept
3367 { alSourceStopvDirect(context
, 1, &source
); }
3369 AL_API
DECL_FUNC2(void, alSourceStopv
, ALsizei
,n
, const ALuint
*,sources
)
3370 FORCE_ALIGN
void AL_APIENTRY
alSourceStopvDirect(ALCcontext
*context
, ALsizei n
,
3371 const ALuint
*sources
) noexcept
3374 throw al::context_error
{AL_INVALID_VALUE
, "Stopping %d sources", n
};
3375 if(n
<= 0) UNLIKELY
return;
3377 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3378 source_store_variant source_store
;
3379 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3381 if(count
> std::tuple_size_v
<source_store_array
>)
3382 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3383 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3386 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3387 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3389 if(ALsource
*src
{LookupSource(context
, sid
)})
3391 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3393 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3395 VoiceChange
*tail
{}, *cur
{};
3396 for(ALsource
*source
: srchandles
)
3398 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3401 cur
= tail
= GetVoiceChanger(context
);
3404 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3405 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3407 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3408 cur
->mVoice
= voice
;
3409 cur
->mSourceID
= source
->id
;
3410 cur
->mState
= VChangeState::Stop
;
3411 source
->state
= AL_STOPPED
;
3413 source
->Offset
= 0.0;
3414 source
->OffsetType
= AL_NONE
;
3415 source
->VoiceIdx
= InvalidVoiceIndex
;
3418 SendVoiceChanges(context
, tail
);
3420 catch(al::context_error
& e
) {
3421 context
->setError(e
.errorCode(), "%s", e
.what());
3425 AL_API
DECL_FUNC1(void, alSourceRewind
, ALuint
,source
)
3426 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindDirect(ALCcontext
*context
, ALuint source
) noexcept
3427 { alSourceRewindvDirect(context
, 1, &source
); }
3429 AL_API
DECL_FUNC2(void, alSourceRewindv
, ALsizei
,n
, const ALuint
*,sources
)
3430 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindvDirect(ALCcontext
*context
, ALsizei n
,
3431 const ALuint
*sources
) noexcept
3434 throw al::context_error
{AL_INVALID_VALUE
, "Rewinding %d sources", n
};
3435 if(n
<= 0) UNLIKELY
return;
3437 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3438 source_store_variant source_store
;
3439 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3441 if(count
> std::tuple_size_v
<source_store_array
>)
3442 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3443 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3446 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3447 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3449 if(ALsource
*src
{LookupSource(context
, sid
)})
3451 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3453 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3455 VoiceChange
*tail
{}, *cur
{};
3456 for(ALsource
*source
: srchandles
)
3458 Voice
*voice
{GetSourceVoice(source
, context
)};
3459 if(source
->state
!= AL_INITIAL
)
3462 cur
= tail
= GetVoiceChanger(context
);
3465 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3466 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3469 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3470 cur
->mVoice
= voice
;
3471 cur
->mSourceID
= source
->id
;
3472 cur
->mState
= VChangeState::Reset
;
3473 source
->state
= AL_INITIAL
;
3475 source
->Offset
= 0.0;
3476 source
->OffsetType
= AL_NONE
;
3477 source
->VoiceIdx
= InvalidVoiceIndex
;
3480 SendVoiceChanges(context
, tail
);
3482 catch(al::context_error
& e
) {
3483 context
->setError(e
.errorCode(), "%s", e
.what());
3487 AL_API
DECL_FUNC3(void, alSourceQueueBuffers
, ALuint
,source
, ALsizei
,nb
, const ALuint
*,buffers
)
3488 FORCE_ALIGN
void AL_APIENTRY
alSourceQueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3489 ALsizei nb
, const ALuint
*buffers
) noexcept
3492 throw al::context_error
{AL_INVALID_VALUE
, "Queueing %d buffers", nb
};
3493 if(nb
<= 0) UNLIKELY
return;
3495 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3496 ALsource
*source
{LookupSource(context
,src
)};
3498 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3500 /* Can't queue on a Static Source */
3501 if(source
->SourceType
== AL_STATIC
)
3502 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing onto static source %u", src
};
3504 /* Check for a valid Buffer, for its frequency and format */
3505 auto *device
= context
->mALDevice
.get();
3506 ALbuffer
*BufferFmt
{nullptr};
3507 for(auto &item
: source
->mQueue
)
3509 BufferFmt
= item
.mBuffer
;
3510 if(BufferFmt
) break;
3513 std::unique_lock
<std::mutex
> buflock
{device
->BufferLock
};
3514 const auto bids
= al::span
{buffers
, static_cast<ALuint
>(nb
)};
3515 const size_t NewListStart
{source
->mQueue
.size()};
3517 ALbufferQueueItem
*BufferList
{nullptr};
3518 std::for_each(bids
.cbegin(), bids
.cend(),
3519 [source
,device
,&BufferFmt
,&BufferList
](const ALuint bid
)
3521 ALbuffer
*buffer
{bid
? LookupBuffer(device
, bid
) : nullptr};
3523 throw al::context_error
{AL_INVALID_NAME
, "Queueing invalid buffer ID %u", bid
};
3527 if(buffer
->mSampleRate
< 1)
3528 throw al::context_error
{AL_INVALID_OPERATION
,
3529 "Queueing buffer %u with no format", buffer
->id
};
3531 if(buffer
->mCallback
)
3532 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing callback buffer %u",
3535 if(buffer
->MappedAccess
!= 0 && !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
3536 throw al::context_error
{AL_INVALID_OPERATION
,
3537 "Queueing non-persistently mapped buffer %u", buffer
->id
};
3540 source
->mQueue
.emplace_back();
3542 BufferList
= &source
->mQueue
.back();
3545 auto &item
= source
->mQueue
.back();
3546 BufferList
->mNext
.store(&item
, std::memory_order_relaxed
);
3550 BufferList
->mBlockAlign
= buffer
->mBlockAlign
;
3551 BufferList
->mSampleLen
= buffer
->mSampleLen
;
3552 BufferList
->mLoopEnd
= buffer
->mSampleLen
;
3553 BufferList
->mSamples
= buffer
->mData
;
3554 BufferList
->mBuffer
= buffer
;
3555 IncrementRef(buffer
->ref
);
3557 bool fmt_mismatch
{false};
3558 if(BufferFmt
== nullptr)
3562 fmt_mismatch
|= BufferFmt
->mSampleRate
!= buffer
->mSampleRate
;
3563 fmt_mismatch
|= BufferFmt
->mChannels
!= buffer
->mChannels
;
3564 fmt_mismatch
|= BufferFmt
->mType
!= buffer
->mType
;
3565 if(BufferFmt
->isBFormat())
3567 fmt_mismatch
|= BufferFmt
->mAmbiLayout
!= buffer
->mAmbiLayout
;
3568 fmt_mismatch
|= BufferFmt
->mAmbiScaling
!= buffer
->mAmbiScaling
;
3570 fmt_mismatch
|= BufferFmt
->mAmbiOrder
!= buffer
->mAmbiOrder
;
3573 throw al::context_error
{AL_INVALID_OPERATION
,
3574 "Queueing buffer with mismatched format\n"
3575 " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt
->mSampleRate
,
3576 NameFromFormat(BufferFmt
->mType
), NameFromFormat(BufferFmt
->mChannels
),
3577 buffer
->mSampleRate
, NameFromFormat(buffer
->mType
),
3578 NameFromFormat(buffer
->mChannels
)};
3582 /* A buffer failed (invalid ID or format), or there was some other
3583 * unexpected error, so unlock and release each buffer we had.
3585 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3586 for(;iter
!= source
->mQueue
.end();++iter
)
3588 if(ALbuffer
*buf
{iter
->mBuffer
})
3589 DecrementRef(buf
->ref
);
3591 source
->mQueue
.resize(NewListStart
);
3594 /* All buffers good. */
3597 /* Source is now streaming */
3598 source
->SourceType
= AL_STREAMING
;
3600 if(NewListStart
!= 0)
3602 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3603 (iter
-1)->mNext
.store(al::to_address(iter
), std::memory_order_release
);
3606 catch(al::context_error
& e
) {
3607 context
->setError(e
.errorCode(), "%s", e
.what());
3610 AL_API
DECL_FUNC3(void, alSourceUnqueueBuffers
, ALuint
,source
, ALsizei
,nb
, ALuint
*,buffers
)
3611 FORCE_ALIGN
void AL_APIENTRY
alSourceUnqueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3612 ALsizei nb
, ALuint
*buffers
) noexcept
3615 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffers", nb
};
3616 if(nb
<= 0) UNLIKELY
return;
3618 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3619 ALsource
*source
{LookupSource(context
,src
)};
3621 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3623 if(source
->SourceType
!= AL_STREAMING
)
3624 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from a non-streaming source %u",src
};
3626 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from looping source %u", src
};
3628 /* Make sure enough buffers have been processed to unqueue. */
3629 const al::span bids
{buffers
, static_cast<ALuint
>(nb
)};
3630 size_t processed
{0};
3631 if(source
->state
!= AL_INITIAL
) LIKELY
3633 VoiceBufferItem
*Current
{nullptr};
3634 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3635 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
3636 for(auto &item
: source
->mQueue
)
3638 if(&item
== Current
)
3643 if(processed
< bids
.size())
3644 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffer%s (only %zu processed)",
3645 nb
, (nb
==1)?"":"s", processed
};
3647 std::generate(bids
.begin(), bids
.end(), [source
]() noexcept
-> ALuint
3649 auto &head
= source
->mQueue
.front();
3651 if(ALbuffer
*buffer
{head
.mBuffer
})
3654 DecrementRef(buffer
->ref
);
3656 source
->mQueue
.pop_front();
3660 catch(al::context_error
& e
) {
3661 context
->setError(e
.errorCode(), "%s", e
.what());
3665 AL_API
void AL_APIENTRY
alSourceQueueBufferLayersSOFT(ALuint
, ALsizei
, const ALuint
*) noexcept
3667 ContextRef context
{GetContextRef()};
3668 if(!context
) UNLIKELY
return;
3670 context
->setError(AL_INVALID_OPERATION
, "alSourceQueueBufferLayersSOFT not supported");
3674 ALsource::ALsource() noexcept
3677 Direct
.GainHF
= 1.0f
;
3678 Direct
.HFReference
= LowPassFreqRef
;
3679 Direct
.GainLF
= 1.0f
;
3680 Direct
.LFReference
= HighPassFreqRef
;
3681 for(auto &send
: Send
)
3683 send
.Slot
= nullptr;
3686 send
.HFReference
= LowPassFreqRef
;
3688 send
.LFReference
= HighPassFreqRef
;
3692 ALsource::~ALsource()
3694 for(auto &item
: mQueue
)
3696 if(ALbuffer
*buffer
{item
.mBuffer
})
3697 DecrementRef(buffer
->ref
);
3700 auto clear_send
= [](ALsource::SendData
&send
) -> void
3701 { if(send
.Slot
) DecrementRef(send
.Slot
->ref
); };
3702 std::for_each(Send
.begin(), Send
.end(), clear_send
);
3705 void UpdateAllSourceProps(ALCcontext
*context
)
3707 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3708 auto voicelist
= context
->getVoicesSpan();
3710 for(Voice
*voice
: voicelist
)
3712 ALuint sid
{voice
->mSourceID
.load(std::memory_order_acquire
)};
3713 ALsource
*source
{sid
? LookupSource(context
, sid
) : nullptr};
3714 if(source
&& source
->VoiceIdx
== vidx
)
3716 if(std::exchange(source
->mPropsDirty
, false))
3717 UpdateSourceProps(source
, voice
, context
);
3723 void ALsource::SetName(ALCcontext
*context
, ALuint id
, std::string_view name
)
3725 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3727 auto source
= LookupSource(context
, id
);
3729 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", id
};
3731 context
->mSourceNames
.insert_or_assign(id
, name
);
3735 SourceSubList::~SourceSubList()
3740 uint64_t usemask
{~FreeMask
};
3743 const int idx
{al::countr_zero(usemask
)};
3744 usemask
&= ~(1_u64
<< idx
);
3745 std::destroy_at(al::to_address(Sources
->begin() + idx
));
3747 FreeMask
= ~usemask
;
3748 SubListAllocator
{}.deallocate(Sources
, 1);
3754 void ALsource::eaxInitialize(ALCcontext
*context
) noexcept
3756 assert(context
!= nullptr);
3757 mEaxAlContext
= context
;
3759 mEaxPrimaryFxSlotId
= context
->eaxGetPrimaryFxSlotIndex();
3762 eax1_translate(mEax1
.i
, mEax
);
3767 void ALsource::eaxDispatch(const EaxCall
& call
)
3769 call
.is_get() ? eax_get(call
) : eax_set(call
);
3772 ALsource
* ALsource::EaxLookupSource(ALCcontext
& al_context
, ALuint source_id
) noexcept
3774 return LookupSource(&al_context
, source_id
);
3777 [[noreturn
]] void ALsource::eax_fail(const char* message
)
3779 throw Exception
{message
};
3782 [[noreturn
]] void ALsource::eax_fail_unknown_property_id()
3784 eax_fail("Unknown property id.");
3787 [[noreturn
]] void ALsource::eax_fail_unknown_version()
3789 eax_fail("Unknown version.");
3792 [[noreturn
]] void ALsource::eax_fail_unknown_active_fx_slot_id()
3794 eax_fail("Unknown active FX slot ID.");
3797 [[noreturn
]] void ALsource::eax_fail_unknown_receiving_fx_slot_id()
3799 eax_fail("Unknown receiving FX slot ID.");
3802 void ALsource::eax_set_sends_defaults(EaxSends
& sends
, const EaxFxSlotIds
& ids
) noexcept
3804 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
3806 auto& send
= sends
[i
];
3807 send
.guidReceivingFXSlotID
= *(ids
[i
]);
3808 send
.lSend
= EAXSOURCE_DEFAULTSEND
;
3809 send
.lSendHF
= EAXSOURCE_DEFAULTSENDHF
;
3810 send
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3811 send
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3812 send
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3813 send
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3814 send
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3815 send
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3819 void ALsource::eax1_set_defaults(Eax1Props
& props
) noexcept
3821 props
.fMix
= EAX_REVERBMIX_USEDISTANCE
;
3824 void ALsource::eax1_set_defaults() noexcept
3826 eax1_set_defaults(mEax1
.i
);
3830 void ALsource::eax2_set_defaults(Eax2Props
& props
) noexcept
3832 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3833 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3834 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3835 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
3836 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3837 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
3838 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
3839 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3840 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3841 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3842 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3843 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3844 props
.dwFlags
= EAXSOURCE_DEFAULTFLAGS
;
3847 void ALsource::eax2_set_defaults() noexcept
3849 eax2_set_defaults(mEax2
.i
);
3853 void ALsource::eax3_set_defaults(Eax3Props
& props
) noexcept
3855 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3856 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3857 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3858 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
3859 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
3860 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
3861 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3862 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3863 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3864 props
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3865 props
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3866 props
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3867 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3868 props
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3869 props
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3870 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3871 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3872 props
.ulFlags
= EAXSOURCE_DEFAULTFLAGS
;
3875 void ALsource::eax3_set_defaults() noexcept
3877 eax3_set_defaults(mEax3
.i
);
3881 void ALsource::eax4_set_sends_defaults(EaxSends
& sends
) noexcept
3883 eax_set_sends_defaults(sends
, eax4_fx_slot_ids
);
3886 void ALsource::eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS
& slots
) noexcept
3888 slots
= EAX40SOURCE_DEFAULTACTIVEFXSLOTID
;
3891 void ALsource::eax4_set_defaults() noexcept
3893 eax3_set_defaults(mEax4
.i
.source
);
3894 eax4_set_sends_defaults(mEax4
.i
.sends
);
3895 eax4_set_active_fx_slots_defaults(mEax4
.i
.active_fx_slots
);
3899 void ALsource::eax5_set_source_defaults(EAX50SOURCEPROPERTIES
& props
) noexcept
3901 eax3_set_defaults(static_cast<Eax3Props
&>(props
));
3902 props
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3905 void ALsource::eax5_set_sends_defaults(EaxSends
& sends
) noexcept
3907 eax_set_sends_defaults(sends
, eax5_fx_slot_ids
);
3910 void ALsource::eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS
& slots
) noexcept
3912 slots
= EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID
;
3915 void ALsource::eax5_set_speaker_levels_defaults(EaxSpeakerLevels
& speaker_levels
) noexcept
3917 for(size_t i
{0};i
< eax_max_speakers
;++i
)
3919 auto& speaker_level
= speaker_levels
[i
];
3920 speaker_level
.lSpeakerID
= static_cast<long>(EAXSPEAKER_FRONT_LEFT
+ i
);
3921 speaker_level
.lLevel
= EAXSOURCE_DEFAULTSPEAKERLEVEL
;
3925 void ALsource::eax5_set_defaults(Eax5Props
& props
) noexcept
3927 eax5_set_source_defaults(props
.source
);
3928 eax5_set_sends_defaults(props
.sends
);
3929 eax5_set_active_fx_slots_defaults(props
.active_fx_slots
);
3930 eax5_set_speaker_levels_defaults(props
.speaker_levels
);
3933 void ALsource::eax5_set_defaults() noexcept
3935 eax5_set_defaults(mEax5
.i
);
3939 void ALsource::eax_set_defaults() noexcept
3941 eax1_set_defaults();
3942 eax2_set_defaults();
3943 eax3_set_defaults();
3944 eax4_set_defaults();
3945 eax5_set_defaults();
3948 void ALsource::eax1_translate(const Eax1Props
& src
, Eax5Props
& dst
) noexcept
3950 eax5_set_defaults(dst
);
3952 if (src
.fMix
== EAX_REVERBMIX_USEDISTANCE
)
3954 dst
.source
.ulFlags
|= EAXSOURCEFLAGS_ROOMAUTO
;
3955 dst
.sends
[0].lSend
= 0;
3959 dst
.source
.ulFlags
&= ~EAXSOURCEFLAGS_ROOMAUTO
;
3960 dst
.sends
[0].lSend
= std::clamp(static_cast<long>(gain_to_level_mb(src
.fMix
)),
3961 EAXSOURCE_MINSEND
, EAXSOURCE_MAXSEND
);
3965 void ALsource::eax2_translate(const Eax2Props
& src
, Eax5Props
& dst
) noexcept
3969 dst
.source
.lDirect
= src
.lDirect
;
3970 dst
.source
.lDirectHF
= src
.lDirectHF
;
3971 dst
.source
.lRoom
= src
.lRoom
;
3972 dst
.source
.lRoomHF
= src
.lRoomHF
;
3973 dst
.source
.lObstruction
= src
.lObstruction
;
3974 dst
.source
.flObstructionLFRatio
= src
.flObstructionLFRatio
;
3975 dst
.source
.lOcclusion
= src
.lOcclusion
;
3976 dst
.source
.flOcclusionLFRatio
= src
.flOcclusionLFRatio
;
3977 dst
.source
.flOcclusionRoomRatio
= src
.flOcclusionRoomRatio
;
3978 dst
.source
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3979 dst
.source
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3980 dst
.source
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3981 dst
.source
.lOutsideVolumeHF
= src
.lOutsideVolumeHF
;
3982 dst
.source
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3983 dst
.source
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3984 dst
.source
.flRoomRolloffFactor
= src
.flRoomRolloffFactor
;
3985 dst
.source
.flAirAbsorptionFactor
= src
.flAirAbsorptionFactor
;
3986 dst
.source
.ulFlags
= src
.dwFlags
;
3987 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3989 // Set everything else to defaults.
3991 eax5_set_sends_defaults(dst
.sends
);
3992 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
3993 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
3996 void ALsource::eax3_translate(const Eax3Props
& src
, Eax5Props
& dst
) noexcept
4000 static_cast<Eax3Props
&>(dst
.source
) = src
;
4001 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4003 // Set everything else to defaults.
4005 eax5_set_sends_defaults(dst
.sends
);
4006 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
4007 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4010 void ALsource::eax4_translate(const Eax4Props
& src
, Eax5Props
& dst
) noexcept
4014 static_cast<Eax3Props
&>(dst
.source
) = src
.source
;
4015 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4019 dst
.sends
= src
.sends
;
4021 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4022 dst
.sends
[i
].guidReceivingFXSlotID
= *(eax5_fx_slot_ids
[i
]);
4026 auto translate_slotid
= [](const GUID
&src_id
) -> GUID
4028 if(src_id
== EAX_NULL_GUID
)
4029 return EAX_NULL_GUID
;
4030 if(src_id
== EAX_PrimaryFXSlotID
)
4031 return EAX_PrimaryFXSlotID
;
4032 if(src_id
== EAXPROPERTYID_EAX40_FXSlot0
)
4033 return EAXPROPERTYID_EAX50_FXSlot0
;
4034 if(src_id
== EAXPROPERTYID_EAX40_FXSlot1
)
4035 return EAXPROPERTYID_EAX50_FXSlot1
;
4036 if(src_id
== EAXPROPERTYID_EAX40_FXSlot2
)
4037 return EAXPROPERTYID_EAX50_FXSlot2
;
4038 if(src_id
== EAXPROPERTYID_EAX40_FXSlot3
)
4039 return EAXPROPERTYID_EAX50_FXSlot3
;
4042 ERR("Unexpected active FX slot ID\n");
4043 return EAX_NULL_GUID
;
4045 const auto src_slots
= al::span
{src
.active_fx_slots
.guidActiveFXSlots
};
4046 const auto dst_slots
= al::span
{dst
.active_fx_slots
.guidActiveFXSlots
};
4047 auto dstiter
= std::transform(src_slots
.cbegin(), src_slots
.cend(), dst_slots
.begin(),
4049 std::fill(dstiter
, dst_slots
.end(), EAX_NULL_GUID
);
4053 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4056 float ALsource::eax_calculate_dst_occlusion_mb(
4057 long src_occlusion_mb
,
4059 float lf_ratio
) noexcept
4061 const auto ratio_1
= path_ratio
+ lf_ratio
- 1.0F
;
4062 const auto ratio_2
= path_ratio
* lf_ratio
;
4063 const auto ratio
= (ratio_2
> ratio_1
) ? ratio_2
: ratio_1
;
4064 const auto dst_occlustion_mb
= static_cast<float>(src_occlusion_mb
) * ratio
;
4065 return dst_occlustion_mb
;
4068 EaxAlLowPassParam
ALsource::eax_create_direct_filter_param() const noexcept
4070 const auto &source
= mEax
.source
;
4072 auto gain_mb
= static_cast<float>(source
.lObstruction
) * source
.flObstructionLFRatio
;
4073 auto gainhf_mb
= static_cast<float>(source
.lObstruction
);
4075 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4077 if(!mEaxActiveFxSlots
[i
])
4080 if(source
.lOcclusion
!= 0)
4082 const auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4083 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4084 const auto is_environmental_fx
= ((fx_slot_eax
.ulFlags
&EAXFXSLOTFLAGS_ENVIRONMENT
) != 0);
4085 const auto is_primary
= (mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index());
4087 if(is_environmental_fx
&& is_primary
)
4089 gain_mb
+= eax_calculate_dst_occlusion_mb(source
.lOcclusion
,
4090 source
.flOcclusionDirectRatio
, source
.flOcclusionLFRatio
);
4092 gainhf_mb
+= static_cast<float>(source
.lOcclusion
) * source
.flOcclusionDirectRatio
;
4096 const auto& send
= mEax
.sends
[i
];
4097 if(send
.lOcclusion
!= 0)
4099 gain_mb
+= eax_calculate_dst_occlusion_mb(send
.lOcclusion
, send
.flOcclusionDirectRatio
,
4100 send
.flOcclusionLFRatio
);
4102 gainhf_mb
+= static_cast<float>(send
.lOcclusion
) * send
.flOcclusionDirectRatio
;
4106 /* gainhf_mb is the absolute mBFS of the filter's high-frequency volume,
4107 * and gain_mb is the absolute mBFS of the filter's low-frequency volume.
4108 * Adjust the HF volume to be relative to the LF volume, to make the
4109 * appropriate main and relative HF filter volumes.
4111 * Also add the Direct and DirectHF properties to the filter, which are
4112 * already the main and relative HF volumes.
4114 gainhf_mb
-= gain_mb
- static_cast<float>(source
.lDirectHF
);
4115 gain_mb
+= static_cast<float>(source
.lDirect
);
4117 return EaxAlLowPassParam
{level_mb_to_gain(gain_mb
),
4118 std::min(level_mb_to_gain(gainhf_mb
), 1.0f
)};
4121 EaxAlLowPassParam
ALsource::eax_create_room_filter_param(
4122 const ALeffectslot
& fx_slot
,
4123 const EAXSOURCEALLSENDPROPERTIES
& send
) const noexcept
4125 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4126 const auto is_environmental_fx
= bool{(fx_slot_eax
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0};
4127 const auto is_primary
= bool{mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index()};
4129 auto gain_mb
= (static_cast<float>(fx_slot_eax
.lOcclusion
) * fx_slot_eax
.flOcclusionLFRatio
)
4130 + eax_calculate_dst_occlusion_mb(send
.lOcclusion
, send
.flOcclusionRoomRatio
,
4131 send
.flOcclusionLFRatio
)
4132 + (static_cast<float>(send
.lExclusion
) * send
.flExclusionLFRatio
);
4134 auto gainhf_mb
= static_cast<float>(fx_slot_eax
.lOcclusion
)
4135 + (static_cast<float>(send
.lOcclusion
) * send
.flOcclusionRoomRatio
);
4137 if(is_environmental_fx
&& is_primary
)
4139 const auto &source
= mEax
.source
;
4141 gain_mb
+= eax_calculate_dst_occlusion_mb(source
.lOcclusion
, source
.flOcclusionRoomRatio
,
4142 source
.flOcclusionLFRatio
);
4143 gain_mb
+= static_cast<float>(source
.lExclusion
) * source
.flExclusionLFRatio
;
4145 gainhf_mb
+= static_cast<float>(source
.lOcclusion
) * source
.flOcclusionRoomRatio
;
4146 gainhf_mb
+= static_cast<float>(source
.lExclusion
+ send
.lExclusion
);
4149 gainhf_mb
-= gain_mb
- static_cast<float>(send
.lSendHF
);
4150 gain_mb
+= static_cast<float>(send
.lSend
);
4151 if(is_environmental_fx
)
4153 const auto &source
= mEax
.source
;
4154 gain_mb
+= static_cast<float>(source
.lRoom
);
4155 gainhf_mb
+= static_cast<float>(source
.lRoomHF
);
4158 return EaxAlLowPassParam
{level_mb_to_gain(gain_mb
),
4159 std::min(level_mb_to_gain(gainhf_mb
), 1.0f
)};
4162 void ALsource::eax_update_direct_filter()
4164 const auto& direct_param
= eax_create_direct_filter_param();
4165 Direct
.Gain
= direct_param
.gain
;
4166 Direct
.GainHF
= direct_param
.gain_hf
;
4167 Direct
.HFReference
= LowPassFreqRef
;
4168 Direct
.GainLF
= 1.0f
;
4169 Direct
.LFReference
= HighPassFreqRef
;
4173 void ALsource::eax_update_room_filters()
4175 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4177 if(!mEaxActiveFxSlots
[i
])
4180 auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4181 const auto& send
= mEax
.sends
[i
];
4182 const auto& room_param
= eax_create_room_filter_param(fx_slot
, send
);
4183 eax_set_al_source_send(&fx_slot
, i
, room_param
);
4187 void ALsource::eax_set_efx_outer_gain_hf()
4189 OuterGainHF
= std::clamp(
4190 level_mb_to_gain(static_cast<float>(mEax
.source
.lOutsideVolumeHF
)),
4191 AL_MIN_CONE_OUTER_GAINHF
,
4192 AL_MAX_CONE_OUTER_GAINHF
);
4195 void ALsource::eax_set_efx_doppler_factor()
4197 DopplerFactor
= mEax
.source
.flDopplerFactor
;
4200 void ALsource::eax_set_efx_rolloff_factor()
4202 RolloffFactor2
= mEax
.source
.flRolloffFactor
;
4205 void ALsource::eax_set_efx_room_rolloff_factor()
4207 RoomRolloffFactor
= mEax
.source
.flRoomRolloffFactor
;
4210 void ALsource::eax_set_efx_air_absorption_factor()
4212 AirAbsorptionFactor
= mEax
.source
.flAirAbsorptionFactor
;
4215 void ALsource::eax_set_efx_dry_gain_hf_auto()
4217 DryGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_DIRECTHFAUTO
) != 0);
4220 void ALsource::eax_set_efx_wet_gain_auto()
4222 WetGainAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMAUTO
) != 0);
4225 void ALsource::eax_set_efx_wet_gain_hf_auto()
4227 WetGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMHFAUTO
) != 0);
4230 void ALsource::eax1_set(const EaxCall
& call
, Eax1Props
& props
)
4232 switch (call
.get_property_id()) {
4233 case DSPROPERTY_EAXBUFFER_ALL
:
4234 eax_defer
<Eax1SourceAllValidator
>(call
, props
);
4237 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4238 eax_defer
<Eax1SourceReverbMixValidator
>(call
, props
.fMix
);
4242 eax_fail_unknown_property_id();
4246 void ALsource::eax2_set(const EaxCall
& call
, Eax2Props
& props
)
4248 switch (call
.get_property_id()) {
4249 case DSPROPERTY_EAX20BUFFER_NONE
:
4252 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4253 eax_defer
<Eax2SourceAllValidator
>(call
, props
);
4256 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4257 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4260 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4261 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4264 case DSPROPERTY_EAX20BUFFER_ROOM
:
4265 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4268 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4269 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4272 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4273 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4276 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4277 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4280 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4281 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4284 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4285 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4288 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4289 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4292 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4293 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4296 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4297 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4300 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4301 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4304 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4305 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.dwFlags
);
4309 eax_fail_unknown_property_id();
4313 void ALsource::eax3_set(const EaxCall
& call
, Eax3Props
& props
)
4315 switch (call
.get_property_id()) {
4316 case EAXSOURCE_NONE
:
4319 case EAXSOURCE_ALLPARAMETERS
:
4320 eax_defer
<Eax3SourceAllValidator
>(call
, props
);
4323 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4324 eax_defer_sub
<Eax4ObstructionValidator
, EAXOBSTRUCTIONPROPERTIES
>(call
, props
.lObstruction
);
4327 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4328 eax_defer_sub
<Eax4OcclusionValidator
, EAXOCCLUSIONPROPERTIES
>(call
, props
.lOcclusion
);
4331 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4332 eax_defer_sub
<Eax4ExclusionValidator
, EAXEXCLUSIONPROPERTIES
>(call
, props
.lExclusion
);
4335 case EAXSOURCE_DIRECT
:
4336 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4339 case EAXSOURCE_DIRECTHF
:
4340 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4343 case EAXSOURCE_ROOM
:
4344 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4347 case EAXSOURCE_ROOMHF
:
4348 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4351 case EAXSOURCE_OBSTRUCTION
:
4352 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4355 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4356 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4359 case EAXSOURCE_OCCLUSION
:
4360 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4363 case EAXSOURCE_OCCLUSIONLFRATIO
:
4364 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4367 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4368 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4371 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4372 eax_defer
<Eax3SourceOcclusionDirectRatioValidator
>(call
, props
.flOcclusionDirectRatio
);
4375 case EAXSOURCE_EXCLUSION
:
4376 eax_defer
<Eax3SourceExclusionValidator
>(call
, props
.lExclusion
);
4379 case EAXSOURCE_EXCLUSIONLFRATIO
:
4380 eax_defer
<Eax3SourceExclusionLfRatioValidator
>(call
, props
.flExclusionLFRatio
);
4383 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4384 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4387 case EAXSOURCE_DOPPLERFACTOR
:
4388 eax_defer
<Eax3SourceDopplerFactorValidator
>(call
, props
.flDopplerFactor
);
4391 case EAXSOURCE_ROLLOFFFACTOR
:
4392 eax_defer
<Eax3SourceRolloffFactorValidator
>(call
, props
.flRolloffFactor
);
4395 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4396 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4399 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4400 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4403 case EAXSOURCE_FLAGS
:
4404 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.ulFlags
);
4408 eax_fail_unknown_property_id();
4412 void ALsource::eax4_set(const EaxCall
& call
, Eax4Props
& props
)
4414 switch (call
.get_property_id()) {
4415 case EAXSOURCE_NONE
:
4416 case EAXSOURCE_ALLPARAMETERS
:
4417 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4418 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4419 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4420 case EAXSOURCE_DIRECT
:
4421 case EAXSOURCE_DIRECTHF
:
4422 case EAXSOURCE_ROOM
:
4423 case EAXSOURCE_ROOMHF
:
4424 case EAXSOURCE_OBSTRUCTION
:
4425 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4426 case EAXSOURCE_OCCLUSION
:
4427 case EAXSOURCE_OCCLUSIONLFRATIO
:
4428 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4429 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4430 case EAXSOURCE_EXCLUSION
:
4431 case EAXSOURCE_EXCLUSIONLFRATIO
:
4432 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4433 case EAXSOURCE_DOPPLERFACTOR
:
4434 case EAXSOURCE_ROLLOFFFACTOR
:
4435 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4436 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4437 case EAXSOURCE_FLAGS
:
4438 eax3_set(call
, props
.source
);
4441 case EAXSOURCE_SENDPARAMETERS
:
4442 eax4_defer_sends
<Eax4SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4445 case EAXSOURCE_ALLSENDPARAMETERS
:
4446 eax4_defer_sends
<Eax4AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4449 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4450 eax4_defer_sends
<Eax4OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4453 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4454 eax4_defer_sends
<Eax4ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4457 case EAXSOURCE_ACTIVEFXSLOTID
:
4458 eax4_defer_active_fx_slot_id(call
, al::span
{props
.active_fx_slots
.guidActiveFXSlots
});
4462 eax_fail_unknown_property_id();
4466 void ALsource::eax5_defer_all_2d(const EaxCall
& call
, EAX50SOURCEPROPERTIES
& props
)
4468 const auto& src_props
= call
.get_value
<Exception
, const EAXSOURCE2DPROPERTIES
>();
4469 Eax5SourceAll2dValidator
{}(src_props
);
4470 props
.lDirect
= src_props
.lDirect
;
4471 props
.lDirectHF
= src_props
.lDirectHF
;
4472 props
.lRoom
= src_props
.lRoom
;
4473 props
.lRoomHF
= src_props
.lRoomHF
;
4474 props
.ulFlags
= src_props
.ulFlags
;
4477 void ALsource::eax5_defer_speaker_levels(const EaxCall
& call
, EaxSpeakerLevels
& props
)
4479 const auto values
= call
.get_values
<const EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4480 std::for_each(values
.cbegin(), values
.cend(), Eax5SpeakerAllValidator
{});
4482 for (const auto& value
: values
) {
4483 const auto index
= static_cast<size_t>(value
.lSpeakerID
- EAXSPEAKER_FRONT_LEFT
);
4484 props
[index
].lLevel
= value
.lLevel
;
4488 void ALsource::eax5_set(const EaxCall
& call
, Eax5Props
& props
)
4490 switch (call
.get_property_id()) {
4491 case EAXSOURCE_NONE
:
4494 case EAXSOURCE_ALLPARAMETERS
:
4495 eax_defer
<Eax5SourceAllValidator
>(call
, props
.source
);
4498 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4499 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4500 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4501 case EAXSOURCE_DIRECT
:
4502 case EAXSOURCE_DIRECTHF
:
4503 case EAXSOURCE_ROOM
:
4504 case EAXSOURCE_ROOMHF
:
4505 case EAXSOURCE_OBSTRUCTION
:
4506 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4507 case EAXSOURCE_OCCLUSION
:
4508 case EAXSOURCE_OCCLUSIONLFRATIO
:
4509 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4510 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4511 case EAXSOURCE_EXCLUSION
:
4512 case EAXSOURCE_EXCLUSIONLFRATIO
:
4513 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4514 case EAXSOURCE_DOPPLERFACTOR
:
4515 case EAXSOURCE_ROLLOFFFACTOR
:
4516 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4517 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4518 eax3_set(call
, props
.source
);
4521 case EAXSOURCE_FLAGS
:
4522 eax_defer
<Eax5SourceFlagsValidator
>(call
, props
.source
.ulFlags
);
4525 case EAXSOURCE_SENDPARAMETERS
:
4526 eax5_defer_sends
<Eax5SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4529 case EAXSOURCE_ALLSENDPARAMETERS
:
4530 eax5_defer_sends
<Eax5AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4533 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4534 eax5_defer_sends
<Eax5OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4537 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4538 eax5_defer_sends
<Eax5ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4541 case EAXSOURCE_ACTIVEFXSLOTID
:
4542 eax5_defer_active_fx_slot_id(call
, al::span
{props
.active_fx_slots
.guidActiveFXSlots
});
4545 case EAXSOURCE_MACROFXFACTOR
:
4546 eax_defer
<Eax5SourceMacroFXFactorValidator
>(call
, props
.source
.flMacroFXFactor
);
4549 case EAXSOURCE_SPEAKERLEVELS
:
4550 eax5_defer_speaker_levels(call
, props
.speaker_levels
);
4553 case EAXSOURCE_ALL2DPARAMETERS
:
4554 eax5_defer_all_2d(call
, props
.source
);
4558 eax_fail_unknown_property_id();
4562 void ALsource::eax_set(const EaxCall
& call
)
4564 const auto eax_version
= call
.get_version();
4567 case 1: eax1_set(call
, mEax1
.d
); break;
4568 case 2: eax2_set(call
, mEax2
.d
); break;
4569 case 3: eax3_set(call
, mEax3
.d
); break;
4570 case 4: eax4_set(call
, mEax4
.d
); break;
4571 case 5: eax5_set(call
, mEax5
.d
); break;
4572 default: eax_fail_unknown_property_id();
4575 mEaxVersion
= eax_version
;
4578 void ALsource::eax_get_active_fx_slot_id(const EaxCall
& call
, const al::span
<const GUID
> src_ids
)
4580 assert(src_ids
.size()==EAX40_MAX_ACTIVE_FXSLOTS
|| src_ids
.size()==EAX50_MAX_ACTIVE_FXSLOTS
);
4581 const auto dst_ids
= call
.get_values
<GUID
>(src_ids
.size());
4582 std::uninitialized_copy_n(src_ids
.begin(), dst_ids
.size(), dst_ids
.begin());
4585 void ALsource::eax1_get(const EaxCall
& call
, const Eax1Props
& props
)
4587 switch (call
.get_property_id()) {
4588 case DSPROPERTY_EAXBUFFER_ALL
:
4589 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4590 call
.set_value
<Exception
>(props
.fMix
);
4594 eax_fail_unknown_property_id();
4598 void ALsource::eax2_get(const EaxCall
& call
, const Eax2Props
& props
)
4600 switch (call
.get_property_id()) {
4601 case DSPROPERTY_EAX20BUFFER_NONE
:
4604 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4605 call
.set_value
<Exception
>(props
);
4608 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4609 call
.set_value
<Exception
>(props
.lDirect
);
4612 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4613 call
.set_value
<Exception
>(props
.lDirectHF
);
4616 case DSPROPERTY_EAX20BUFFER_ROOM
:
4617 call
.set_value
<Exception
>(props
.lRoom
);
4620 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4621 call
.set_value
<Exception
>(props
.lRoomHF
);
4624 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4625 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4628 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4629 call
.set_value
<Exception
>(props
.lObstruction
);
4632 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4633 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4636 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4637 call
.set_value
<Exception
>(props
.lOcclusion
);
4640 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4641 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4644 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4645 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4648 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4649 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4652 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4653 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4656 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4657 call
.set_value
<Exception
>(props
.dwFlags
);
4661 eax_fail_unknown_property_id();
4665 void ALsource::eax3_get_obstruction(const EaxCall
& call
, const Eax3Props
& props
)
4667 const auto& subprops
= reinterpret_cast<const EAXOBSTRUCTIONPROPERTIES
&>(props
.lObstruction
);
4668 call
.set_value
<Exception
>(subprops
);
4671 void ALsource::eax3_get_occlusion(const EaxCall
& call
, const Eax3Props
& props
)
4673 const auto& subprops
= reinterpret_cast<const EAXOCCLUSIONPROPERTIES
&>(props
.lOcclusion
);
4674 call
.set_value
<Exception
>(subprops
);
4677 void ALsource::eax3_get_exclusion(const EaxCall
& call
, const Eax3Props
& props
)
4679 const auto& subprops
= reinterpret_cast<const EAXEXCLUSIONPROPERTIES
&>(props
.lExclusion
);
4680 call
.set_value
<Exception
>(subprops
);
4683 void ALsource::eax3_get(const EaxCall
& call
, const Eax3Props
& props
)
4685 switch (call
.get_property_id()) {
4686 case EAXSOURCE_NONE
:
4689 case EAXSOURCE_ALLPARAMETERS
:
4690 call
.set_value
<Exception
>(props
);
4693 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4694 eax3_get_obstruction(call
, props
);
4697 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4698 eax3_get_occlusion(call
, props
);
4701 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4702 eax3_get_exclusion(call
, props
);
4705 case EAXSOURCE_DIRECT
:
4706 call
.set_value
<Exception
>(props
.lDirect
);
4709 case EAXSOURCE_DIRECTHF
:
4710 call
.set_value
<Exception
>(props
.lDirectHF
);
4713 case EAXSOURCE_ROOM
:
4714 call
.set_value
<Exception
>(props
.lRoom
);
4717 case EAXSOURCE_ROOMHF
:
4718 call
.set_value
<Exception
>(props
.lRoomHF
);
4721 case EAXSOURCE_OBSTRUCTION
:
4722 call
.set_value
<Exception
>(props
.lObstruction
);
4725 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4726 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4729 case EAXSOURCE_OCCLUSION
:
4730 call
.set_value
<Exception
>(props
.lOcclusion
);
4733 case EAXSOURCE_OCCLUSIONLFRATIO
:
4734 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4737 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4738 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4741 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4742 call
.set_value
<Exception
>(props
.flOcclusionDirectRatio
);
4745 case EAXSOURCE_EXCLUSION
:
4746 call
.set_value
<Exception
>(props
.lExclusion
);
4749 case EAXSOURCE_EXCLUSIONLFRATIO
:
4750 call
.set_value
<Exception
>(props
.flExclusionLFRatio
);
4753 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4754 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4757 case EAXSOURCE_DOPPLERFACTOR
:
4758 call
.set_value
<Exception
>(props
.flDopplerFactor
);
4761 case EAXSOURCE_ROLLOFFFACTOR
:
4762 call
.set_value
<Exception
>(props
.flRolloffFactor
);
4765 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4766 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4769 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4770 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4773 case EAXSOURCE_FLAGS
:
4774 call
.set_value
<Exception
>(props
.ulFlags
);
4778 eax_fail_unknown_property_id();
4782 void ALsource::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
4784 switch (call
.get_property_id()) {
4785 case EAXSOURCE_NONE
:
4788 case EAXSOURCE_ALLPARAMETERS
:
4789 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4790 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4791 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4792 case EAXSOURCE_DIRECT
:
4793 case EAXSOURCE_DIRECTHF
:
4794 case EAXSOURCE_ROOM
:
4795 case EAXSOURCE_ROOMHF
:
4796 case EAXSOURCE_OBSTRUCTION
:
4797 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4798 case EAXSOURCE_OCCLUSION
:
4799 case EAXSOURCE_OCCLUSIONLFRATIO
:
4800 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4801 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4802 case EAXSOURCE_EXCLUSION
:
4803 case EAXSOURCE_EXCLUSIONLFRATIO
:
4804 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4805 case EAXSOURCE_DOPPLERFACTOR
:
4806 case EAXSOURCE_ROLLOFFFACTOR
:
4807 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4808 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4809 case EAXSOURCE_FLAGS
:
4810 eax3_get(call
, props
.source
);
4813 case EAXSOURCE_SENDPARAMETERS
:
4814 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4817 case EAXSOURCE_ALLSENDPARAMETERS
:
4818 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4821 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4822 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4825 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4826 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4829 case EAXSOURCE_ACTIVEFXSLOTID
:
4830 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4834 eax_fail_unknown_property_id();
4838 void ALsource::eax5_get_all_2d(const EaxCall
& call
, const EAX50SOURCEPROPERTIES
& props
)
4840 auto& subprops
= call
.get_value
<Exception
, EAXSOURCE2DPROPERTIES
>();
4841 subprops
.lDirect
= props
.lDirect
;
4842 subprops
.lDirectHF
= props
.lDirectHF
;
4843 subprops
.lRoom
= props
.lRoom
;
4844 subprops
.lRoomHF
= props
.lRoomHF
;
4845 subprops
.ulFlags
= props
.ulFlags
;
4848 void ALsource::eax5_get_speaker_levels(const EaxCall
& call
, const EaxSpeakerLevels
& props
)
4850 const auto subprops
= call
.get_values
<EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4851 std::uninitialized_copy_n(props
.cbegin(), subprops
.size(), subprops
.begin());
4854 void ALsource::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
4856 switch (call
.get_property_id()) {
4857 case EAXSOURCE_NONE
:
4860 case EAXSOURCE_ALLPARAMETERS
:
4861 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4862 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4863 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4864 case EAXSOURCE_DIRECT
:
4865 case EAXSOURCE_DIRECTHF
:
4866 case EAXSOURCE_ROOM
:
4867 case EAXSOURCE_ROOMHF
:
4868 case EAXSOURCE_OBSTRUCTION
:
4869 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4870 case EAXSOURCE_OCCLUSION
:
4871 case EAXSOURCE_OCCLUSIONLFRATIO
:
4872 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4873 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4874 case EAXSOURCE_EXCLUSION
:
4875 case EAXSOURCE_EXCLUSIONLFRATIO
:
4876 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4877 case EAXSOURCE_DOPPLERFACTOR
:
4878 case EAXSOURCE_ROLLOFFFACTOR
:
4879 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4880 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4881 case EAXSOURCE_FLAGS
:
4882 eax3_get(call
, props
.source
);
4885 case EAXSOURCE_SENDPARAMETERS
:
4886 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4889 case EAXSOURCE_ALLSENDPARAMETERS
:
4890 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4893 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4894 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4897 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4898 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4901 case EAXSOURCE_ACTIVEFXSLOTID
:
4902 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4905 case EAXSOURCE_MACROFXFACTOR
:
4906 call
.set_value
<Exception
>(props
.source
.flMacroFXFactor
);
4909 case EAXSOURCE_SPEAKERLEVELS
:
4910 call
.set_value
<Exception
>(props
.speaker_levels
);
4913 case EAXSOURCE_ALL2DPARAMETERS
:
4914 eax5_get_all_2d(call
, props
.source
);
4918 eax_fail_unknown_property_id();
4922 void ALsource::eax_get(const EaxCall
& call
)
4924 switch (call
.get_version()) {
4925 case 1: eax1_get(call
, mEax1
.i
); break;
4926 case 2: eax2_get(call
, mEax2
.i
); break;
4927 case 3: eax3_get(call
, mEax3
.i
); break;
4928 case 4: eax4_get(call
, mEax4
.i
); break;
4929 case 5: eax5_get(call
, mEax5
.i
); break;
4930 default: eax_fail_unknown_version();
4934 void ALsource::eax_set_al_source_send(ALeffectslot
*slot
, size_t sendidx
, const EaxAlLowPassParam
&filter
)
4936 if(sendidx
>= EAX_MAX_FXSLOTS
)
4939 auto &send
= Send
[sendidx
];
4940 send
.Gain
= filter
.gain
;
4941 send
.GainHF
= filter
.gain_hf
;
4942 send
.HFReference
= LowPassFreqRef
;
4944 send
.LFReference
= HighPassFreqRef
;
4947 IncrementRef(slot
->ref
);
4948 if(auto *oldslot
= send
.Slot
)
4949 DecrementRef(oldslot
->ref
);
4955 void ALsource::eax_commit_active_fx_slots()
4957 // Clear all slots to an inactive state.
4958 mEaxActiveFxSlots
.fill(false);
4960 // Mark the set slots as active.
4961 for(const auto& slot_id
: mEax
.active_fx_slots
.guidActiveFXSlots
)
4963 if(slot_id
== EAX_NULL_GUID
)
4966 else if(slot_id
== EAX_PrimaryFXSlotID
)
4968 // Mark primary FX slot as active.
4969 if(mEaxPrimaryFxSlotId
.has_value())
4970 mEaxActiveFxSlots
[*mEaxPrimaryFxSlotId
] = true;
4972 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot0
)
4973 mEaxActiveFxSlots
[0] = true;
4974 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot1
)
4975 mEaxActiveFxSlots
[1] = true;
4976 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot2
)
4977 mEaxActiveFxSlots
[2] = true;
4978 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot3
)
4979 mEaxActiveFxSlots
[3] = true;
4982 // Deactivate EFX auxiliary effect slots for inactive slots. Active slots
4983 // will be updated with the room filters.
4984 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4986 if(!mEaxActiveFxSlots
[i
])
4987 eax_set_al_source_send(nullptr, i
, EaxAlLowPassParam
{1.0f
, 1.0f
});
4991 void ALsource::eax_commit_filters()
4993 eax_update_direct_filter();
4994 eax_update_room_filters();
4997 void ALsource::eaxCommit()
4999 const auto primary_fx_slot_id
= mEaxAlContext
->eaxGetPrimaryFxSlotIndex();
5000 const auto is_primary_fx_slot_id_changed
= (mEaxPrimaryFxSlotId
!= primary_fx_slot_id
);
5002 if(!mEaxChanged
&& !is_primary_fx_slot_id_changed
)
5005 mEaxPrimaryFxSlotId
= primary_fx_slot_id
;
5006 mEaxChanged
= false;
5012 eax1_translate(mEax1
.i
, mEax
);
5016 eax2_translate(mEax2
.i
, mEax
);
5020 eax3_translate(mEax3
.i
, mEax
);
5024 eax4_translate(mEax4
.i
, mEax
);
5032 eax_set_efx_outer_gain_hf();
5033 eax_set_efx_doppler_factor();
5034 eax_set_efx_rolloff_factor();
5035 eax_set_efx_room_rolloff_factor();
5036 eax_set_efx_air_absorption_factor();
5037 eax_set_efx_dry_gain_hf_auto();
5038 eax_set_efx_wet_gain_auto();
5039 eax_set_efx_wet_gain_hf_auto();
5041 eax_commit_active_fx_slots();
5042 eax_commit_filters();
5045 #endif // ALSOFT_EAX