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
46 #include <type_traits>
47 #include <unordered_map>
57 #include "alc/backends/base.h"
58 #include "alc/context.h"
59 #include "alc/device.h"
60 #include "alc/inprogext.h"
62 #include "alnumeric.h"
65 #include "auxeffectslot.h"
67 #include "core/buffer_storage.h"
68 #include "core/logging.h"
69 #include "core/mixer/defs.h"
70 #include "core/voice_change.h"
71 #include "direct_defs.h"
74 #include "flexarray.h"
75 #include "intrusive_ptr.h"
76 #include "opthelpers.h"
81 #include "eax/fx_slot_index.h"
82 #include "eax/utils.h"
87 using SubListAllocator
= al::allocator
<std::array
<ALsource
,64>>;
88 using std::chrono::nanoseconds
;
89 using seconds_d
= std::chrono::duration
<double>;
90 using source_store_array
= std::array
<ALsource
*,3>;
91 using source_store_vector
= std::vector
<ALsource
*>;
92 using source_store_variant
= std::variant
<std::monostate
,source_store_array
,source_store_vector
>;
95 Voice
*GetSourceVoice(ALsource
*source
, ALCcontext
*context
)
97 auto voicelist
= context
->getVoicesSpan();
98 ALuint idx
{source
->VoiceIdx
};
99 if(idx
< voicelist
.size())
101 ALuint sid
{source
->id
};
102 Voice
*voice
= voicelist
[idx
];
103 if(voice
->mSourceID
.load(std::memory_order_acquire
) == sid
)
106 source
->VoiceIdx
= InvalidVoiceIndex
;
111 void UpdateSourceProps(const ALsource
*source
, Voice
*voice
, ALCcontext
*context
)
113 /* Get an unused property container, or allocate a new one as needed. */
114 VoicePropsItem
*props
{context
->mFreeVoiceProps
.load(std::memory_order_acquire
)};
117 context
->allocVoiceProps();
118 props
= context
->mFreeVoiceProps
.load(std::memory_order_acquire
);
120 VoicePropsItem
*next
;
122 next
= props
->next
.load(std::memory_order_relaxed
);
123 } while(context
->mFreeVoiceProps
.compare_exchange_weak(props
, next
,
124 std::memory_order_acq_rel
, std::memory_order_acquire
) == false);
126 props
->Pitch
= source
->Pitch
;
127 props
->Gain
= source
->Gain
;
128 props
->OuterGain
= source
->OuterGain
;
129 props
->MinGain
= source
->MinGain
;
130 props
->MaxGain
= source
->MaxGain
;
131 props
->InnerAngle
= source
->InnerAngle
;
132 props
->OuterAngle
= source
->OuterAngle
;
133 props
->RefDistance
= source
->RefDistance
;
134 props
->MaxDistance
= source
->MaxDistance
;
135 props
->RolloffFactor
= source
->RolloffFactor
137 + source
->RolloffFactor2
140 props
->Position
= source
->Position
;
141 props
->Velocity
= source
->Velocity
;
142 props
->Direction
= source
->Direction
;
143 props
->OrientAt
= source
->OrientAt
;
144 props
->OrientUp
= source
->OrientUp
;
145 props
->HeadRelative
= source
->HeadRelative
;
146 props
->mDistanceModel
= source
->mDistanceModel
;
147 props
->mResampler
= source
->mResampler
;
148 props
->DirectChannels
= source
->DirectChannels
;
149 props
->mSpatializeMode
= source
->mSpatialize
;
151 props
->DryGainHFAuto
= source
->DryGainHFAuto
;
152 props
->WetGainAuto
= source
->WetGainAuto
;
153 props
->WetGainHFAuto
= source
->WetGainHFAuto
;
154 props
->OuterGainHF
= source
->OuterGainHF
;
156 props
->AirAbsorptionFactor
= source
->AirAbsorptionFactor
;
157 props
->RoomRolloffFactor
= source
->RoomRolloffFactor
;
158 props
->DopplerFactor
= source
->DopplerFactor
;
160 props
->StereoPan
= source
->StereoPan
;
162 props
->Radius
= source
->Radius
;
163 props
->EnhWidth
= source
->EnhWidth
;
164 props
->Panning
= source
->mPanningEnabled
? source
->mPan
: 0.0f
;
166 props
->Direct
.Gain
= source
->Direct
.Gain
;
167 props
->Direct
.GainHF
= source
->Direct
.GainHF
;
168 props
->Direct
.HFReference
= source
->Direct
.HFReference
;
169 props
->Direct
.GainLF
= source
->Direct
.GainLF
;
170 props
->Direct
.LFReference
= source
->Direct
.LFReference
;
172 auto copy_send
= [](const ALsource::SendData
&srcsend
) noexcept
-> VoiceProps::SendData
174 VoiceProps::SendData ret
{};
175 ret
.Slot
= srcsend
.Slot
? srcsend
.Slot
->mSlot
: nullptr;
176 ret
.Gain
= srcsend
.Gain
;
177 ret
.GainHF
= srcsend
.GainHF
;
178 ret
.HFReference
= srcsend
.HFReference
;
179 ret
.GainLF
= srcsend
.GainLF
;
180 ret
.LFReference
= srcsend
.LFReference
;
183 std::transform(source
->Send
.cbegin(), source
->Send
.cend(), props
->Send
.begin(), copy_send
);
184 if(!props
->Send
[0].Slot
&& context
->mDefaultSlot
)
185 props
->Send
[0].Slot
= context
->mDefaultSlot
->mSlot
;
187 /* Set the new container for updating internal parameters. */
188 props
= voice
->mUpdate
.exchange(props
, std::memory_order_acq_rel
);
191 /* If there was an unused update container, put it back in the
194 AtomicReplaceHead(context
->mFreeVoiceProps
, props
);
198 /* GetSourceSampleOffset
200 * Gets the current read offset for the given Source, in 32.32 fixed-point
201 * samples. The offset is relative to the start of the queue (not the start of
202 * the current buffer).
204 int64_t GetSourceSampleOffset(ALsource
*Source
, ALCcontext
*context
, nanoseconds
*clocktime
)
206 ALCdevice
*device
{context
->mALDevice
.get()};
207 const VoiceBufferItem
*Current
{};
213 refcount
= device
->waitForMix();
214 *clocktime
= device
->getClockTime();
215 voice
= GetSourceVoice(Source
, context
);
218 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
220 readPos
= int64_t{voice
->mPosition
.load(std::memory_order_relaxed
)} << MixerFracBits
;
221 readPos
+= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
223 std::atomic_thread_fence(std::memory_order_acquire
);
224 } while(refcount
!= device
->mMixCount
.load(std::memory_order_relaxed
));
229 for(auto &item
: Source
->mQueue
)
231 if(&item
== Current
) break;
232 readPos
+= int64_t{item
.mSampleLen
} << MixerFracBits
;
234 if(readPos
> std::numeric_limits
<int64_t>::max() >> (32-MixerFracBits
))
235 return std::numeric_limits
<int64_t>::max();
236 return readPos
<< (32-MixerFracBits
);
239 /* GetSourceSecOffset
241 * Gets the current read offset for the given Source, in seconds. The offset is
242 * relative to the start of the queue (not the start of the current buffer).
244 double GetSourceSecOffset(ALsource
*Source
, ALCcontext
*context
, nanoseconds
*clocktime
)
246 ALCdevice
*device
{context
->mALDevice
.get()};
247 const VoiceBufferItem
*Current
{};
253 refcount
= device
->waitForMix();
254 *clocktime
= device
->getClockTime();
255 voice
= GetSourceVoice(Source
, context
);
258 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
260 readPos
= int64_t{voice
->mPosition
.load(std::memory_order_relaxed
)} << MixerFracBits
;
261 readPos
+= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
263 std::atomic_thread_fence(std::memory_order_acquire
);
264 } while(refcount
!= device
->mMixCount
.load(std::memory_order_relaxed
));
269 const ALbuffer
*BufferFmt
{nullptr};
270 auto BufferList
= Source
->mQueue
.cbegin();
271 while(BufferList
!= Source
->mQueue
.cend() && al::to_address(BufferList
) != Current
)
273 if(!BufferFmt
) BufferFmt
= BufferList
->mBuffer
;
274 readPos
+= int64_t{BufferList
->mSampleLen
} << MixerFracBits
;
277 while(BufferList
!= Source
->mQueue
.cend() && !BufferFmt
)
279 BufferFmt
= BufferList
->mBuffer
;
282 ASSUME(BufferFmt
!= nullptr);
284 return static_cast<double>(readPos
) / double{MixerFracOne
} / BufferFmt
->mSampleRate
;
289 * Gets the current read offset for the given Source, in the appropriate format
290 * (Bytes, Samples or Seconds). The offset is relative to the start of the
291 * queue (not the start of the current buffer).
294 NOINLINE T
GetSourceOffset(ALsource
*Source
, ALenum name
, ALCcontext
*context
)
296 ALCdevice
*device
{context
->mALDevice
.get()};
297 const VoiceBufferItem
*Current
{};
304 refcount
= device
->waitForMix();
305 voice
= GetSourceVoice(Source
, context
);
308 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
310 readPos
= voice
->mPosition
.load(std::memory_order_relaxed
);
311 readPosFrac
= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
313 std::atomic_thread_fence(std::memory_order_acquire
);
314 } while(refcount
!= device
->mMixCount
.load(std::memory_order_relaxed
));
319 const ALbuffer
*BufferFmt
{nullptr};
320 auto BufferList
= Source
->mQueue
.cbegin();
321 while(BufferList
!= Source
->mQueue
.cend() && al::to_address(BufferList
) != Current
)
323 if(!BufferFmt
) BufferFmt
= BufferList
->mBuffer
;
324 readPos
+= BufferList
->mSampleLen
;
327 while(BufferList
!= Source
->mQueue
.cend() && !BufferFmt
)
329 BufferFmt
= BufferList
->mBuffer
;
332 ASSUME(BufferFmt
!= nullptr);
338 if constexpr(std::is_floating_point_v
<T
>)
340 offset
= static_cast<T
>(readPos
) + static_cast<T
>(readPosFrac
)/T
{MixerFracOne
};
341 offset
/= static_cast<T
>(BufferFmt
->mSampleRate
);
345 readPos
/= BufferFmt
->mSampleRate
;
346 offset
= static_cast<T
>(std::clamp
<int64_t>(readPos
, std::numeric_limits
<T
>::min(),
347 std::numeric_limits
<T
>::max()));
351 case AL_SAMPLE_OFFSET
:
352 if constexpr(std::is_floating_point_v
<T
>)
353 offset
= static_cast<T
>(readPos
) + static_cast<T
>(readPosFrac
)/T
{MixerFracOne
};
355 offset
= static_cast<T
>(std::clamp
<int64_t>(readPos
, std::numeric_limits
<T
>::min(),
356 std::numeric_limits
<T
>::max()));
360 const ALuint BlockSamples
{BufferFmt
->mBlockAlign
};
361 const ALuint BlockSize
{BufferFmt
->blockSizeFromFmt()};
362 /* Round down to the block boundary. */
363 readPos
= readPos
/ BlockSamples
* BlockSize
;
365 if constexpr(std::is_floating_point_v
<T
>)
366 offset
= static_cast<T
>(readPos
);
369 if(readPos
> std::numeric_limits
<T
>::max())
370 offset
= RoundDown(std::numeric_limits
<T
>::max(), static_cast<T
>(BlockSize
));
371 else if(readPos
< std::numeric_limits
<T
>::min())
372 offset
= RoundUp(std::numeric_limits
<T
>::min(), static_cast<T
>(BlockSize
));
374 offset
= static_cast<T
>(readPos
);
383 * Gets the length of the given Source's buffer queue, in the appropriate
384 * format (Bytes, Samples or Seconds).
387 NOINLINE T
GetSourceLength(const ALsource
*source
, ALenum name
)
390 const ALbuffer
*BufferFmt
{nullptr};
391 for(auto &listitem
: source
->mQueue
)
394 BufferFmt
= listitem
.mBuffer
;
395 length
+= listitem
.mSampleLen
;
400 ASSUME(BufferFmt
!= nullptr);
403 case AL_SEC_LENGTH_SOFT
:
404 if constexpr(std::is_floating_point_v
<T
>)
405 return static_cast<T
>(length
) / static_cast<T
>(BufferFmt
->mSampleRate
);
407 return static_cast<T
>(std::min
<uint64_t>(length
/BufferFmt
->mSampleRate
,
408 std::numeric_limits
<T
>::max()));
410 case AL_SAMPLE_LENGTH_SOFT
:
411 if constexpr(std::is_floating_point_v
<T
>)
412 return static_cast<T
>(length
);
414 return static_cast<T
>(std::min
<uint64_t>(length
, std::numeric_limits
<T
>::max()));
416 case AL_BYTE_LENGTH_SOFT
:
417 const ALuint BlockSamples
{BufferFmt
->mBlockAlign
};
418 const ALuint BlockSize
{BufferFmt
->blockSizeFromFmt()};
419 /* Round down to the block boundary. */
420 length
= length
/ BlockSamples
* BlockSize
;
422 if constexpr(std::is_floating_point_v
<T
>)
423 return static_cast<T
>(length
);
426 if(length
> uint64_t{std::numeric_limits
<T
>::max()})
427 return RoundDown(std::numeric_limits
<T
>::max(), static_cast<T
>(BlockSize
));
428 return static_cast<T
>(length
);
438 ALbufferQueueItem
*bufferitem
;
444 * Retrieves the voice position, fixed-point fraction, and bufferlist item
445 * using the given offset type and offset. If the offset is out of range,
446 * returns an empty optional.
448 std::optional
<VoicePos
> GetSampleOffset(std::deque
<ALbufferQueueItem
> &BufferList
,
449 ALenum OffsetType
, double Offset
)
451 /* Find the first valid Buffer in the Queue */
452 const ALbuffer
*BufferFmt
{nullptr};
453 for(auto &item
: BufferList
)
455 BufferFmt
= item
.mBuffer
;
458 if(!BufferFmt
) UNLIKELY
461 /* Get sample frame offset */
464 double dbloff
, dblfrac
;
468 dblfrac
= std::modf(Offset
*BufferFmt
->mSampleRate
, &dbloff
);
471 /* If there's a negative fraction, reduce the offset to "floor" it,
472 * and convert the fraction to a percentage to the next value (e.g.
473 * -2.75 -> -3 + 0.25).
478 offset
= static_cast<int64_t>(dbloff
);
479 frac
= static_cast<uint
>(std::min(dblfrac
*MixerFracOne
, MixerFracOne
-1.0));
482 case AL_SAMPLE_OFFSET
:
483 dblfrac
= std::modf(Offset
, &dbloff
);
489 offset
= static_cast<int64_t>(dbloff
);
490 frac
= static_cast<uint
>(std::min(dblfrac
*MixerFracOne
, MixerFracOne
-1.0));
494 /* Determine the ByteOffset (and ensure it is block aligned) */
495 Offset
= std::floor(Offset
/ BufferFmt
->blockSizeFromFmt());
496 offset
= static_cast<int64_t>(Offset
) * BufferFmt
->mBlockAlign
;
501 /* Find the bufferlist item this offset belongs to. */
504 if(offset
< std::numeric_limits
<int>::min())
506 return VoicePos
{static_cast<int>(offset
), frac
, &BufferList
.front()};
509 if(BufferFmt
->mCallback
)
512 int64_t totalBufferLen
{0};
513 for(auto &item
: BufferList
)
515 if(totalBufferLen
> offset
)
517 if(item
.mSampleLen
> offset
-totalBufferLen
)
519 /* Offset is in this buffer */
520 return VoicePos
{static_cast<int>(offset
-totalBufferLen
), frac
, &item
};
522 totalBufferLen
+= item
.mSampleLen
;
525 /* Offset is out of range of the queue */
530 void InitVoice(Voice
*voice
, ALsource
*source
, ALbufferQueueItem
*BufferList
, ALCcontext
*context
,
533 voice
->mLoopBuffer
.store(source
->Looping
? &source
->mQueue
.front() : nullptr,
534 std::memory_order_relaxed
);
536 ALbuffer
*buffer
{BufferList
->mBuffer
};
537 voice
->mFrequency
= buffer
->mSampleRate
;
538 if(buffer
->mChannels
== FmtMono
&& source
->mPanningEnabled
)
539 voice
->mFmtChannels
= FmtMonoDup
;
540 else if(buffer
->mChannels
== FmtStereo
&& source
->mStereoMode
== SourceStereo::Enhanced
)
541 voice
->mFmtChannels
= FmtSuperStereo
;
543 voice
->mFmtChannels
= buffer
->mChannels
;
544 voice
->mFmtType
= buffer
->mType
;
545 voice
->mFrameStep
= buffer
->channelsFromFmt();
546 voice
->mBytesPerBlock
= buffer
->blockSizeFromFmt();
547 voice
->mSamplesPerBlock
= buffer
->mBlockAlign
;
548 voice
->mAmbiLayout
= IsUHJ(voice
->mFmtChannels
) ? AmbiLayout::FuMa
: buffer
->mAmbiLayout
;
549 voice
->mAmbiScaling
= IsUHJ(voice
->mFmtChannels
) ? AmbiScaling::UHJ
: buffer
->mAmbiScaling
;
550 voice
->mAmbiOrder
= (voice
->mFmtChannels
== FmtSuperStereo
) ? 1 : buffer
->mAmbiOrder
;
552 if(buffer
->mCallback
) voice
->mFlags
.set(VoiceIsCallback
);
553 else if(source
->SourceType
== AL_STATIC
) voice
->mFlags
.set(VoiceIsStatic
);
554 voice
->mNumCallbackBlocks
= 0;
555 voice
->mCallbackBlockBase
= 0;
557 voice
->prepare(device
);
559 source
->mPropsDirty
= false;
560 UpdateSourceProps(source
, voice
, context
);
562 voice
->mSourceID
.store(source
->id
, std::memory_order_release
);
566 VoiceChange
*GetVoiceChanger(ALCcontext
*ctx
)
568 VoiceChange
*vchg
{ctx
->mVoiceChangeTail
};
569 if(vchg
== ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)) UNLIKELY
571 ctx
->allocVoiceChanges();
572 vchg
= ctx
->mVoiceChangeTail
;
575 ctx
->mVoiceChangeTail
= vchg
->mNext
.exchange(nullptr, std::memory_order_relaxed
);
580 void SendVoiceChanges(ALCcontext
*ctx
, VoiceChange
*tail
)
582 ALCdevice
*device
{ctx
->mALDevice
.get()};
584 VoiceChange
*oldhead
{ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)};
585 while(VoiceChange
*next
{oldhead
->mNext
.load(std::memory_order_relaxed
)})
587 oldhead
->mNext
.store(tail
, std::memory_order_release
);
589 const bool connected
{device
->Connected
.load(std::memory_order_acquire
)};
590 std::ignore
= device
->waitForMix();
591 if(!connected
) UNLIKELY
593 if(ctx
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
595 /* If the device is disconnected and voices are stopped, just
596 * ignore all pending changes.
598 VoiceChange
*cur
{ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)};
599 while(VoiceChange
*next
{cur
->mNext
.load(std::memory_order_acquire
)})
602 if(Voice
*voice
{cur
->mVoice
})
603 voice
->mSourceID
.store(0, std::memory_order_relaxed
);
605 ctx
->mCurrentVoiceChange
.store(cur
, std::memory_order_release
);
611 bool SetVoiceOffset(Voice
*oldvoice
, const VoicePos
&vpos
, ALsource
*source
, ALCcontext
*context
,
614 /* First, get a free voice to start at the new offset. */
615 auto voicelist
= context
->getVoicesSpan();
618 for(Voice
*voice
: voicelist
)
620 if(voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
621 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
622 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false)
629 if(!newvoice
) UNLIKELY
631 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
632 if(allvoices
.size() == voicelist
.size())
633 context
->allocVoices(1);
634 context
->mActiveVoiceCount
.fetch_add(1, std::memory_order_release
);
635 voicelist
= context
->getVoicesSpan();
638 for(Voice
*voice
: voicelist
)
640 if(voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
641 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
642 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false)
649 ASSUME(newvoice
!= nullptr);
652 /* Initialize the new voice and set its starting offset.
653 * TODO: It might be better to have the VoiceChange processing copy the old
654 * voice's mixing parameters (and pending update) insead of initializing it
655 * all here. This would just need to set the minimum properties to link the
656 * voice to the source and its position-dependent properties (including the
659 newvoice
->mPlayState
.store(Voice::Pending
, std::memory_order_relaxed
);
660 newvoice
->mPosition
.store(vpos
.pos
, std::memory_order_relaxed
);
661 newvoice
->mPositionFrac
.store(vpos
.frac
, std::memory_order_relaxed
);
662 newvoice
->mCurrentBuffer
.store(vpos
.bufferitem
, std::memory_order_relaxed
);
663 newvoice
->mStartTime
= oldvoice
->mStartTime
;
664 newvoice
->mFlags
.reset();
665 if(vpos
.pos
> 0 || (vpos
.pos
== 0 && vpos
.frac
> 0)
666 || vpos
.bufferitem
!= &source
->mQueue
.front())
667 newvoice
->mFlags
.set(VoiceIsFading
);
668 InitVoice(newvoice
, source
, vpos
.bufferitem
, context
, device
);
669 source
->VoiceIdx
= vidx
;
671 /* Set the old voice as having a pending change, and send it off with the
672 * new one with a new offset voice change.
674 oldvoice
->mPendingChange
.store(true, std::memory_order_relaxed
);
676 VoiceChange
*vchg
{GetVoiceChanger(context
)};
677 vchg
->mOldVoice
= oldvoice
;
678 vchg
->mVoice
= newvoice
;
679 vchg
->mSourceID
= source
->id
;
680 vchg
->mState
= VChangeState::Restart
;
681 SendVoiceChanges(context
, vchg
);
683 /* If the old voice still has a sourceID, it's still active and the change-
684 * over will work on the next update.
686 if(oldvoice
->mSourceID
.load(std::memory_order_acquire
) != 0u) LIKELY
689 /* Otherwise, if the new voice's state is not pending, the change-over
692 if(newvoice
->mPlayState
.load(std::memory_order_acquire
) != Voice::Pending
)
695 /* Otherwise, wait for any current mix to finish and check one last time. */
696 std::ignore
= device
->waitForMix();
697 if(newvoice
->mPlayState
.load(std::memory_order_acquire
) != Voice::Pending
)
699 /* The change-over failed because the old voice stopped before the new
700 * voice could start at the new offset. Let go of the new voice and have
701 * the caller store the source offset since it's stopped.
703 newvoice
->mCurrentBuffer
.store(nullptr, std::memory_order_relaxed
);
704 newvoice
->mLoopBuffer
.store(nullptr, std::memory_order_relaxed
);
705 newvoice
->mSourceID
.store(0u, std::memory_order_relaxed
);
706 newvoice
->mPlayState
.store(Voice::Stopped
, std::memory_order_relaxed
);
712 * Returns if the last known state for the source was playing or paused. Does
713 * not sync with the mixer voice.
715 inline bool IsPlayingOrPaused(ALsource
*source
)
716 { return source
->state
== AL_PLAYING
|| source
->state
== AL_PAUSED
; }
719 * Returns an updated source state using the matching voice's status (or lack
722 inline ALenum
GetSourceState(ALsource
*source
, Voice
*voice
)
724 if(!voice
&& source
->state
== AL_PLAYING
)
725 source
->state
= AL_STOPPED
;
726 return source
->state
;
730 bool EnsureSources(ALCcontext
*context
, size_t needed
)
732 size_t count
{std::accumulate(context
->mSourceList
.cbegin(), context
->mSourceList
.cend(), 0_uz
,
733 [](size_t cur
, const SourceSubList
&sublist
) noexcept
-> size_t
734 { return cur
+ static_cast<ALuint
>(al::popcount(sublist
.FreeMask
)); })};
737 while(needed
> count
)
739 if(context
->mSourceList
.size() >= 1<<25) UNLIKELY
742 SourceSubList sublist
{};
743 sublist
.FreeMask
= ~0_u64
;
744 sublist
.Sources
= SubListAllocator
{}.allocate(1);
745 context
->mSourceList
.emplace_back(std::move(sublist
));
746 count
+= std::tuple_size_v
<SubListAllocator::value_type
>;
755 ALsource
*AllocSource(ALCcontext
*context
) noexcept
757 auto sublist
= std::find_if(context
->mSourceList
.begin(), context
->mSourceList
.end(),
758 [](const SourceSubList
&entry
) noexcept
-> bool
759 { return entry
.FreeMask
!= 0; });
760 auto lidx
= static_cast<ALuint
>(std::distance(context
->mSourceList
.begin(), sublist
));
761 auto slidx
= static_cast<ALuint
>(al::countr_zero(sublist
->FreeMask
));
764 ALsource
*source
{al::construct_at(al::to_address(sublist
->Sources
->begin() + slidx
))};
766 source
->eaxInitialize(context
);
769 /* Add 1 to avoid source ID 0. */
770 source
->id
= ((lidx
<<6) | slidx
) + 1;
772 context
->mNumSources
+= 1;
773 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
778 void FreeSource(ALCcontext
*context
, ALsource
*source
)
780 context
->mSourceNames
.erase(source
->id
);
782 const ALuint id
{source
->id
- 1};
783 const size_t lidx
{id
>> 6};
784 const ALuint slidx
{id
& 0x3f};
786 if(Voice
*voice
{GetSourceVoice(source
, context
)})
788 VoiceChange
*vchg
{GetVoiceChanger(context
)};
790 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
791 vchg
->mVoice
= voice
;
792 vchg
->mSourceID
= source
->id
;
793 vchg
->mState
= VChangeState::Stop
;
795 SendVoiceChanges(context
, vchg
);
798 std::destroy_at(source
);
800 context
->mSourceList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
801 context
->mNumSources
--;
805 inline ALsource
*LookupSource(ALCcontext
*context
, ALuint id
) noexcept
807 const size_t lidx
{(id
-1) >> 6};
808 const ALuint slidx
{(id
-1) & 0x3f};
810 if(lidx
>= context
->mSourceList
.size()) UNLIKELY
812 SourceSubList
&sublist
{context
->mSourceList
[lidx
]};
813 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
815 return al::to_address(sublist
.Sources
->begin() + slidx
);
818 auto LookupBuffer
= [](ALCdevice
*device
, auto id
) noexcept
-> ALbuffer
*
820 const auto lidx
{(id
-1) >> 6};
821 const auto slidx
{(id
-1) & 0x3f};
823 if(lidx
>= device
->BufferList
.size()) UNLIKELY
825 BufferSubList
&sublist
= device
->BufferList
[static_cast<size_t>(lidx
)];
826 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
828 return al::to_address(sublist
.Buffers
->begin() + static_cast<size_t>(slidx
));
831 auto LookupFilter
= [](ALCdevice
*device
, auto id
) noexcept
-> ALfilter
*
833 const auto lidx
{(id
-1) >> 6};
834 const auto slidx
{(id
-1) & 0x3f};
836 if(lidx
>= device
->FilterList
.size()) UNLIKELY
838 FilterSubList
&sublist
= device
->FilterList
[static_cast<size_t>(lidx
)];
839 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
841 return al::to_address(sublist
.Filters
->begin() + static_cast<size_t>(slidx
));
844 auto LookupEffectSlot
= [](ALCcontext
*context
, auto id
) noexcept
-> ALeffectslot
*
846 const auto lidx
{(id
-1) >> 6};
847 const auto slidx
{(id
-1) & 0x3f};
849 if(lidx
>= context
->mEffectSlotList
.size()) UNLIKELY
851 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[static_cast<size_t>(lidx
)]};
852 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
854 return al::to_address(sublist
.EffectSlots
->begin() + static_cast<size_t>(slidx
));
858 auto StereoModeFromEnum
= [](auto mode
) noexcept
-> std::optional
<SourceStereo
>
862 case AL_NORMAL_SOFT
: return SourceStereo::Normal
;
863 case AL_SUPER_STEREO_SOFT
: return SourceStereo::Enhanced
;
867 ALenum
EnumFromStereoMode(SourceStereo mode
)
871 case SourceStereo::Normal
: return AL_NORMAL_SOFT
;
872 case SourceStereo::Enhanced
: return AL_SUPER_STEREO_SOFT
;
874 throw std::runtime_error
{"Invalid SourceStereo: "+std::to_string(int(mode
))};
877 auto SpatializeModeFromEnum
= [](auto mode
) noexcept
-> std::optional
<SpatializeMode
>
881 case AL_FALSE
: return SpatializeMode::Off
;
882 case AL_TRUE
: return SpatializeMode::On
;
883 case AL_AUTO_SOFT
: return SpatializeMode::Auto
;
887 ALenum
EnumFromSpatializeMode(SpatializeMode mode
)
891 case SpatializeMode::Off
: return AL_FALSE
;
892 case SpatializeMode::On
: return AL_TRUE
;
893 case SpatializeMode::Auto
: return AL_AUTO_SOFT
;
895 throw std::runtime_error
{"Invalid SpatializeMode: "+std::to_string(int(mode
))};
898 auto DirectModeFromEnum
= [](auto mode
) noexcept
-> std::optional
<DirectMode
>
902 case AL_FALSE
: return DirectMode::Off
;
903 case AL_DROP_UNMATCHED_SOFT
: return DirectMode::DropMismatch
;
904 case AL_REMIX_UNMATCHED_SOFT
: return DirectMode::RemixMismatch
;
908 ALenum
EnumFromDirectMode(DirectMode mode
)
912 case DirectMode::Off
: return AL_FALSE
;
913 case DirectMode::DropMismatch
: return AL_DROP_UNMATCHED_SOFT
;
914 case DirectMode::RemixMismatch
: return AL_REMIX_UNMATCHED_SOFT
;
916 throw std::runtime_error
{"Invalid DirectMode: "+std::to_string(int(mode
))};
919 auto DistanceModelFromALenum
= [](auto model
) noexcept
-> std::optional
<DistanceModel
>
923 case AL_NONE
: return DistanceModel::Disable
;
924 case AL_INVERSE_DISTANCE
: return DistanceModel::Inverse
;
925 case AL_INVERSE_DISTANCE_CLAMPED
: return DistanceModel::InverseClamped
;
926 case AL_LINEAR_DISTANCE
: return DistanceModel::Linear
;
927 case AL_LINEAR_DISTANCE_CLAMPED
: return DistanceModel::LinearClamped
;
928 case AL_EXPONENT_DISTANCE
: return DistanceModel::Exponent
;
929 case AL_EXPONENT_DISTANCE_CLAMPED
: return DistanceModel::ExponentClamped
;
933 ALenum
ALenumFromDistanceModel(DistanceModel model
)
937 case DistanceModel::Disable
: return AL_NONE
;
938 case DistanceModel::Inverse
: return AL_INVERSE_DISTANCE
;
939 case DistanceModel::InverseClamped
: return AL_INVERSE_DISTANCE_CLAMPED
;
940 case DistanceModel::Linear
: return AL_LINEAR_DISTANCE
;
941 case DistanceModel::LinearClamped
: return AL_LINEAR_DISTANCE_CLAMPED
;
942 case DistanceModel::Exponent
: return AL_EXPONENT_DISTANCE
;
943 case DistanceModel::ExponentClamped
: return AL_EXPONENT_DISTANCE_CLAMPED
;
945 throw std::runtime_error
{"Unexpected distance model "+std::to_string(static_cast<int>(model
))};
948 enum SourceProp
: ALenum
{
951 srcMinGain
= AL_MIN_GAIN
,
952 srcMaxGain
= AL_MAX_GAIN
,
953 srcMaxDistance
= AL_MAX_DISTANCE
,
954 srcRolloffFactor
= AL_ROLLOFF_FACTOR
,
955 srcDopplerFactor
= AL_DOPPLER_FACTOR
,
956 srcConeOuterGain
= AL_CONE_OUTER_GAIN
,
957 srcSecOffset
= AL_SEC_OFFSET
,
958 srcSampleOffset
= AL_SAMPLE_OFFSET
,
959 srcByteOffset
= AL_BYTE_OFFSET
,
960 srcConeInnerAngle
= AL_CONE_INNER_ANGLE
,
961 srcConeOuterAngle
= AL_CONE_OUTER_ANGLE
,
962 srcRefDistance
= AL_REFERENCE_DISTANCE
,
964 srcPosition
= AL_POSITION
,
965 srcVelocity
= AL_VELOCITY
,
966 srcDirection
= AL_DIRECTION
,
968 srcSourceRelative
= AL_SOURCE_RELATIVE
,
969 srcLooping
= AL_LOOPING
,
970 srcBuffer
= AL_BUFFER
,
971 srcSourceState
= AL_SOURCE_STATE
,
972 srcBuffersQueued
= AL_BUFFERS_QUEUED
,
973 srcBuffersProcessed
= AL_BUFFERS_PROCESSED
,
974 srcSourceType
= AL_SOURCE_TYPE
,
977 srcConeOuterGainHF
= AL_CONE_OUTER_GAINHF
,
978 srcAirAbsorptionFactor
= AL_AIR_ABSORPTION_FACTOR
,
979 srcRoomRolloffFactor
= AL_ROOM_ROLLOFF_FACTOR
,
980 srcDirectFilterGainHFAuto
= AL_DIRECT_FILTER_GAINHF_AUTO
,
981 srcAuxSendFilterGainAuto
= AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
,
982 srcAuxSendFilterGainHFAuto
= AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
,
983 srcDirectFilter
= AL_DIRECT_FILTER
,
984 srcAuxSendFilter
= AL_AUXILIARY_SEND_FILTER
,
986 /* AL_SOFT_direct_channels */
987 srcDirectChannelsSOFT
= AL_DIRECT_CHANNELS_SOFT
,
989 /* AL_EXT_source_distance_model */
990 srcDistanceModel
= AL_DISTANCE_MODEL
,
992 /* AL_SOFT_source_latency */
993 srcSampleOffsetLatencySOFT
= AL_SAMPLE_OFFSET_LATENCY_SOFT
,
994 srcSecOffsetLatencySOFT
= AL_SEC_OFFSET_LATENCY_SOFT
,
996 /* AL_EXT_STEREO_ANGLES */
997 srcAngles
= AL_STEREO_ANGLES
,
999 /* AL_EXT_SOURCE_RADIUS */
1000 srcRadius
= AL_SOURCE_RADIUS
,
1002 /* AL_EXT_BFORMAT */
1003 srcOrientation
= AL_ORIENTATION
,
1005 /* AL_SOFT_source_length */
1006 srcByteLength
= AL_BYTE_LENGTH_SOFT
,
1007 srcSampleLength
= AL_SAMPLE_LENGTH_SOFT
,
1008 srcSecLength
= AL_SEC_LENGTH_SOFT
,
1010 /* AL_SOFT_source_resampler */
1011 srcResampler
= AL_SOURCE_RESAMPLER_SOFT
,
1013 /* AL_SOFT_source_spatialize */
1014 srcSpatialize
= AL_SOURCE_SPATIALIZE_SOFT
,
1016 /* ALC_SOFT_device_clock */
1017 srcSampleOffsetClockSOFT
= AL_SAMPLE_OFFSET_CLOCK_SOFT
,
1018 srcSecOffsetClockSOFT
= AL_SEC_OFFSET_CLOCK_SOFT
,
1021 srcStereoMode
= AL_STEREO_MODE_SOFT
,
1022 srcSuperStereoWidth
= AL_SUPER_STEREO_WIDTH_SOFT
,
1024 /* AL_SOFT_buffer_sub_data */
1025 srcByteRWOffsetsSOFT
= AL_BYTE_RW_OFFSETS_SOFT
,
1026 srcSampleRWOffsetsSOFT
= AL_SAMPLE_RW_OFFSETS_SOFT
,
1028 /* AL_SOFT_source_panning */
1029 srcPanningEnabledSOFT
= AL_PANNING_ENABLED_SOFT
,
1030 srcPanSOFT
= AL_PAN_SOFT
,
1034 constexpr ALuint
IntValsByProp(ALenum prop
)
1036 switch(static_cast<SourceProp
>(prop
))
1038 case AL_SOURCE_STATE
:
1039 case AL_SOURCE_TYPE
:
1040 case AL_BUFFERS_QUEUED
:
1041 case AL_BUFFERS_PROCESSED
:
1042 case AL_BYTE_LENGTH_SOFT
:
1043 case AL_SAMPLE_LENGTH_SOFT
:
1044 case AL_SOURCE_RELATIVE
:
1047 case AL_SAMPLE_OFFSET
:
1048 case AL_BYTE_OFFSET
:
1049 case AL_DIRECT_FILTER
:
1050 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1051 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1052 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1053 case AL_DIRECT_CHANNELS_SOFT
:
1054 case AL_DISTANCE_MODEL
:
1055 case AL_SOURCE_RESAMPLER_SOFT
:
1056 case AL_SOURCE_SPATIALIZE_SOFT
:
1057 case AL_STEREO_MODE_SOFT
:
1058 case AL_PANNING_ENABLED_SOFT
:
1062 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1063 if(sBufferSubDataCompat
)
1066 case AL_CONE_INNER_ANGLE
:
1067 case AL_CONE_OUTER_ANGLE
:
1072 case AL_REFERENCE_DISTANCE
:
1073 case AL_ROLLOFF_FACTOR
:
1074 case AL_CONE_OUTER_GAIN
:
1075 case AL_MAX_DISTANCE
:
1077 case AL_DOPPLER_FACTOR
:
1078 case AL_CONE_OUTER_GAINHF
:
1079 case AL_AIR_ABSORPTION_FACTOR
:
1080 case AL_ROOM_ROLLOFF_FACTOR
:
1081 case AL_SEC_LENGTH_SOFT
:
1082 case AL_SUPER_STEREO_WIDTH_SOFT
:
1083 return 1; /* 1x float */
1085 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1086 if(sBufferSubDataCompat
)
1090 case AL_AUXILIARY_SEND_FILTER
:
1096 return 3; /* 3x float */
1098 case AL_ORIENTATION
:
1099 return 6; /* 6x float */
1101 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1102 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1103 case AL_STEREO_ANGLES
:
1104 break; /* i64 only */
1105 case AL_SEC_OFFSET_LATENCY_SOFT
:
1106 case AL_SEC_OFFSET_CLOCK_SOFT
:
1107 break; /* double only */
1113 constexpr ALuint
Int64ValsByProp(ALenum prop
)
1115 switch(static_cast<SourceProp
>(prop
))
1117 case AL_SOURCE_STATE
:
1118 case AL_SOURCE_TYPE
:
1119 case AL_BUFFERS_QUEUED
:
1120 case AL_BUFFERS_PROCESSED
:
1121 case AL_BYTE_LENGTH_SOFT
:
1122 case AL_SAMPLE_LENGTH_SOFT
:
1123 case AL_SOURCE_RELATIVE
:
1126 case AL_SAMPLE_OFFSET
:
1127 case AL_BYTE_OFFSET
:
1128 case AL_DIRECT_FILTER
:
1129 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1130 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1131 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1132 case AL_DIRECT_CHANNELS_SOFT
:
1133 case AL_DISTANCE_MODEL
:
1134 case AL_SOURCE_RESAMPLER_SOFT
:
1135 case AL_SOURCE_SPATIALIZE_SOFT
:
1136 case AL_STEREO_MODE_SOFT
:
1137 case AL_PANNING_ENABLED_SOFT
:
1141 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1142 if(sBufferSubDataCompat
)
1145 case AL_CONE_INNER_ANGLE
:
1146 case AL_CONE_OUTER_ANGLE
:
1151 case AL_REFERENCE_DISTANCE
:
1152 case AL_ROLLOFF_FACTOR
:
1153 case AL_CONE_OUTER_GAIN
:
1154 case AL_MAX_DISTANCE
:
1156 case AL_DOPPLER_FACTOR
:
1157 case AL_CONE_OUTER_GAINHF
:
1158 case AL_AIR_ABSORPTION_FACTOR
:
1159 case AL_ROOM_ROLLOFF_FACTOR
:
1160 case AL_SEC_LENGTH_SOFT
:
1161 case AL_SUPER_STEREO_WIDTH_SOFT
:
1162 return 1; /* 1x float */
1164 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1165 if(sBufferSubDataCompat
)
1169 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1170 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1171 case AL_STEREO_ANGLES
:
1174 case AL_AUXILIARY_SEND_FILTER
:
1180 return 3; /* 3x float */
1182 case AL_ORIENTATION
:
1183 return 6; /* 6x float */
1185 case AL_SEC_OFFSET_LATENCY_SOFT
:
1186 case AL_SEC_OFFSET_CLOCK_SOFT
:
1187 break; /* double only */
1193 constexpr ALuint
FloatValsByProp(ALenum prop
)
1195 switch(static_cast<SourceProp
>(prop
))
1201 case AL_MAX_DISTANCE
:
1202 case AL_ROLLOFF_FACTOR
:
1203 case AL_DOPPLER_FACTOR
:
1204 case AL_CONE_OUTER_GAIN
:
1206 case AL_SAMPLE_OFFSET
:
1207 case AL_BYTE_OFFSET
:
1208 case AL_CONE_INNER_ANGLE
:
1209 case AL_CONE_OUTER_ANGLE
:
1210 case AL_REFERENCE_DISTANCE
:
1211 case AL_CONE_OUTER_GAINHF
:
1212 case AL_AIR_ABSORPTION_FACTOR
:
1213 case AL_ROOM_ROLLOFF_FACTOR
:
1214 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1215 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1216 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1217 case AL_DIRECT_CHANNELS_SOFT
:
1218 case AL_DISTANCE_MODEL
:
1219 case AL_SOURCE_RELATIVE
:
1221 case AL_SOURCE_STATE
:
1222 case AL_BUFFERS_QUEUED
:
1223 case AL_BUFFERS_PROCESSED
:
1224 case AL_SOURCE_TYPE
:
1225 case AL_SOURCE_RESAMPLER_SOFT
:
1226 case AL_SOURCE_SPATIALIZE_SOFT
:
1227 case AL_BYTE_LENGTH_SOFT
:
1228 case AL_SAMPLE_LENGTH_SOFT
:
1229 case AL_SEC_LENGTH_SOFT
:
1230 case AL_STEREO_MODE_SOFT
:
1231 case AL_SUPER_STEREO_WIDTH_SOFT
:
1232 case AL_PANNING_ENABLED_SOFT
:
1236 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1237 if(!sBufferSubDataCompat
)
1240 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1243 case AL_STEREO_ANGLES
:
1251 case AL_ORIENTATION
:
1254 case AL_SEC_OFFSET_LATENCY_SOFT
:
1255 case AL_SEC_OFFSET_CLOCK_SOFT
:
1256 break; /* Double only */
1259 case AL_DIRECT_FILTER
:
1260 case AL_AUXILIARY_SEND_FILTER
:
1261 break; /* i/i64 only */
1262 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1263 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1264 break; /* i64 only */
1268 constexpr ALuint
DoubleValsByProp(ALenum prop
)
1270 switch(static_cast<SourceProp
>(prop
))
1276 case AL_MAX_DISTANCE
:
1277 case AL_ROLLOFF_FACTOR
:
1278 case AL_DOPPLER_FACTOR
:
1279 case AL_CONE_OUTER_GAIN
:
1281 case AL_SAMPLE_OFFSET
:
1282 case AL_BYTE_OFFSET
:
1283 case AL_CONE_INNER_ANGLE
:
1284 case AL_CONE_OUTER_ANGLE
:
1285 case AL_REFERENCE_DISTANCE
:
1286 case AL_CONE_OUTER_GAINHF
:
1287 case AL_AIR_ABSORPTION_FACTOR
:
1288 case AL_ROOM_ROLLOFF_FACTOR
:
1289 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1290 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1291 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1292 case AL_DIRECT_CHANNELS_SOFT
:
1293 case AL_DISTANCE_MODEL
:
1294 case AL_SOURCE_RELATIVE
:
1296 case AL_SOURCE_STATE
:
1297 case AL_BUFFERS_QUEUED
:
1298 case AL_BUFFERS_PROCESSED
:
1299 case AL_SOURCE_TYPE
:
1300 case AL_SOURCE_RESAMPLER_SOFT
:
1301 case AL_SOURCE_SPATIALIZE_SOFT
:
1302 case AL_BYTE_LENGTH_SOFT
:
1303 case AL_SAMPLE_LENGTH_SOFT
:
1304 case AL_SEC_LENGTH_SOFT
:
1305 case AL_STEREO_MODE_SOFT
:
1306 case AL_SUPER_STEREO_WIDTH_SOFT
:
1307 case AL_PANNING_ENABLED_SOFT
:
1311 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1312 if(!sBufferSubDataCompat
)
1315 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1318 case AL_SEC_OFFSET_LATENCY_SOFT
:
1319 case AL_SEC_OFFSET_CLOCK_SOFT
:
1320 case AL_STEREO_ANGLES
:
1328 case AL_ORIENTATION
:
1332 case AL_DIRECT_FILTER
:
1333 case AL_AUXILIARY_SEND_FILTER
:
1334 break; /* i/i64 only */
1335 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1336 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1337 break; /* i64 only */
1343 void UpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1345 if(!context
->mDeferUpdates
)
1347 if(Voice
*voice
{GetSourceVoice(source
, context
)})
1349 UpdateSourceProps(source
, voice
, context
);
1353 source
->mPropsDirty
= true;
1356 void CommitAndUpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1358 if(!context
->mDeferUpdates
)
1360 if(context
->hasEax())
1361 source
->eaxCommit();
1362 if(Voice
*voice
{GetSourceVoice(source
, context
)})
1364 UpdateSourceProps(source
, voice
, context
);
1368 source
->mPropsDirty
= true;
1373 inline void CommitAndUpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1374 { UpdateSourceProps(source
, context
); }
1378 template<typename T
>
1379 struct PropType
{ };
1381 struct PropType
<ALint
> { static const char *Name() { return "integer"; } };
1383 struct PropType
<ALint64SOFT
> { static const char *Name() { return "int64"; } };
1385 struct PropType
<ALfloat
> { static const char *Name() { return "float"; } };
1387 struct PropType
<ALdouble
> { static const char *Name() { return "double"; } };
1390 std::array
<char,32> mStr
{};
1392 template<typename T
>
1395 using ST
= std::make_signed_t
<std::remove_cv_t
<T
>>;
1396 if constexpr(std::is_same_v
<ST
,int>)
1397 std::snprintf(mStr
.data(), mStr
.size(), "0x%x", value
);
1398 else if constexpr(std::is_same_v
<ST
,long>)
1399 std::snprintf(mStr
.data(), mStr
.size(), "0x%lx", value
);
1400 else if constexpr(std::is_same_v
<ST
,long long>)
1401 std::snprintf(mStr
.data(), mStr
.size(), "0x%llx", value
);
1404 [[nodiscard
]] auto c_str() const noexcept
-> const char* { return mStr
.data(); }
1409 * Returns a pair of lambdas to check the following setter.
1411 * The first lambda checks the size of the span is valid for the required size,
1412 * setting the proper context error and throwing a check_size_exception if it
1415 * The second lambda tests the validity of the value check, setting the proper
1416 * context error and throwing a check_value_exception if it failed.
1418 template<typename T
, size_t N
>
1419 auto GetCheckers(const SourceProp prop
, const al::span
<T
,N
> values
)
1421 return std::make_pair(
1422 [=](size_t expect
) -> void
1424 if(values
.size() == expect
) return;
1425 throw al::context_error
{AL_INVALID_ENUM
,
1426 "Property 0x%04x expects %zu value(s), got %zu", prop
, expect
, values
.size()};
1428 [](bool passed
) -> void
1431 throw al::context_error
{AL_INVALID_VALUE
, "Value out of range"};
1436 template<typename T
>
1437 NOINLINE
void SetProperty(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
1438 const al::span
<const T
> values
)
1440 auto [CheckSize
, CheckValue
] = GetCheckers(prop
, values
);
1441 ALCdevice
*device
{Context
->mALDevice
.get()};
1445 case AL_SOURCE_STATE
:
1446 case AL_SOURCE_TYPE
:
1447 case AL_BUFFERS_QUEUED
:
1448 case AL_BUFFERS_PROCESSED
:
1449 if constexpr(std::is_integral_v
<T
>)
1452 throw al::context_error
{AL_INVALID_OPERATION
,
1453 "Setting read-only source property 0x%04x", prop
};
1457 case AL_BYTE_LENGTH_SOFT
:
1458 case AL_SAMPLE_LENGTH_SOFT
:
1459 case AL_SEC_LENGTH_SOFT
:
1460 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1461 case AL_SEC_OFFSET_LATENCY_SOFT
:
1462 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1463 case AL_SEC_OFFSET_CLOCK_SOFT
:
1465 throw al::context_error
{AL_INVALID_OPERATION
, "Setting read-only source property 0x%04x",
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 CheckValue(values
[0] >= T
{0});
1493 Source
->Gain
= static_cast<float>(values
[0]);
1494 return UpdateSourceProps(Source
, Context
);
1496 case AL_MAX_DISTANCE
:
1498 CheckValue(values
[0] >= T
{0});
1500 Source
->MaxDistance
= static_cast<float>(values
[0]);
1501 return CommitAndUpdateSourceProps(Source
, Context
);
1503 case AL_ROLLOFF_FACTOR
:
1505 CheckValue(values
[0] >= T
{0});
1507 Source
->RolloffFactor
= static_cast<float>(values
[0]);
1508 return CommitAndUpdateSourceProps(Source
, Context
);
1510 case AL_REFERENCE_DISTANCE
:
1512 CheckValue(values
[0] >= T
{0});
1514 Source
->RefDistance
= static_cast<float>(values
[0]);
1515 return CommitAndUpdateSourceProps(Source
, Context
);
1519 CheckValue(values
[0] >= T
{0});
1521 Source
->MinGain
= static_cast<float>(values
[0]);
1522 return UpdateSourceProps(Source
, Context
);
1526 CheckValue(values
[0] >= T
{0});
1528 Source
->MaxGain
= static_cast<float>(values
[0]);
1529 return UpdateSourceProps(Source
, Context
);
1531 case AL_CONE_OUTER_GAIN
:
1533 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1535 Source
->OuterGain
= static_cast<float>(values
[0]);
1536 return UpdateSourceProps(Source
, Context
);
1538 case AL_CONE_OUTER_GAINHF
:
1540 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1542 Source
->OuterGainHF
= static_cast<float>(values
[0]);
1543 return UpdateSourceProps(Source
, Context
);
1545 case AL_AIR_ABSORPTION_FACTOR
:
1547 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{10});
1549 Source
->AirAbsorptionFactor
= static_cast<float>(values
[0]);
1550 return UpdateSourceProps(Source
, Context
);
1552 case AL_ROOM_ROLLOFF_FACTOR
:
1554 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1556 Source
->RoomRolloffFactor
= static_cast<float>(values
[0]);
1557 return UpdateSourceProps(Source
, Context
);
1559 case AL_DOPPLER_FACTOR
:
1561 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1563 Source
->DopplerFactor
= static_cast<float>(values
[0]);
1564 return UpdateSourceProps(Source
, Context
);
1567 case AL_SOURCE_RELATIVE
:
1568 if constexpr(std::is_integral_v
<T
>)
1571 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1573 Source
->HeadRelative
= values
[0] != AL_FALSE
;
1574 return CommitAndUpdateSourceProps(Source
, Context
);
1579 if constexpr(std::is_integral_v
<T
>)
1582 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1584 Source
->Looping
= values
[0] != AL_FALSE
;
1585 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1588 voice
->mLoopBuffer
.store(&Source
->mQueue
.front(), std::memory_order_release
);
1590 voice
->mLoopBuffer
.store(nullptr, std::memory_order_release
);
1592 /* If the source is playing, wait for the current mix to finish
1593 * to ensure it isn't currently looping back or reaching the
1596 std::ignore
= device
->waitForMix();
1603 if constexpr(std::is_integral_v
<T
>)
1606 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1607 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1608 throw al::context_error
{AL_INVALID_OPERATION
,
1609 "Setting buffer on playing or paused source %u", Source
->id
};
1611 std::deque
<ALbufferQueueItem
> oldlist
;
1614 using UT
= std::make_unsigned_t
<T
>;
1615 std::lock_guard
<std::mutex
> buflock
{device
->BufferLock
};
1616 ALbuffer
*buffer
{LookupBuffer(device
, static_cast<UT
>(values
[0]))};
1618 throw al::context_error
{AL_INVALID_VALUE
, "Invalid buffer ID %s",
1619 std::to_string(values
[0]).c_str()};
1620 if(buffer
->MappedAccess
&& !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
1621 throw al::context_error
{AL_INVALID_OPERATION
,
1622 "Setting non-persistently mapped buffer %u", buffer
->id
};
1623 if(buffer
->mCallback
&& buffer
->ref
.load(std::memory_order_relaxed
) != 0)
1624 throw al::context_error
{AL_INVALID_OPERATION
,
1625 "Setting already-set callback buffer %u", buffer
->id
};
1627 /* Add the selected buffer to a one-item queue */
1628 std::deque
<ALbufferQueueItem
> newlist
;
1629 newlist
.emplace_back();
1630 newlist
.back().mCallback
= buffer
->mCallback
;
1631 newlist
.back().mUserData
= buffer
->mUserData
;
1632 newlist
.back().mBlockAlign
= buffer
->mBlockAlign
;
1633 newlist
.back().mSampleLen
= buffer
->mSampleLen
;
1634 newlist
.back().mLoopStart
= buffer
->mLoopStart
;
1635 newlist
.back().mLoopEnd
= buffer
->mLoopEnd
;
1636 newlist
.back().mSamples
= buffer
->mData
;
1637 newlist
.back().mBuffer
= buffer
;
1638 IncrementRef(buffer
->ref
);
1640 /* Source is now Static */
1641 Source
->SourceType
= AL_STATIC
;
1642 Source
->mQueue
.swap(oldlist
);
1643 Source
->mQueue
.swap(newlist
);
1647 /* Source is now Undetermined */
1648 Source
->SourceType
= AL_UNDETERMINED
;
1649 Source
->mQueue
.swap(oldlist
);
1652 /* Delete all elements in the previous queue */
1653 for(auto &item
: oldlist
)
1655 if(ALbuffer
*buffer
{item
.mBuffer
})
1656 DecrementRef(buffer
->ref
);
1664 case AL_SAMPLE_OFFSET
:
1665 case AL_BYTE_OFFSET
:
1667 if constexpr(std::is_floating_point_v
<T
>)
1668 CheckValue(std::isfinite(values
[0]));
1670 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1672 auto vpos
= GetSampleOffset(Source
->mQueue
, prop
, static_cast<double>(values
[0]));
1673 if(!vpos
) throw al::context_error
{AL_INVALID_VALUE
, "Invalid offset"};
1675 if(SetVoiceOffset(voice
, *vpos
, Source
, Context
, Context
->mALDevice
.get()))
1678 Source
->OffsetType
= prop
;
1679 Source
->Offset
= static_cast<double>(values
[0]);
1682 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1683 if(sBufferSubDataCompat
)
1685 if constexpr(std::is_integral_v
<T
>)
1688 throw al::context_error
{AL_INVALID_OPERATION
,
1689 "Setting read-only source property 0x%04x", prop
};
1694 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1695 if(sBufferSubDataCompat
)
1697 if constexpr(std::is_integral_v
<T
>)
1700 throw al::context_error
{AL_INVALID_OPERATION
,
1701 "Setting read-only source property 0x%04x", prop
};
1706 if constexpr(std::is_floating_point_v
<T
>)
1707 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1709 CheckValue(values
[0] >= T
{0});
1711 Source
->Radius
= static_cast<float>(values
[0]);
1712 return UpdateSourceProps(Source
, Context
);
1714 case AL_SUPER_STEREO_WIDTH_SOFT
:
1716 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1718 Source
->EnhWidth
= static_cast<float>(values
[0]);
1719 return UpdateSourceProps(Source
, Context
);
1721 case AL_PANNING_ENABLED_SOFT
:
1723 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1724 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1725 throw al::context_error
{AL_INVALID_OPERATION
,
1726 "Modifying panning enabled on playing or paused source %u", Source
->id
};
1728 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1730 Source
->mPanningEnabled
= values
[0] != AL_FALSE
;
1731 return UpdateSourceProps(Source
, Context
);
1735 CheckValue(values
[0] >= T
{-1} && values
[0] <= T
{1});
1737 Source
->mPan
= static_cast<float>(values
[0]);
1738 return UpdateSourceProps(Source
, Context
);
1740 case AL_STEREO_ANGLES
:
1742 if constexpr(std::is_floating_point_v
<T
>)
1743 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1744 && std::isfinite(static_cast<float>(values
[1])));
1746 Source
->StereoPan
[0] = static_cast<float>(values
[0]);
1747 Source
->StereoPan
[1] = static_cast<float>(values
[1]);
1748 return UpdateSourceProps(Source
, Context
);
1753 if constexpr(std::is_floating_point_v
<T
>)
1754 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1755 && std::isfinite(static_cast<float>(values
[1]))
1756 && std::isfinite(static_cast<float>(values
[2])));
1758 Source
->Position
[0] = static_cast<float>(values
[0]);
1759 Source
->Position
[1] = static_cast<float>(values
[1]);
1760 Source
->Position
[2] = static_cast<float>(values
[2]);
1761 return CommitAndUpdateSourceProps(Source
, Context
);
1765 if constexpr(std::is_floating_point_v
<T
>)
1766 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1767 && std::isfinite(static_cast<float>(values
[1]))
1768 && std::isfinite(static_cast<float>(values
[2])));
1770 Source
->Velocity
[0] = static_cast<float>(values
[0]);
1771 Source
->Velocity
[1] = static_cast<float>(values
[1]);
1772 Source
->Velocity
[2] = static_cast<float>(values
[2]);
1773 return CommitAndUpdateSourceProps(Source
, Context
);
1777 if constexpr(std::is_floating_point_v
<T
>)
1778 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1779 && std::isfinite(static_cast<float>(values
[1]))
1780 && std::isfinite(static_cast<float>(values
[2])));
1782 Source
->Direction
[0] = static_cast<float>(values
[0]);
1783 Source
->Direction
[1] = static_cast<float>(values
[1]);
1784 Source
->Direction
[2] = static_cast<float>(values
[2]);
1785 return CommitAndUpdateSourceProps(Source
, Context
);
1787 case AL_ORIENTATION
:
1789 if constexpr(std::is_floating_point_v
<T
>)
1790 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1791 && std::isfinite(static_cast<float>(values
[1]))
1792 && std::isfinite(static_cast<float>(values
[2]))
1793 && std::isfinite(static_cast<float>(values
[3]))
1794 && std::isfinite(static_cast<float>(values
[4]))
1795 && std::isfinite(static_cast<float>(values
[5])));
1797 Source
->OrientAt
[0] = static_cast<float>(values
[0]);
1798 Source
->OrientAt
[1] = static_cast<float>(values
[1]);
1799 Source
->OrientAt
[2] = static_cast<float>(values
[2]);
1800 Source
->OrientUp
[0] = static_cast<float>(values
[3]);
1801 Source
->OrientUp
[1] = static_cast<float>(values
[4]);
1802 Source
->OrientUp
[2] = static_cast<float>(values
[5]);
1803 return UpdateSourceProps(Source
, Context
);
1806 case AL_DIRECT_FILTER
:
1807 if constexpr(std::is_integral_v
<T
>)
1810 const auto filterid
= static_cast<std::make_unsigned_t
<T
>>(values
[0]);
1813 std::lock_guard
<std::mutex
> filterlock
{device
->FilterLock
};
1814 ALfilter
*filter
{LookupFilter(device
, filterid
)};
1816 throw al::context_error
{AL_INVALID_VALUE
, "Invalid filter ID %s",
1817 std::to_string(filterid
).c_str()};
1818 Source
->Direct
.Gain
= filter
->Gain
;
1819 Source
->Direct
.GainHF
= filter
->GainHF
;
1820 Source
->Direct
.HFReference
= filter
->HFReference
;
1821 Source
->Direct
.GainLF
= filter
->GainLF
;
1822 Source
->Direct
.LFReference
= filter
->LFReference
;
1826 Source
->Direct
.Gain
= 1.0f
;
1827 Source
->Direct
.GainHF
= 1.0f
;
1828 Source
->Direct
.HFReference
= LowPassFreqRef
;
1829 Source
->Direct
.GainLF
= 1.0f
;
1830 Source
->Direct
.LFReference
= HighPassFreqRef
;
1832 return UpdateSourceProps(Source
, Context
);
1836 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1837 if constexpr(std::is_integral_v
<T
>)
1840 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1842 Source
->DryGainHFAuto
= values
[0] != AL_FALSE
;
1843 return UpdateSourceProps(Source
, Context
);
1847 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1848 if constexpr(std::is_integral_v
<T
>)
1851 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1853 Source
->WetGainAuto
= values
[0] != AL_FALSE
;
1854 return UpdateSourceProps(Source
, Context
);
1858 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1859 if constexpr(std::is_integral_v
<T
>)
1862 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1864 Source
->WetGainHFAuto
= values
[0] != AL_FALSE
;
1865 return UpdateSourceProps(Source
, Context
);
1869 case AL_DIRECT_CHANNELS_SOFT
:
1870 if constexpr(std::is_integral_v
<T
>)
1873 if(auto mode
= DirectModeFromEnum(values
[0]))
1875 Source
->DirectChannels
= *mode
;
1876 return UpdateSourceProps(Source
, Context
);
1878 throw al::context_error
{AL_INVALID_VALUE
, "Invalid direct channels mode: %s\n",
1879 HexPrinter
{values
[0]}.c_str()};
1883 case AL_DISTANCE_MODEL
:
1884 if constexpr(std::is_integral_v
<T
>)
1887 if(auto model
= DistanceModelFromALenum(values
[0]))
1889 Source
->mDistanceModel
= *model
;
1890 if(Context
->mSourceDistanceModel
)
1891 UpdateSourceProps(Source
, Context
);
1894 throw al::context_error
{AL_INVALID_VALUE
, "Invalid distance model: %s\n",
1895 HexPrinter
{values
[0]}.c_str()};
1899 case AL_SOURCE_RESAMPLER_SOFT
:
1900 if constexpr(std::is_integral_v
<T
>)
1903 CheckValue(values
[0] >= 0 && values
[0] <= static_cast<int>(Resampler::Max
));
1905 Source
->mResampler
= static_cast<Resampler
>(values
[0]);
1906 return UpdateSourceProps(Source
, Context
);
1910 case AL_SOURCE_SPATIALIZE_SOFT
:
1911 if constexpr(std::is_integral_v
<T
>)
1914 if(auto mode
= SpatializeModeFromEnum(values
[0]))
1916 Source
->mSpatialize
= *mode
;
1917 return UpdateSourceProps(Source
, Context
);
1919 throw al::context_error
{AL_INVALID_VALUE
, "Invalid source spatialize mode: %s\n",
1920 HexPrinter
{values
[0]}.c_str()};
1924 case AL_STEREO_MODE_SOFT
:
1925 if constexpr(std::is_integral_v
<T
>)
1928 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1929 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1930 throw al::context_error
{AL_INVALID_OPERATION
,
1931 "Modifying stereo mode on playing or paused source %u", Source
->id
};
1933 if(auto mode
= StereoModeFromEnum(values
[0]))
1935 Source
->mStereoMode
= *mode
;
1938 throw al::context_error
{AL_INVALID_VALUE
, "Invalid stereo mode: %s\n",
1939 HexPrinter
{values
[0]}.c_str()};
1943 case AL_AUXILIARY_SEND_FILTER
:
1944 if constexpr(std::is_integral_v
<T
>)
1947 const auto slotid
= static_cast<std::make_unsigned_t
<T
>>(values
[0]);
1948 const auto sendidx
= static_cast<std::make_unsigned_t
<T
>>(values
[1]);
1949 const auto filterid
= static_cast<std::make_unsigned_t
<T
>>(values
[2]);
1951 std::unique_lock slotlock
{Context
->mEffectSlotLock
};
1952 ALeffectslot
*slot
{};
1955 slot
= LookupEffectSlot(Context
, slotid
);
1957 throw al::context_error
{AL_INVALID_VALUE
, "Invalid effect ID %s",
1958 std::to_string(slotid
).c_str()};
1961 if(sendidx
>= device
->NumAuxSends
)
1962 throw al::context_error
{AL_INVALID_VALUE
, "Invalid send %s",
1963 std::to_string(sendidx
).c_str()};
1964 auto &send
= Source
->Send
[static_cast<size_t>(sendidx
)];
1968 std::lock_guard
<std::mutex
> filterlock
{device
->FilterLock
};
1969 ALfilter
*filter
{LookupFilter(device
, filterid
)};
1971 throw al::context_error
{AL_INVALID_VALUE
, "Invalid filter ID %s",
1972 std::to_string(filterid
).c_str()};
1974 send
.Gain
= filter
->Gain
;
1975 send
.GainHF
= filter
->GainHF
;
1976 send
.HFReference
= filter
->HFReference
;
1977 send
.GainLF
= filter
->GainLF
;
1978 send
.LFReference
= filter
->LFReference
;
1982 /* Disable filter */
1985 send
.HFReference
= LowPassFreqRef
;
1987 send
.LFReference
= HighPassFreqRef
;
1990 /* We must force an update if the current auxiliary slot is valid
1991 * and about to be changed on an active source, in case the old
1992 * slot is about to be deleted.
1994 if(send
.Slot
&& slot
!= send
.Slot
&& IsPlayingOrPaused(Source
))
1996 /* Add refcount on the new slot, and release the previous slot */
1997 if(slot
) IncrementRef(slot
->ref
);
1998 if(auto *oldslot
= send
.Slot
)
1999 DecrementRef(oldslot
->ref
);
2002 Voice
*voice
{GetSourceVoice(Source
, Context
)};
2003 if(voice
) UpdateSourceProps(Source
, voice
, Context
);
2004 else Source
->mPropsDirty
= true;
2008 if(slot
) IncrementRef(slot
->ref
);
2009 if(auto *oldslot
= send
.Slot
)
2010 DecrementRef(oldslot
->ref
);
2012 UpdateSourceProps(Source
, Context
);
2019 ERR("Unexpected %s property: 0x%04x\n", PropType
<T
>::Name(), prop
);
2020 throw al::context_error
{AL_INVALID_ENUM
, "Invalid source %s property 0x%04x",
2021 PropType
<T
>::Name(), prop
};
2025 template<typename T
, size_t N
>
2026 auto GetSizeChecker(const SourceProp prop
, const al::span
<T
,N
> values
)
2028 return [=](size_t expect
) -> void
2030 if(values
.size() == expect
) LIKELY
return;
2031 throw al::context_error
{AL_INVALID_ENUM
, "Property 0x%04x expects %zu value(s), got %zu",
2032 prop
, expect
, values
.size()};
2036 template<typename T
>
2037 NOINLINE
void GetProperty(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
2038 const al::span
<T
> values
)
2040 using std::chrono::duration_cast
;
2041 auto CheckSize
= GetSizeChecker(prop
, values
);
2042 ALCdevice
*device
{Context
->mALDevice
.get()};
2048 values
[0] = static_cast<T
>(Source
->Gain
);
2053 values
[0] = static_cast<T
>(Source
->Pitch
);
2056 case AL_MAX_DISTANCE
:
2058 values
[0] = static_cast<T
>(Source
->MaxDistance
);
2061 case AL_ROLLOFF_FACTOR
:
2063 values
[0] = static_cast<T
>(Source
->RolloffFactor
);
2066 case AL_REFERENCE_DISTANCE
:
2068 values
[0] = static_cast<T
>(Source
->RefDistance
);
2071 case AL_CONE_INNER_ANGLE
:
2073 values
[0] = static_cast<T
>(Source
->InnerAngle
);
2076 case AL_CONE_OUTER_ANGLE
:
2078 values
[0] = static_cast<T
>(Source
->OuterAngle
);
2083 values
[0] = static_cast<T
>(Source
->MinGain
);
2088 values
[0] = static_cast<T
>(Source
->MaxGain
);
2091 case AL_CONE_OUTER_GAIN
:
2093 values
[0] = static_cast<T
>(Source
->OuterGain
);
2097 case AL_SAMPLE_OFFSET
:
2098 case AL_BYTE_OFFSET
:
2100 values
[0] = GetSourceOffset
<T
>(Source
, prop
, Context
);
2103 case AL_CONE_OUTER_GAINHF
:
2105 values
[0] = static_cast<T
>(Source
->OuterGainHF
);
2108 case AL_AIR_ABSORPTION_FACTOR
:
2110 values
[0] = static_cast<T
>(Source
->AirAbsorptionFactor
);
2113 case AL_ROOM_ROLLOFF_FACTOR
:
2115 values
[0] = static_cast<T
>(Source
->RoomRolloffFactor
);
2118 case AL_DOPPLER_FACTOR
:
2120 values
[0] = static_cast<T
>(Source
->DopplerFactor
);
2123 case AL_SAMPLE_RW_OFFSETS_SOFT
:
2124 if constexpr(std::is_integral_v
<T
>)
2126 if(sBufferSubDataCompat
)
2129 values
[0] = GetSourceOffset
<T
>(Source
, AL_SAMPLE_OFFSET
, Context
);
2130 /* FIXME: values[1] should be ahead of values[0] by the device
2131 * update time. It needs to clamp or wrap the length of the
2134 values
[1] = values
[0];
2139 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
2140 if constexpr(std::is_floating_point_v
<T
>)
2142 if(sBufferSubDataCompat
)
2146 values
[0] = static_cast<T
>(Source
->Radius
);
2151 if(sBufferSubDataCompat
)
2154 values
[0] = GetSourceOffset
<T
>(Source
, AL_BYTE_OFFSET
, Context
);
2155 /* FIXME: values[1] should be ahead of values[0] by the device
2156 * update time. It needs to clamp or wrap the length of the
2159 values
[1] = values
[0];
2165 case AL_SUPER_STEREO_WIDTH_SOFT
:
2167 values
[0] = static_cast<T
>(Source
->EnhWidth
);
2170 case AL_BYTE_LENGTH_SOFT
:
2171 case AL_SAMPLE_LENGTH_SOFT
:
2172 case AL_SEC_LENGTH_SOFT
:
2174 values
[0] = GetSourceLength
<T
>(Source
, prop
);
2177 case AL_PANNING_ENABLED_SOFT
:
2179 values
[0] = Source
->mPanningEnabled
;
2184 values
[0] = static_cast<T
>(Source
->mPan
);
2187 case AL_STEREO_ANGLES
:
2188 if constexpr(std::is_floating_point_v
<T
>)
2191 values
[0] = static_cast<T
>(Source
->StereoPan
[0]);
2192 values
[1] = static_cast<T
>(Source
->StereoPan
[1]);
2197 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
2198 if constexpr(std::is_same_v
<T
,int64_t>)
2201 /* Get the source offset with the clock time first. Then get the
2202 * clock time with the device latency. Order is important.
2204 ClockLatency clocktime
{};
2205 nanoseconds srcclock
{};
2206 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2208 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2209 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2211 if(srcclock
== clocktime
.ClockTime
)
2212 values
[1] = nanoseconds
{clocktime
.Latency
}.count();
2215 /* If the clock time incremented, reduce the latency by that
2216 * much since it's that much closer to the source offset it got
2219 const auto diff
= std::min(clocktime
.Latency
, clocktime
.ClockTime
-srcclock
);
2220 values
[1] = nanoseconds
{clocktime
.Latency
- diff
}.count();
2226 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
2227 if constexpr(std::is_same_v
<T
,int64_t>)
2230 nanoseconds srcclock
{};
2231 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2232 values
[1] = srcclock
.count();
2237 case AL_SEC_OFFSET_LATENCY_SOFT
:
2238 if constexpr(std::is_same_v
<T
,double>)
2241 /* Get the source offset with the clock time first. Then get the
2242 * clock time with the device latency. Order is important.
2244 ClockLatency clocktime
{};
2245 nanoseconds srcclock
{};
2246 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2248 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2249 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2251 if(srcclock
== clocktime
.ClockTime
)
2252 values
[1] = duration_cast
<seconds_d
>(clocktime
.Latency
).count();
2255 /* If the clock time incremented, reduce the latency by that
2256 * much since it's that much closer to the source offset it got
2259 const auto diff
= std::min(clocktime
.Latency
, clocktime
.ClockTime
-srcclock
);
2260 values
[1] = duration_cast
<seconds_d
>(clocktime
.Latency
- diff
).count();
2266 case AL_SEC_OFFSET_CLOCK_SOFT
:
2267 if constexpr(std::is_same_v
<T
,double>)
2270 nanoseconds srcclock
{};
2271 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2272 values
[1] = duration_cast
<seconds_d
>(srcclock
).count();
2279 values
[0] = static_cast<T
>(Source
->Position
[0]);
2280 values
[1] = static_cast<T
>(Source
->Position
[1]);
2281 values
[2] = static_cast<T
>(Source
->Position
[2]);
2286 values
[0] = static_cast<T
>(Source
->Velocity
[0]);
2287 values
[1] = static_cast<T
>(Source
->Velocity
[1]);
2288 values
[2] = static_cast<T
>(Source
->Velocity
[2]);
2293 values
[0] = static_cast<T
>(Source
->Direction
[0]);
2294 values
[1] = static_cast<T
>(Source
->Direction
[1]);
2295 values
[2] = static_cast<T
>(Source
->Direction
[2]);
2298 case AL_ORIENTATION
:
2300 values
[0] = static_cast<T
>(Source
->OrientAt
[0]);
2301 values
[1] = static_cast<T
>(Source
->OrientAt
[1]);
2302 values
[2] = static_cast<T
>(Source
->OrientAt
[2]);
2303 values
[3] = static_cast<T
>(Source
->OrientUp
[0]);
2304 values
[4] = static_cast<T
>(Source
->OrientUp
[1]);
2305 values
[5] = static_cast<T
>(Source
->OrientUp
[2]);
2309 case AL_SOURCE_RELATIVE
:
2310 if constexpr(std::is_integral_v
<T
>)
2313 values
[0] = Source
->HeadRelative
;
2319 if constexpr(std::is_integral_v
<T
>)
2322 values
[0] = Source
->Looping
;
2328 if constexpr(std::is_integral_v
<T
>)
2331 ALbufferQueueItem
*BufferList
{};
2332 /* HACK: This query should technically only return the buffer set
2333 * on a static source. However, some apps had used it to detect
2334 * when a streaming source changed buffers, so report the current
2335 * buffer's ID when playing.
2337 if(Source
->SourceType
== AL_STATIC
|| Source
->state
== AL_INITIAL
)
2339 if(!Source
->mQueue
.empty())
2340 BufferList
= &Source
->mQueue
.front();
2342 else if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2344 VoiceBufferItem
*Current
{voice
->mCurrentBuffer
.load(std::memory_order_relaxed
)};
2345 BufferList
= static_cast<ALbufferQueueItem
*>(Current
);
2347 ALbuffer
*buffer
{BufferList
? BufferList
->mBuffer
: nullptr};
2348 values
[0] = buffer
? static_cast<T
>(buffer
->id
) : T
{0};
2353 case AL_SOURCE_STATE
:
2354 if constexpr(std::is_integral_v
<T
>)
2357 values
[0] = GetSourceState(Source
, GetSourceVoice(Source
, Context
));
2362 case AL_BUFFERS_QUEUED
:
2363 if constexpr(std::is_integral_v
<T
>)
2366 values
[0] = static_cast<T
>(Source
->mQueue
.size());
2371 case AL_BUFFERS_PROCESSED
:
2372 if constexpr(std::is_integral_v
<T
>)
2375 if(Source
->Looping
|| Source
->SourceType
!= AL_STREAMING
)
2377 /* Buffers on a looping source are in a perpetual state of
2378 * PENDING, so don't report any as PROCESSED
2385 if(Source
->state
!= AL_INITIAL
)
2387 const VoiceBufferItem
*Current
{nullptr};
2388 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2389 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
2390 for(auto &item
: Source
->mQueue
)
2392 if(&item
== Current
)
2403 case AL_SOURCE_TYPE
:
2404 if constexpr(std::is_integral_v
<T
>)
2407 values
[0] = Source
->SourceType
;
2412 case AL_DIRECT_FILTER_GAINHF_AUTO
:
2413 if constexpr(std::is_integral_v
<T
>)
2416 values
[0] = Source
->DryGainHFAuto
;
2421 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
2422 if constexpr(std::is_integral_v
<T
>)
2425 values
[0] = Source
->WetGainAuto
;
2430 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
2431 if constexpr(std::is_integral_v
<T
>)
2434 values
[0] = Source
->WetGainHFAuto
;
2439 case AL_DIRECT_CHANNELS_SOFT
:
2440 if constexpr(std::is_integral_v
<T
>)
2443 values
[0] = EnumFromDirectMode(Source
->DirectChannels
);
2448 case AL_DISTANCE_MODEL
:
2449 if constexpr(std::is_integral_v
<T
>)
2452 values
[0] = ALenumFromDistanceModel(Source
->mDistanceModel
);
2457 case AL_SOURCE_RESAMPLER_SOFT
:
2458 if constexpr(std::is_integral_v
<T
>)
2461 values
[0] = static_cast<T
>(Source
->mResampler
);
2466 case AL_SOURCE_SPATIALIZE_SOFT
:
2467 if constexpr(std::is_integral_v
<T
>)
2470 values
[0] = EnumFromSpatializeMode(Source
->mSpatialize
);
2475 case AL_STEREO_MODE_SOFT
:
2476 if constexpr(std::is_integral_v
<T
>)
2479 values
[0] = EnumFromStereoMode(Source
->mStereoMode
);
2484 case AL_DIRECT_FILTER
:
2485 case AL_AUXILIARY_SEND_FILTER
:
2489 ERR("Unexpected %s query property: 0x%04x\n", PropType
<T
>::Name(), prop
);
2490 throw al::context_error
{AL_INVALID_ENUM
, "Invalid source %s query property 0x%04x",
2491 PropType
<T
>::Name(), prop
};
2495 void StartSources(ALCcontext
*const context
, const al::span
<ALsource
*> srchandles
,
2496 const nanoseconds start_time
=nanoseconds::min())
2498 ALCdevice
*device
{context
->mALDevice
.get()};
2499 /* If the device is disconnected, and voices stop on disconnect, go right
2502 if(!device
->Connected
.load(std::memory_order_acquire
)) UNLIKELY
2504 if(context
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
2506 for(ALsource
*source
: srchandles
)
2508 /* TODO: Send state change event? */
2509 source
->Offset
= 0.0;
2510 source
->OffsetType
= AL_NONE
;
2511 source
->state
= AL_STOPPED
;
2517 /* Count the number of reusable voices. */
2518 auto voicelist
= context
->getVoicesSpan();
2519 size_t free_voices
{0};
2520 for(const Voice
*voice
: voicelist
)
2522 free_voices
+= (voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2523 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2524 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false);
2525 if(free_voices
== srchandles
.size())
2528 if(srchandles
.size() != free_voices
) UNLIKELY
2530 const size_t inc_amount
{srchandles
.size() - free_voices
};
2531 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
2532 if(inc_amount
> allvoices
.size() - voicelist
.size())
2534 /* Increase the number of voices to handle the request. */
2535 context
->allocVoices(inc_amount
- (allvoices
.size() - voicelist
.size()));
2537 context
->mActiveVoiceCount
.fetch_add(inc_amount
, std::memory_order_release
);
2538 voicelist
= context
->getVoicesSpan();
2541 auto voiceiter
= voicelist
.begin();
2543 VoiceChange
*tail
{}, *cur
{};
2544 for(ALsource
*source
: srchandles
)
2546 /* Check that there is a queue containing at least one valid, non zero
2549 auto find_buffer
= [](ALbufferQueueItem
&entry
) noexcept
2550 { return entry
.mSampleLen
!= 0 || entry
.mCallback
!= nullptr; };
2551 auto BufferList
= std::find_if(source
->mQueue
.begin(), source
->mQueue
.end(), find_buffer
);
2553 /* If there's nothing to play, go right to stopped. */
2554 if(BufferList
== source
->mQueue
.end()) UNLIKELY
2556 /* NOTE: A source without any playable buffers should not have a
2557 * Voice since it shouldn't be in a playing or paused state. So
2558 * there's no need to look up its voice and clear the source.
2560 source
->Offset
= 0.0;
2561 source
->OffsetType
= AL_NONE
;
2562 source
->state
= AL_STOPPED
;
2567 cur
= tail
= GetVoiceChanger(context
);
2570 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
2571 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
2574 Voice
*voice
{GetSourceVoice(source
, context
)};
2575 switch(GetSourceState(source
, voice
))
2578 /* A source that's paused simply resumes. If there's no voice, it
2579 * was lost from a disconnect, so just start over with a new one.
2581 cur
->mOldVoice
= nullptr;
2583 cur
->mVoice
= voice
;
2584 cur
->mSourceID
= source
->id
;
2585 cur
->mState
= VChangeState::Play
;
2586 source
->state
= AL_PLAYING
;
2588 if(context
->hasEax())
2589 source
->eaxCommit();
2590 #endif // ALSOFT_EAX
2594 /* A source that's already playing is restarted from the beginning.
2595 * Stop the current voice and start a new one so it properly cross-
2596 * fades back to the beginning.
2599 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
2600 cur
->mOldVoice
= voice
;
2605 assert(voice
== nullptr);
2606 cur
->mOldVoice
= nullptr;
2608 if(context
->hasEax())
2609 source
->eaxCommit();
2610 #endif // ALSOFT_EAX
2614 /* Find the next unused voice to play this source with. */
2615 for(;voiceiter
!= voicelist
.end();++voiceiter
,++vidx
)
2617 Voice
*v
{*voiceiter
};
2618 if(v
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2619 && v
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2620 && v
->mPendingChange
.load(std::memory_order_relaxed
) == false)
2626 ASSUME(voice
!= nullptr);
2628 voice
->mPosition
.store(0, std::memory_order_relaxed
);
2629 voice
->mPositionFrac
.store(0, std::memory_order_relaxed
);
2630 voice
->mCurrentBuffer
.store(&source
->mQueue
.front(), std::memory_order_relaxed
);
2631 voice
->mStartTime
= start_time
;
2632 voice
->mFlags
.reset();
2633 /* A source that's not playing or paused has any offset applied when it
2636 if(const ALenum offsettype
{source
->OffsetType
})
2638 const double offset
{source
->Offset
};
2639 source
->OffsetType
= AL_NONE
;
2640 source
->Offset
= 0.0;
2641 if(auto vpos
= GetSampleOffset(source
->mQueue
, offsettype
, offset
))
2643 voice
->mPosition
.store(vpos
->pos
, std::memory_order_relaxed
);
2644 voice
->mPositionFrac
.store(vpos
->frac
, std::memory_order_relaxed
);
2645 voice
->mCurrentBuffer
.store(vpos
->bufferitem
, std::memory_order_relaxed
);
2646 if(vpos
->pos
> 0 || (vpos
->pos
== 0 && vpos
->frac
> 0)
2647 || vpos
->bufferitem
!= &source
->mQueue
.front())
2648 voice
->mFlags
.set(VoiceIsFading
);
2651 InitVoice(voice
, source
, al::to_address(BufferList
), context
, device
);
2653 source
->VoiceIdx
= vidx
;
2654 source
->state
= AL_PLAYING
;
2656 cur
->mVoice
= voice
;
2657 cur
->mSourceID
= source
->id
;
2658 cur
->mState
= VChangeState::Play
;
2661 SendVoiceChanges(context
, tail
);
2666 AL_API
DECL_FUNC2(void, alGenSources
, ALsizei
,n
, ALuint
*,sources
)
2667 FORCE_ALIGN
void AL_APIENTRY
alGenSourcesDirect(ALCcontext
*context
, ALsizei n
, ALuint
*sources
) noexcept
2670 throw al::context_error
{AL_INVALID_VALUE
, "Generating %d sources", n
};
2671 if(n
<= 0) UNLIKELY
return;
2673 std::unique_lock
<std::mutex
> srclock
{context
->mSourceLock
};
2674 ALCdevice
*device
{context
->mALDevice
.get()};
2676 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2677 if(sids
.size() > device
->SourcesMax
-context
->mNumSources
)
2678 throw al::context_error
{AL_OUT_OF_MEMORY
, "Exceeding %u source limit (%u + %d)",
2679 device
->SourcesMax
, context
->mNumSources
, n
};
2680 if(!EnsureSources(context
, sids
.size()))
2681 throw al::context_error
{AL_OUT_OF_MEMORY
, "Failed to allocate %d source%s", n
,
2682 (n
== 1) ? "" : "s"};
2684 std::generate(sids
.begin(), sids
.end(), [context
]{ return AllocSource(context
)->id
; });
2686 catch(al::context_error
& e
) {
2687 context
->setError(e
.errorCode(), "%s", e
.what());
2690 AL_API
DECL_FUNC2(void, alDeleteSources
, ALsizei
,n
, const ALuint
*,sources
)
2691 FORCE_ALIGN
void AL_APIENTRY
alDeleteSourcesDirect(ALCcontext
*context
, ALsizei n
,
2692 const ALuint
*sources
) noexcept
2695 throw al::context_error
{AL_INVALID_VALUE
, "Deleting %d sources", n
};
2696 if(n
<= 0) UNLIKELY
return;
2698 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2700 /* Check that all Sources are valid */
2701 auto validate_source
= [context
](const ALuint sid
) -> bool
2702 { return LookupSource(context
, sid
) != nullptr; };
2704 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2705 auto invsrc
= std::find_if_not(sids
.begin(), sids
.end(), validate_source
);
2706 if(invsrc
!= sids
.end())
2707 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", *invsrc
};
2709 /* All good. Delete source IDs. */
2710 auto delete_source
= [&context
](const ALuint sid
) -> void
2712 if(ALsource
*src
{LookupSource(context
, sid
)})
2713 FreeSource(context
, src
);
2715 std::for_each(sids
.begin(), sids
.end(), delete_source
);
2717 catch(al::context_error
& e
) {
2718 context
->setError(e
.errorCode(), "%s", e
.what());
2721 AL_API
DECL_FUNC1(ALboolean
, alIsSource
, ALuint
,source
)
2722 FORCE_ALIGN ALboolean AL_APIENTRY
alIsSourceDirect(ALCcontext
*context
, ALuint source
) noexcept
2724 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2725 if(LookupSource(context
, source
) != nullptr)
2731 AL_API
DECL_FUNC3(void, alSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
,value
)
2732 FORCE_ALIGN
void AL_APIENTRY
alSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2733 ALfloat value
) noexcept
2735 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2736 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2737 ALsource
*Source
{LookupSource(context
, source
)};
2739 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2741 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2743 catch(al::context_error
& e
) {
2744 context
->setError(e
.errorCode(), "%s", e
.what());
2747 AL_API
DECL_FUNC5(void, alSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
,value1
, ALfloat
,value2
, ALfloat
,value3
)
2748 FORCE_ALIGN
void AL_APIENTRY
alSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2749 ALfloat value1
, ALfloat value2
, ALfloat value3
) noexcept
2751 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2752 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2753 ALsource
*Source
{LookupSource(context
, source
)};
2755 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2757 const std::array fvals
{value1
, value2
, value3
};
2758 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
2760 catch(al::context_error
& e
) {
2761 context
->setError(e
.errorCode(), "%s", e
.what());
2764 AL_API
DECL_FUNC3(void, alSourcefv
, ALuint
,source
, ALenum
,param
, const ALfloat
*,values
)
2765 FORCE_ALIGN
void AL_APIENTRY
alSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2766 const ALfloat
*values
) noexcept
2768 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2769 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2770 ALsource
*Source
{LookupSource(context
, source
)};
2772 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2774 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2776 const ALuint count
{FloatValsByProp(param
)};
2777 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2779 catch(al::context_error
& e
) {
2780 context
->setError(e
.errorCode(), "%s", e
.what());
2784 AL_API
DECL_FUNCEXT3(void, alSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value
)
2785 FORCE_ALIGN
void AL_APIENTRY
alSourcedDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2786 ALdouble value
) noexcept
2788 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2789 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2790 ALsource
*Source
{LookupSource(context
, source
)};
2792 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2794 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1});
2796 catch(al::context_error
& e
) {
2797 context
->setError(e
.errorCode(), "%s", e
.what());
2800 AL_API
DECL_FUNCEXT5(void, alSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value1
, ALdouble
,value2
, ALdouble
,value3
)
2801 FORCE_ALIGN
void AL_APIENTRY
alSource3dDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2802 ALdouble value1
, ALdouble value2
, ALdouble value3
) noexcept
2804 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2805 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2806 ALsource
*Source
{LookupSource(context
, source
)};
2808 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2810 const std::array dvals
{value1
, value2
, value3
};
2811 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
2813 catch(al::context_error
& e
) {
2814 context
->setError(e
.errorCode(), "%s", e
.what());
2817 AL_API
DECL_FUNCEXT3(void, alSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, const ALdouble
*,values
)
2818 FORCE_ALIGN
void AL_APIENTRY
alSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2819 const ALdouble
*values
) noexcept
2821 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2822 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2823 ALsource
*Source
{LookupSource(context
, source
)};
2825 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2827 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2829 const ALuint count
{DoubleValsByProp(param
)};
2830 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2832 catch(al::context_error
& e
) {
2833 context
->setError(e
.errorCode(), "%s", e
.what());
2837 AL_API
DECL_FUNC3(void, alSourcei
, ALuint
,source
, ALenum
,param
, ALint
,value
)
2838 FORCE_ALIGN
void AL_APIENTRY
alSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2839 ALint value
) noexcept
2841 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2842 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2843 ALsource
*Source
{LookupSource(context
, source
)};
2845 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2847 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2849 catch(al::context_error
& e
) {
2850 context
->setError(e
.errorCode(), "%s", e
.what());
2853 AL_API
DECL_FUNC5(void, alSource3i
, ALuint
,buffer
, ALenum
,param
, ALint
,value1
, ALint
,value2
, ALint
,value3
)
2854 FORCE_ALIGN
void AL_APIENTRY
alSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2855 ALint value1
, ALint value2
, ALint value3
) noexcept
2857 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2858 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2859 ALsource
*Source
{LookupSource(context
, source
)};
2861 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2863 const std::array ivals
{value1
, value2
, value3
};
2864 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
2866 catch(al::context_error
& e
) {
2867 context
->setError(e
.errorCode(), "%s", e
.what());
2870 AL_API
DECL_FUNC3(void, alSourceiv
, ALuint
,source
, ALenum
,param
, const ALint
*,values
)
2871 FORCE_ALIGN
void AL_APIENTRY
alSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2872 const ALint
*values
) noexcept
2874 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2875 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2876 ALsource
*Source
{LookupSource(context
, source
)};
2878 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2880 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2882 const ALuint count
{IntValsByProp(param
)};
2883 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2885 catch(al::context_error
& e
) {
2886 context
->setError(e
.errorCode(), "%s", e
.what());
2890 AL_API
DECL_FUNCEXT3(void, alSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value
)
2891 FORCE_ALIGN
void AL_APIENTRY
alSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
,
2892 ALenum param
, ALint64SOFT value
) noexcept
2894 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2895 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2896 ALsource
*Source
{LookupSource(context
, source
)};
2898 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2900 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2902 catch(al::context_error
& e
) {
2903 context
->setError(e
.errorCode(), "%s", e
.what());
2906 AL_API
DECL_FUNCEXT5(void, alSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value1
, ALint64SOFT
,value2
, ALint64SOFT
,value3
)
2907 FORCE_ALIGN
void AL_APIENTRY
alSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
2908 ALenum param
, ALint64SOFT value1
, ALint64SOFT value2
, ALint64SOFT value3
) noexcept
2910 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2911 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2912 ALsource
*Source
{LookupSource(context
, source
)};
2914 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2916 const std::array i64vals
{value1
, value2
, value3
};
2917 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
2919 catch(al::context_error
& e
) {
2920 context
->setError(e
.errorCode(), "%s", e
.what());
2923 AL_API
DECL_FUNCEXT3(void, alSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, const ALint64SOFT
*,values
)
2924 FORCE_ALIGN
void AL_APIENTRY
alSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
2925 ALenum param
, const ALint64SOFT
*values
) noexcept
2927 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2928 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2929 ALsource
*Source
{LookupSource(context
, source
)};
2931 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2933 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2935 const ALuint count
{Int64ValsByProp(param
)};
2936 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2938 catch(al::context_error
& e
) {
2939 context
->setError(e
.errorCode(), "%s", e
.what());
2943 AL_API
DECL_FUNC3(void, alGetSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
*,value
)
2944 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2945 ALfloat
*value
) noexcept
2947 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2948 ALsource
*Source
{LookupSource(context
, source
)};
2950 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2952 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2954 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
2956 catch(al::context_error
& e
) {
2957 context
->setError(e
.errorCode(), "%s", e
.what());
2960 AL_API
DECL_FUNC5(void, alGetSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
*,value1
, ALfloat
*,value2
, ALfloat
*,value3
)
2961 FORCE_ALIGN
void AL_APIENTRY
alGetSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2962 ALfloat
*value1
, ALfloat
*value2
, ALfloat
*value3
) noexcept
2964 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2965 ALsource
*Source
{LookupSource(context
, source
)};
2967 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2968 if(!(value1
&& value2
&& value3
))
2969 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2971 std::array
<float,3> fvals
{};
2972 GetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
2977 catch(al::context_error
& e
) {
2978 context
->setError(e
.errorCode(), "%s", e
.what());
2981 AL_API
DECL_FUNC3(void, alGetSourcefv
, ALuint
,source
, ALenum
,param
, ALfloat
*,values
)
2982 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2983 ALfloat
*values
) noexcept
2985 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2986 ALsource
*Source
{LookupSource(context
, source
)};
2988 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2990 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2992 const ALuint count
{FloatValsByProp(param
)};
2993 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2995 catch(al::context_error
& e
) {
2996 context
->setError(e
.errorCode(), "%s", e
.what());
3000 AL_API
DECL_FUNCEXT3(void, alGetSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value
)
3001 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedDirectSOFT(ALCcontext
*context
, ALuint source
,
3002 ALenum param
, ALdouble
*value
) noexcept
3004 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3005 ALsource
*Source
{LookupSource(context
, source
)};
3007 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3009 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3011 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3013 catch(al::context_error
& e
) {
3014 context
->setError(e
.errorCode(), "%s", e
.what());
3017 AL_API
DECL_FUNCEXT5(void, alGetSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value1
, ALdouble
*,value2
, ALdouble
*,value3
)
3018 FORCE_ALIGN
void AL_APIENTRY
alGetSource3dDirectSOFT(ALCcontext
*context
, ALuint source
,
3019 ALenum param
, ALdouble
*value1
, ALdouble
*value2
, ALdouble
*value3
) noexcept
3021 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3022 ALsource
*Source
{LookupSource(context
, source
)};
3024 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3025 if(!(value1
&& value2
&& value3
))
3026 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3028 std::array
<double,3> dvals
{};
3029 GetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
3034 catch(al::context_error
& e
) {
3035 context
->setError(e
.errorCode(), "%s", e
.what());
3038 AL_API
DECL_FUNCEXT3(void, alGetSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,values
)
3039 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
,
3040 ALenum param
, ALdouble
*values
) noexcept
3042 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3043 ALsource
*Source
{LookupSource(context
, source
)};
3045 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3047 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3049 const ALuint count
{DoubleValsByProp(param
)};
3050 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3052 catch(al::context_error
& e
) {
3053 context
->setError(e
.errorCode(), "%s", e
.what());
3057 AL_API
DECL_FUNC3(void, alGetSourcei
, ALuint
,source
, ALenum
,param
, ALint
*,value
)
3058 FORCE_ALIGN
void AL_APIENTRY
alGetSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3059 ALint
*value
) noexcept
3061 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3062 ALsource
*Source
{LookupSource(context
, source
)};
3064 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3066 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3068 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3070 catch(al::context_error
& e
) {
3071 context
->setError(e
.errorCode(), "%s", e
.what());
3074 AL_API
DECL_FUNC5(void, alGetSource3i
, ALuint
,source
, ALenum
,param
, ALint
*,value1
, ALint
*,value2
, ALint
*,value3
)
3075 FORCE_ALIGN
void AL_APIENTRY
alGetSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3076 ALint
*value1
, ALint
*value2
, ALint
*value3
) noexcept
3078 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3079 ALsource
*Source
{LookupSource(context
, source
)};
3081 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3082 if(!(value1
&& value2
&& value3
))
3083 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3085 std::array
<int,3> ivals
{};
3086 GetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
3091 catch(al::context_error
& e
) {
3092 context
->setError(e
.errorCode(), "%s", e
.what());
3095 AL_API
DECL_FUNC3(void, alGetSourceiv
, ALuint
,source
, ALenum
,param
, ALint
*,values
)
3096 FORCE_ALIGN
void AL_APIENTRY
alGetSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3097 ALint
*values
) noexcept
3099 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3100 ALsource
*Source
{LookupSource(context
, source
)};
3102 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3104 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3106 const ALuint count
{IntValsByProp(param
)};
3107 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3109 catch(al::context_error
& e
) {
3110 context
->setError(e
.errorCode(), "%s", e
.what());
3114 AL_API
DECL_FUNCEXT3(void, alGetSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value
)
3115 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
, ALint64SOFT
*value
) noexcept
3117 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3118 ALsource
*Source
{LookupSource(context
, source
)};
3120 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3122 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3124 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3126 catch(al::context_error
& e
) {
3127 context
->setError(e
.errorCode(), "%s", e
.what());
3130 AL_API
DECL_FUNCEXT5(void, alGetSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value1
, ALint64SOFT
*,value2
, ALint64SOFT
*,value3
)
3131 FORCE_ALIGN
void AL_APIENTRY
alGetSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
3132 ALenum param
, ALint64SOFT
*value1
, ALint64SOFT
*value2
, ALint64SOFT
*value3
) noexcept
3134 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3135 ALsource
*Source
{LookupSource(context
, source
)};
3137 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3138 if(!(value1
&& value2
&& value3
))
3139 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3141 std::array
<int64_t,3> i64vals
{};
3142 GetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
3143 *value1
= i64vals
[0];
3144 *value2
= i64vals
[1];
3145 *value3
= i64vals
[2];
3147 catch(al::context_error
& e
) {
3148 context
->setError(e
.errorCode(), "%s", e
.what());
3151 AL_API
DECL_FUNCEXT3(void, alGetSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,values
)
3152 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
3153 ALenum param
, ALint64SOFT
*values
) noexcept
3155 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3156 ALsource
*Source
{LookupSource(context
, source
)};
3158 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3160 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3162 const ALuint count
{Int64ValsByProp(param
)};
3163 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3165 catch(al::context_error
& e
) {
3166 context
->setError(e
.errorCode(), "%s", e
.what());
3170 AL_API
DECL_FUNC1(void, alSourcePlay
, ALuint
,source
)
3171 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayDirect(ALCcontext
*context
, ALuint source
) noexcept
3173 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3174 ALsource
*Source
{LookupSource(context
, source
)};
3176 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3178 StartSources(context
, {&Source
, 1});
3180 catch(al::context_error
& e
) {
3181 context
->setError(e
.errorCode(), "%s", e
.what());
3184 FORCE_ALIGN
DECL_FUNCEXT2(void, alSourcePlayAtTime
,SOFT
, ALuint
,source
, ALint64SOFT
,start_time
)
3185 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimeDirectSOFT(ALCcontext
*context
, ALuint source
,
3186 ALint64SOFT start_time
) noexcept
3189 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3191 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3192 ALsource
*Source
{LookupSource(context
, source
)};
3194 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3196 StartSources(context
, {&Source
, 1}, nanoseconds
{start_time
});
3198 catch(al::context_error
& e
) {
3199 context
->setError(e
.errorCode(), "%s", e
.what());
3202 AL_API
DECL_FUNC2(void, alSourcePlayv
, ALsizei
,n
, const ALuint
*,sources
)
3203 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayvDirect(ALCcontext
*context
, ALsizei n
,
3204 const ALuint
*sources
) noexcept
3207 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3208 if(n
<= 0) UNLIKELY
return;
3210 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3211 source_store_variant source_store
;
3212 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3214 if(count
> std::tuple_size_v
<source_store_array
>)
3215 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3216 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3219 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3220 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3222 if(ALsource
*src
{LookupSource(context
, sid
)})
3224 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3226 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3228 StartSources(context
, srchandles
);
3230 catch(al::context_error
& e
) {
3231 context
->setError(e
.errorCode(), "%s", e
.what());
3234 FORCE_ALIGN
DECL_FUNCEXT3(void, alSourcePlayAtTimev
,SOFT
, ALsizei
,n
, const ALuint
*,sources
, ALint64SOFT
,start_time
)
3235 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimevDirectSOFT(ALCcontext
*context
, ALsizei n
,
3236 const ALuint
*sources
, ALint64SOFT start_time
) noexcept
3239 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3240 if(n
<= 0) UNLIKELY
return;
3243 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3245 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3246 source_store_variant source_store
;
3247 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3249 if(count
> std::tuple_size_v
<source_store_array
>)
3250 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3251 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3254 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3255 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3257 if(ALsource
*src
{LookupSource(context
, sid
)})
3259 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3261 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3263 StartSources(context
, srchandles
, nanoseconds
{start_time
});
3265 catch(al::context_error
& e
) {
3266 context
->setError(e
.errorCode(), "%s", e
.what());
3270 AL_API
DECL_FUNC1(void, alSourcePause
, ALuint
,source
)
3271 FORCE_ALIGN
void AL_APIENTRY
alSourcePauseDirect(ALCcontext
*context
, ALuint source
) noexcept
3272 { alSourcePausevDirect(context
, 1, &source
); }
3274 AL_API
DECL_FUNC2(void, alSourcePausev
, ALsizei
,n
, const ALuint
*,sources
)
3275 FORCE_ALIGN
void AL_APIENTRY
alSourcePausevDirect(ALCcontext
*context
, ALsizei n
,
3276 const ALuint
*sources
) noexcept
3279 throw al::context_error
{AL_INVALID_VALUE
, "Pausing %d sources", n
};
3280 if(n
<= 0) UNLIKELY
return;
3282 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3283 source_store_variant source_store
;
3284 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3286 if(count
> std::tuple_size_v
<source_store_array
>)
3287 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3288 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3291 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3292 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3294 if(ALsource
*src
{LookupSource(context
, sid
)})
3296 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3298 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3300 /* Pausing has to be done in two steps. First, for each source that's
3301 * detected to be playing, chamge the voice (asynchronously) to
3304 VoiceChange
*tail
{}, *cur
{};
3305 for(ALsource
*source
: srchandles
)
3307 Voice
*voice
{GetSourceVoice(source
, context
)};
3308 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3311 cur
= tail
= GetVoiceChanger(context
);
3314 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3315 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3317 cur
->mVoice
= voice
;
3318 cur
->mSourceID
= source
->id
;
3319 cur
->mState
= VChangeState::Pause
;
3324 SendVoiceChanges(context
, tail
);
3325 /* Second, now that the voice changes have been sent, because it's
3326 * possible that the voice stopped after it was detected playing and
3327 * before the voice got paused, recheck that the source is still
3328 * considered playing and set it to paused if so.
3330 for(ALsource
*source
: srchandles
)
3332 Voice
*voice
{GetSourceVoice(source
, context
)};
3333 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3334 source
->state
= AL_PAUSED
;
3338 catch(al::context_error
& e
) {
3339 context
->setError(e
.errorCode(), "%s", e
.what());
3343 AL_API
DECL_FUNC1(void, alSourceStop
, ALuint
,source
)
3344 FORCE_ALIGN
void AL_APIENTRY
alSourceStopDirect(ALCcontext
*context
, ALuint source
) noexcept
3345 { alSourceStopvDirect(context
, 1, &source
); }
3347 AL_API
DECL_FUNC2(void, alSourceStopv
, ALsizei
,n
, const ALuint
*,sources
)
3348 FORCE_ALIGN
void AL_APIENTRY
alSourceStopvDirect(ALCcontext
*context
, ALsizei n
,
3349 const ALuint
*sources
) noexcept
3352 throw al::context_error
{AL_INVALID_VALUE
, "Stopping %d sources", n
};
3353 if(n
<= 0) UNLIKELY
return;
3355 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3356 source_store_variant source_store
;
3357 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3359 if(count
> std::tuple_size_v
<source_store_array
>)
3360 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3361 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3364 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3365 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3367 if(ALsource
*src
{LookupSource(context
, sid
)})
3369 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3371 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3373 VoiceChange
*tail
{}, *cur
{};
3374 for(ALsource
*source
: srchandles
)
3376 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3379 cur
= tail
= GetVoiceChanger(context
);
3382 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3383 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3385 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3386 cur
->mVoice
= voice
;
3387 cur
->mSourceID
= source
->id
;
3388 cur
->mState
= VChangeState::Stop
;
3389 source
->state
= AL_STOPPED
;
3391 source
->Offset
= 0.0;
3392 source
->OffsetType
= AL_NONE
;
3393 source
->VoiceIdx
= InvalidVoiceIndex
;
3396 SendVoiceChanges(context
, tail
);
3398 catch(al::context_error
& e
) {
3399 context
->setError(e
.errorCode(), "%s", e
.what());
3403 AL_API
DECL_FUNC1(void, alSourceRewind
, ALuint
,source
)
3404 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindDirect(ALCcontext
*context
, ALuint source
) noexcept
3405 { alSourceRewindvDirect(context
, 1, &source
); }
3407 AL_API
DECL_FUNC2(void, alSourceRewindv
, ALsizei
,n
, const ALuint
*,sources
)
3408 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindvDirect(ALCcontext
*context
, ALsizei n
,
3409 const ALuint
*sources
) noexcept
3412 throw al::context_error
{AL_INVALID_VALUE
, "Rewinding %d sources", n
};
3413 if(n
<= 0) UNLIKELY
return;
3415 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3416 source_store_variant source_store
;
3417 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3419 if(count
> std::tuple_size_v
<source_store_array
>)
3420 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3421 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3424 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3425 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3427 if(ALsource
*src
{LookupSource(context
, sid
)})
3429 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3431 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3433 VoiceChange
*tail
{}, *cur
{};
3434 for(ALsource
*source
: srchandles
)
3436 Voice
*voice
{GetSourceVoice(source
, context
)};
3437 if(source
->state
!= AL_INITIAL
)
3440 cur
= tail
= GetVoiceChanger(context
);
3443 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3444 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3447 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3448 cur
->mVoice
= voice
;
3449 cur
->mSourceID
= source
->id
;
3450 cur
->mState
= VChangeState::Reset
;
3451 source
->state
= AL_INITIAL
;
3453 source
->Offset
= 0.0;
3454 source
->OffsetType
= AL_NONE
;
3455 source
->VoiceIdx
= InvalidVoiceIndex
;
3458 SendVoiceChanges(context
, tail
);
3460 catch(al::context_error
& e
) {
3461 context
->setError(e
.errorCode(), "%s", e
.what());
3465 AL_API
DECL_FUNC3(void, alSourceQueueBuffers
, ALuint
,source
, ALsizei
,nb
, const ALuint
*,buffers
)
3466 FORCE_ALIGN
void AL_APIENTRY
alSourceQueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3467 ALsizei nb
, const ALuint
*buffers
) noexcept
3470 throw al::context_error
{AL_INVALID_VALUE
, "Queueing %d buffers", nb
};
3471 if(nb
<= 0) UNLIKELY
return;
3473 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3474 ALsource
*source
{LookupSource(context
,src
)};
3476 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3478 /* Can't queue on a Static Source */
3479 if(source
->SourceType
== AL_STATIC
)
3480 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing onto static source %u", src
};
3482 /* Check for a valid Buffer, for its frequency and format */
3483 ALCdevice
*device
{context
->mALDevice
.get()};
3484 ALbuffer
*BufferFmt
{nullptr};
3485 for(auto &item
: source
->mQueue
)
3487 BufferFmt
= item
.mBuffer
;
3488 if(BufferFmt
) break;
3491 std::unique_lock
<std::mutex
> buflock
{device
->BufferLock
};
3492 const auto bids
= al::span
{buffers
, static_cast<ALuint
>(nb
)};
3493 const size_t NewListStart
{source
->mQueue
.size()};
3495 ALbufferQueueItem
*BufferList
{nullptr};
3496 std::for_each(bids
.cbegin(), bids
.cend(),
3497 [source
,device
,&BufferFmt
,&BufferList
](const ALuint bid
)
3499 ALbuffer
*buffer
{bid
? LookupBuffer(device
, bid
) : nullptr};
3501 throw al::context_error
{AL_INVALID_NAME
, "Queueing invalid buffer ID %u", bid
};
3505 if(buffer
->mSampleRate
< 1)
3506 throw al::context_error
{AL_INVALID_OPERATION
,
3507 "Queueing buffer %u with no format", buffer
->id
};
3509 if(buffer
->mCallback
)
3510 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing callback buffer %u",
3513 if(buffer
->MappedAccess
!= 0 && !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
3514 throw al::context_error
{AL_INVALID_OPERATION
,
3515 "Queueing non-persistently mapped buffer %u", buffer
->id
};
3518 source
->mQueue
.emplace_back();
3520 BufferList
= &source
->mQueue
.back();
3523 auto &item
= source
->mQueue
.back();
3524 BufferList
->mNext
.store(&item
, std::memory_order_relaxed
);
3528 BufferList
->mBlockAlign
= buffer
->mBlockAlign
;
3529 BufferList
->mSampleLen
= buffer
->mSampleLen
;
3530 BufferList
->mLoopEnd
= buffer
->mSampleLen
;
3531 BufferList
->mSamples
= buffer
->mData
;
3532 BufferList
->mBuffer
= buffer
;
3533 IncrementRef(buffer
->ref
);
3535 bool fmt_mismatch
{false};
3536 if(BufferFmt
== nullptr)
3540 fmt_mismatch
|= BufferFmt
->mSampleRate
!= buffer
->mSampleRate
;
3541 fmt_mismatch
|= BufferFmt
->mChannels
!= buffer
->mChannels
;
3542 fmt_mismatch
|= BufferFmt
->mType
!= buffer
->mType
;
3543 if(BufferFmt
->isBFormat())
3545 fmt_mismatch
|= BufferFmt
->mAmbiLayout
!= buffer
->mAmbiLayout
;
3546 fmt_mismatch
|= BufferFmt
->mAmbiScaling
!= buffer
->mAmbiScaling
;
3548 fmt_mismatch
|= BufferFmt
->mAmbiOrder
!= buffer
->mAmbiOrder
;
3551 throw al::context_error
{AL_INVALID_OPERATION
,
3552 "Queueing buffer with mismatched format\n"
3553 " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt
->mSampleRate
,
3554 NameFromFormat(BufferFmt
->mType
), NameFromFormat(BufferFmt
->mChannels
),
3555 buffer
->mSampleRate
, NameFromFormat(buffer
->mType
),
3556 NameFromFormat(buffer
->mChannels
)};
3560 /* A buffer failed (invalid ID or format), or there was some other
3561 * unexpected error, so unlock and release each buffer we had.
3563 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3564 for(;iter
!= source
->mQueue
.end();++iter
)
3566 if(ALbuffer
*buf
{iter
->mBuffer
})
3567 DecrementRef(buf
->ref
);
3569 source
->mQueue
.resize(NewListStart
);
3572 /* All buffers good. */
3575 /* Source is now streaming */
3576 source
->SourceType
= AL_STREAMING
;
3578 if(NewListStart
!= 0)
3580 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3581 (iter
-1)->mNext
.store(al::to_address(iter
), std::memory_order_release
);
3584 catch(al::context_error
& e
) {
3585 context
->setError(e
.errorCode(), "%s", e
.what());
3588 AL_API
DECL_FUNC3(void, alSourceUnqueueBuffers
, ALuint
,source
, ALsizei
,nb
, ALuint
*,buffers
)
3589 FORCE_ALIGN
void AL_APIENTRY
alSourceUnqueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3590 ALsizei nb
, ALuint
*buffers
) noexcept
3593 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffers", nb
};
3594 if(nb
<= 0) UNLIKELY
return;
3596 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3597 ALsource
*source
{LookupSource(context
,src
)};
3599 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3601 if(source
->SourceType
!= AL_STREAMING
)
3602 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from a non-streaming source %u",src
};
3604 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from looping source %u", src
};
3606 /* Make sure enough buffers have been processed to unqueue. */
3607 const al::span bids
{buffers
, static_cast<ALuint
>(nb
)};
3608 size_t processed
{0};
3609 if(source
->state
!= AL_INITIAL
) LIKELY
3611 VoiceBufferItem
*Current
{nullptr};
3612 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3613 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
3614 for(auto &item
: source
->mQueue
)
3616 if(&item
== Current
)
3621 if(processed
< bids
.size())
3622 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffer%s (only %zu processed)",
3623 nb
, (nb
==1)?"":"s", processed
};
3625 std::generate(bids
.begin(), bids
.end(), [source
]() noexcept
-> ALuint
3627 auto &head
= source
->mQueue
.front();
3629 if(ALbuffer
*buffer
{head
.mBuffer
})
3632 DecrementRef(buffer
->ref
);
3634 source
->mQueue
.pop_front();
3638 catch(al::context_error
& e
) {
3639 context
->setError(e
.errorCode(), "%s", e
.what());
3643 AL_API
void AL_APIENTRY
alSourceQueueBufferLayersSOFT(ALuint
, ALsizei
, const ALuint
*) noexcept
3645 ContextRef context
{GetContextRef()};
3646 if(!context
) UNLIKELY
return;
3648 context
->setError(AL_INVALID_OPERATION
, "alSourceQueueBufferLayersSOFT not supported");
3652 ALsource::ALsource() noexcept
3655 Direct
.GainHF
= 1.0f
;
3656 Direct
.HFReference
= LowPassFreqRef
;
3657 Direct
.GainLF
= 1.0f
;
3658 Direct
.LFReference
= HighPassFreqRef
;
3659 for(auto &send
: Send
)
3661 send
.Slot
= nullptr;
3664 send
.HFReference
= LowPassFreqRef
;
3666 send
.LFReference
= HighPassFreqRef
;
3670 ALsource::~ALsource()
3672 for(auto &item
: mQueue
)
3674 if(ALbuffer
*buffer
{item
.mBuffer
})
3675 DecrementRef(buffer
->ref
);
3678 auto clear_send
= [](ALsource::SendData
&send
) -> void
3679 { if(send
.Slot
) DecrementRef(send
.Slot
->ref
); };
3680 std::for_each(Send
.begin(), Send
.end(), clear_send
);
3683 void UpdateAllSourceProps(ALCcontext
*context
)
3685 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3686 auto voicelist
= context
->getVoicesSpan();
3688 for(Voice
*voice
: voicelist
)
3690 ALuint sid
{voice
->mSourceID
.load(std::memory_order_acquire
)};
3691 ALsource
*source
{sid
? LookupSource(context
, sid
) : nullptr};
3692 if(source
&& source
->VoiceIdx
== vidx
)
3694 if(std::exchange(source
->mPropsDirty
, false))
3695 UpdateSourceProps(source
, voice
, context
);
3701 void ALsource::SetName(ALCcontext
*context
, ALuint id
, std::string_view name
)
3703 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3705 auto source
= LookupSource(context
, id
);
3707 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", id
};
3709 context
->mSourceNames
.insert_or_assign(id
, name
);
3713 SourceSubList::~SourceSubList()
3718 uint64_t usemask
{~FreeMask
};
3721 const int idx
{al::countr_zero(usemask
)};
3722 usemask
&= ~(1_u64
<< idx
);
3723 std::destroy_at(al::to_address(Sources
->begin() + idx
));
3725 FreeMask
= ~usemask
;
3726 SubListAllocator
{}.deallocate(Sources
, 1);
3732 void ALsource::eaxInitialize(ALCcontext
*context
) noexcept
3734 assert(context
!= nullptr);
3735 mEaxAlContext
= context
;
3737 mEaxPrimaryFxSlotId
= context
->eaxGetPrimaryFxSlotIndex();
3740 eax1_translate(mEax1
.i
, mEax
);
3745 void ALsource::eaxDispatch(const EaxCall
& call
)
3747 call
.is_get() ? eax_get(call
) : eax_set(call
);
3750 ALsource
* ALsource::EaxLookupSource(ALCcontext
& al_context
, ALuint source_id
) noexcept
3752 return LookupSource(&al_context
, source_id
);
3755 [[noreturn
]] void ALsource::eax_fail(const char* message
)
3757 throw Exception
{message
};
3760 [[noreturn
]] void ALsource::eax_fail_unknown_property_id()
3762 eax_fail("Unknown property id.");
3765 [[noreturn
]] void ALsource::eax_fail_unknown_version()
3767 eax_fail("Unknown version.");
3770 [[noreturn
]] void ALsource::eax_fail_unknown_active_fx_slot_id()
3772 eax_fail("Unknown active FX slot ID.");
3775 [[noreturn
]] void ALsource::eax_fail_unknown_receiving_fx_slot_id()
3777 eax_fail("Unknown receiving FX slot ID.");
3780 void ALsource::eax_set_sends_defaults(EaxSends
& sends
, const EaxFxSlotIds
& ids
) noexcept
3782 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
3784 auto& send
= sends
[i
];
3785 send
.guidReceivingFXSlotID
= *(ids
[i
]);
3786 send
.lSend
= EAXSOURCE_DEFAULTSEND
;
3787 send
.lSendHF
= EAXSOURCE_DEFAULTSENDHF
;
3788 send
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3789 send
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3790 send
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3791 send
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3792 send
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3793 send
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3797 void ALsource::eax1_set_defaults(Eax1Props
& props
) noexcept
3799 props
.fMix
= EAX_REVERBMIX_USEDISTANCE
;
3802 void ALsource::eax1_set_defaults() noexcept
3804 eax1_set_defaults(mEax1
.i
);
3808 void ALsource::eax2_set_defaults(Eax2Props
& props
) noexcept
3810 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3811 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3812 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3813 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
3814 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3815 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
3816 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
3817 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3818 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3819 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3820 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3821 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3822 props
.dwFlags
= EAXSOURCE_DEFAULTFLAGS
;
3825 void ALsource::eax2_set_defaults() noexcept
3827 eax2_set_defaults(mEax2
.i
);
3831 void ALsource::eax3_set_defaults(Eax3Props
& props
) noexcept
3833 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3834 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3835 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3836 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
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
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3843 props
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3844 props
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3845 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3846 props
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3847 props
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3848 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3849 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3850 props
.ulFlags
= EAXSOURCE_DEFAULTFLAGS
;
3853 void ALsource::eax3_set_defaults() noexcept
3855 eax3_set_defaults(mEax3
.i
);
3859 void ALsource::eax4_set_sends_defaults(EaxSends
& sends
) noexcept
3861 eax_set_sends_defaults(sends
, eax4_fx_slot_ids
);
3864 void ALsource::eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS
& slots
) noexcept
3866 slots
= EAX40SOURCE_DEFAULTACTIVEFXSLOTID
;
3869 void ALsource::eax4_set_defaults() noexcept
3871 eax3_set_defaults(mEax4
.i
.source
);
3872 eax4_set_sends_defaults(mEax4
.i
.sends
);
3873 eax4_set_active_fx_slots_defaults(mEax4
.i
.active_fx_slots
);
3877 void ALsource::eax5_set_source_defaults(EAX50SOURCEPROPERTIES
& props
) noexcept
3879 eax3_set_defaults(static_cast<Eax3Props
&>(props
));
3880 props
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3883 void ALsource::eax5_set_sends_defaults(EaxSends
& sends
) noexcept
3885 eax_set_sends_defaults(sends
, eax5_fx_slot_ids
);
3888 void ALsource::eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS
& slots
) noexcept
3890 slots
= EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID
;
3893 void ALsource::eax5_set_speaker_levels_defaults(EaxSpeakerLevels
& speaker_levels
) noexcept
3895 for(size_t i
{0};i
< eax_max_speakers
;++i
)
3897 auto& speaker_level
= speaker_levels
[i
];
3898 speaker_level
.lSpeakerID
= static_cast<long>(EAXSPEAKER_FRONT_LEFT
+ i
);
3899 speaker_level
.lLevel
= EAXSOURCE_DEFAULTSPEAKERLEVEL
;
3903 void ALsource::eax5_set_defaults(Eax5Props
& props
) noexcept
3905 eax5_set_source_defaults(props
.source
);
3906 eax5_set_sends_defaults(props
.sends
);
3907 eax5_set_active_fx_slots_defaults(props
.active_fx_slots
);
3908 eax5_set_speaker_levels_defaults(props
.speaker_levels
);
3911 void ALsource::eax5_set_defaults() noexcept
3913 eax5_set_defaults(mEax5
.i
);
3917 void ALsource::eax_set_defaults() noexcept
3919 eax1_set_defaults();
3920 eax2_set_defaults();
3921 eax3_set_defaults();
3922 eax4_set_defaults();
3923 eax5_set_defaults();
3926 void ALsource::eax1_translate(const Eax1Props
& src
, Eax5Props
& dst
) noexcept
3928 eax5_set_defaults(dst
);
3930 if (src
.fMix
== EAX_REVERBMIX_USEDISTANCE
)
3932 dst
.source
.ulFlags
|= EAXSOURCEFLAGS_ROOMAUTO
;
3933 dst
.sends
[0].lSend
= 0;
3937 dst
.source
.ulFlags
&= ~EAXSOURCEFLAGS_ROOMAUTO
;
3938 dst
.sends
[0].lSend
= std::clamp(static_cast<long>(gain_to_level_mb(src
.fMix
)),
3939 EAXSOURCE_MINSEND
, EAXSOURCE_MAXSEND
);
3943 void ALsource::eax2_translate(const Eax2Props
& src
, Eax5Props
& dst
) noexcept
3947 dst
.source
.lDirect
= src
.lDirect
;
3948 dst
.source
.lDirectHF
= src
.lDirectHF
;
3949 dst
.source
.lRoom
= src
.lRoom
;
3950 dst
.source
.lRoomHF
= src
.lRoomHF
;
3951 dst
.source
.lObstruction
= src
.lObstruction
;
3952 dst
.source
.flObstructionLFRatio
= src
.flObstructionLFRatio
;
3953 dst
.source
.lOcclusion
= src
.lOcclusion
;
3954 dst
.source
.flOcclusionLFRatio
= src
.flOcclusionLFRatio
;
3955 dst
.source
.flOcclusionRoomRatio
= src
.flOcclusionRoomRatio
;
3956 dst
.source
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3957 dst
.source
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3958 dst
.source
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3959 dst
.source
.lOutsideVolumeHF
= src
.lOutsideVolumeHF
;
3960 dst
.source
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3961 dst
.source
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3962 dst
.source
.flRoomRolloffFactor
= src
.flRoomRolloffFactor
;
3963 dst
.source
.flAirAbsorptionFactor
= src
.flAirAbsorptionFactor
;
3964 dst
.source
.ulFlags
= src
.dwFlags
;
3965 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3967 // Set everything else to defaults.
3969 eax5_set_sends_defaults(dst
.sends
);
3970 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
3971 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
3974 void ALsource::eax3_translate(const Eax3Props
& src
, Eax5Props
& dst
) noexcept
3978 static_cast<Eax3Props
&>(dst
.source
) = src
;
3979 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3981 // Set everything else to defaults.
3983 eax5_set_sends_defaults(dst
.sends
);
3984 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
3985 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
3988 void ALsource::eax4_translate(const Eax4Props
& src
, Eax5Props
& dst
) noexcept
3992 static_cast<Eax3Props
&>(dst
.source
) = src
.source
;
3993 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3997 dst
.sends
= src
.sends
;
3999 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4000 dst
.sends
[i
].guidReceivingFXSlotID
= *(eax5_fx_slot_ids
[i
]);
4004 auto translate_slotid
= [](const GUID
&src_id
) -> GUID
4006 if(src_id
== EAX_NULL_GUID
)
4007 return EAX_NULL_GUID
;
4008 if(src_id
== EAX_PrimaryFXSlotID
)
4009 return EAX_PrimaryFXSlotID
;
4010 if(src_id
== EAXPROPERTYID_EAX40_FXSlot0
)
4011 return EAXPROPERTYID_EAX50_FXSlot0
;
4012 if(src_id
== EAXPROPERTYID_EAX40_FXSlot1
)
4013 return EAXPROPERTYID_EAX50_FXSlot1
;
4014 if(src_id
== EAXPROPERTYID_EAX40_FXSlot2
)
4015 return EAXPROPERTYID_EAX50_FXSlot2
;
4016 if(src_id
== EAXPROPERTYID_EAX40_FXSlot3
)
4017 return EAXPROPERTYID_EAX50_FXSlot3
;
4020 ERR("Unexpected active FX slot ID\n");
4021 return EAX_NULL_GUID
;
4023 const auto src_slots
= al::span
{src
.active_fx_slots
.guidActiveFXSlots
};
4024 const auto dst_slots
= al::span
{dst
.active_fx_slots
.guidActiveFXSlots
};
4025 auto dstiter
= std::transform(src_slots
.cbegin(), src_slots
.cend(), dst_slots
.begin(),
4027 std::fill(dstiter
, dst_slots
.end(), EAX_NULL_GUID
);
4031 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4034 float ALsource::eax_calculate_dst_occlusion_mb(
4035 long src_occlusion_mb
,
4037 float lf_ratio
) noexcept
4039 const auto ratio_1
= path_ratio
+ lf_ratio
- 1.0F
;
4040 const auto ratio_2
= path_ratio
* lf_ratio
;
4041 const auto ratio
= (ratio_2
> ratio_1
) ? ratio_2
: ratio_1
;
4042 const auto dst_occlustion_mb
= static_cast<float>(src_occlusion_mb
) * ratio
;
4043 return dst_occlustion_mb
;
4046 EaxAlLowPassParam
ALsource::eax_create_direct_filter_param() const noexcept
4048 const auto &source
= mEax
.source
;
4050 auto gain_mb
= static_cast<float>(source
.lObstruction
) * source
.flObstructionLFRatio
;
4051 auto gainhf_mb
= static_cast<float>(source
.lObstruction
);
4053 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4055 if(!mEaxActiveFxSlots
[i
])
4058 if(source
.lOcclusion
!= 0)
4060 const auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4061 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4062 const auto is_environmental_fx
= ((fx_slot_eax
.ulFlags
&EAXFXSLOTFLAGS_ENVIRONMENT
) != 0);
4063 const auto is_primary
= (mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index());
4065 if(is_environmental_fx
&& is_primary
)
4067 gain_mb
+= eax_calculate_dst_occlusion_mb(source
.lOcclusion
,
4068 source
.flOcclusionDirectRatio
, source
.flOcclusionLFRatio
);
4070 gainhf_mb
+= static_cast<float>(source
.lOcclusion
) * source
.flOcclusionDirectRatio
;
4074 const auto& send
= mEax
.sends
[i
];
4075 if(send
.lOcclusion
!= 0)
4077 gain_mb
+= eax_calculate_dst_occlusion_mb(send
.lOcclusion
, send
.flOcclusionDirectRatio
,
4078 send
.flOcclusionLFRatio
);
4080 gainhf_mb
+= static_cast<float>(send
.lOcclusion
) * send
.flOcclusionDirectRatio
;
4084 /* gainhf_mb is the absolute mBFS of the filter's high-frequency volume,
4085 * and gain_mb is the absolute mBFS of the filter's low-frequency volume.
4086 * Adjust the HF volume to be relative to the LF volume, to make the
4087 * appropriate main and relative HF filter volumes.
4089 * Also add the Direct and DirectHF properties to the filter, which are
4090 * already the main and relative HF volumes.
4092 gainhf_mb
-= gain_mb
- static_cast<float>(source
.lDirectHF
);
4093 gain_mb
+= static_cast<float>(source
.lDirect
);
4095 return EaxAlLowPassParam
{level_mb_to_gain(gain_mb
),
4096 std::min(level_mb_to_gain(gainhf_mb
), 1.0f
)};
4099 EaxAlLowPassParam
ALsource::eax_create_room_filter_param(
4100 const ALeffectslot
& fx_slot
,
4101 const EAXSOURCEALLSENDPROPERTIES
& send
) const noexcept
4103 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4104 const auto is_environmental_fx
= bool{(fx_slot_eax
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0};
4105 const auto is_primary
= bool{mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index()};
4107 auto gain_mb
= (static_cast<float>(fx_slot_eax
.lOcclusion
) * fx_slot_eax
.flOcclusionLFRatio
)
4108 + eax_calculate_dst_occlusion_mb(send
.lOcclusion
, send
.flOcclusionRoomRatio
,
4109 send
.flOcclusionLFRatio
)
4110 + (static_cast<float>(send
.lExclusion
) * send
.flExclusionLFRatio
);
4112 auto gainhf_mb
= static_cast<float>(fx_slot_eax
.lOcclusion
)
4113 + (static_cast<float>(send
.lOcclusion
) * send
.flOcclusionRoomRatio
);
4115 if(is_environmental_fx
&& is_primary
)
4117 const auto &source
= mEax
.source
;
4119 gain_mb
+= eax_calculate_dst_occlusion_mb(source
.lOcclusion
, source
.flOcclusionRoomRatio
,
4120 source
.flOcclusionLFRatio
);
4121 gain_mb
+= static_cast<float>(source
.lExclusion
) * source
.flExclusionLFRatio
;
4123 gainhf_mb
+= static_cast<float>(source
.lOcclusion
) * source
.flOcclusionRoomRatio
;
4124 gainhf_mb
+= static_cast<float>(source
.lExclusion
+ send
.lExclusion
);
4127 gainhf_mb
-= gain_mb
- static_cast<float>(send
.lSendHF
);
4128 gain_mb
+= static_cast<float>(send
.lSend
);
4129 if(is_environmental_fx
)
4131 const auto &source
= mEax
.source
;
4132 gain_mb
+= static_cast<float>(source
.lRoom
);
4133 gainhf_mb
+= static_cast<float>(source
.lRoomHF
);
4136 return EaxAlLowPassParam
{level_mb_to_gain(gain_mb
),
4137 std::min(level_mb_to_gain(gainhf_mb
), 1.0f
)};
4140 void ALsource::eax_update_direct_filter()
4142 const auto& direct_param
= eax_create_direct_filter_param();
4143 Direct
.Gain
= direct_param
.gain
;
4144 Direct
.GainHF
= direct_param
.gain_hf
;
4145 Direct
.HFReference
= LowPassFreqRef
;
4146 Direct
.GainLF
= 1.0f
;
4147 Direct
.LFReference
= HighPassFreqRef
;
4151 void ALsource::eax_update_room_filters()
4153 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4155 if(!mEaxActiveFxSlots
[i
])
4158 auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4159 const auto& send
= mEax
.sends
[i
];
4160 const auto& room_param
= eax_create_room_filter_param(fx_slot
, send
);
4161 eax_set_al_source_send(&fx_slot
, i
, room_param
);
4165 void ALsource::eax_set_efx_outer_gain_hf()
4167 OuterGainHF
= std::clamp(
4168 level_mb_to_gain(static_cast<float>(mEax
.source
.lOutsideVolumeHF
)),
4169 AL_MIN_CONE_OUTER_GAINHF
,
4170 AL_MAX_CONE_OUTER_GAINHF
);
4173 void ALsource::eax_set_efx_doppler_factor()
4175 DopplerFactor
= mEax
.source
.flDopplerFactor
;
4178 void ALsource::eax_set_efx_rolloff_factor()
4180 RolloffFactor2
= mEax
.source
.flRolloffFactor
;
4183 void ALsource::eax_set_efx_room_rolloff_factor()
4185 RoomRolloffFactor
= mEax
.source
.flRoomRolloffFactor
;
4188 void ALsource::eax_set_efx_air_absorption_factor()
4190 AirAbsorptionFactor
= mEax
.source
.flAirAbsorptionFactor
;
4193 void ALsource::eax_set_efx_dry_gain_hf_auto()
4195 DryGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_DIRECTHFAUTO
) != 0);
4198 void ALsource::eax_set_efx_wet_gain_auto()
4200 WetGainAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMAUTO
) != 0);
4203 void ALsource::eax_set_efx_wet_gain_hf_auto()
4205 WetGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMHFAUTO
) != 0);
4208 void ALsource::eax1_set(const EaxCall
& call
, Eax1Props
& props
)
4210 switch (call
.get_property_id()) {
4211 case DSPROPERTY_EAXBUFFER_ALL
:
4212 eax_defer
<Eax1SourceAllValidator
>(call
, props
);
4215 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4216 eax_defer
<Eax1SourceReverbMixValidator
>(call
, props
.fMix
);
4220 eax_fail_unknown_property_id();
4224 void ALsource::eax2_set(const EaxCall
& call
, Eax2Props
& props
)
4226 switch (call
.get_property_id()) {
4227 case DSPROPERTY_EAX20BUFFER_NONE
:
4230 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4231 eax_defer
<Eax2SourceAllValidator
>(call
, props
);
4234 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4235 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4238 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4239 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4242 case DSPROPERTY_EAX20BUFFER_ROOM
:
4243 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4246 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4247 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4250 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4251 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4254 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4255 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4258 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4259 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4262 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4263 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4266 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4267 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4270 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4271 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4274 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4275 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4278 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4279 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4282 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4283 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.dwFlags
);
4287 eax_fail_unknown_property_id();
4291 void ALsource::eax3_set(const EaxCall
& call
, Eax3Props
& props
)
4293 switch (call
.get_property_id()) {
4294 case EAXSOURCE_NONE
:
4297 case EAXSOURCE_ALLPARAMETERS
:
4298 eax_defer
<Eax3SourceAllValidator
>(call
, props
);
4301 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4302 eax_defer_sub
<Eax4ObstructionValidator
, EAXOBSTRUCTIONPROPERTIES
>(call
, props
.lObstruction
);
4305 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4306 eax_defer_sub
<Eax4OcclusionValidator
, EAXOCCLUSIONPROPERTIES
>(call
, props
.lOcclusion
);
4309 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4310 eax_defer_sub
<Eax4ExclusionValidator
, EAXEXCLUSIONPROPERTIES
>(call
, props
.lExclusion
);
4313 case EAXSOURCE_DIRECT
:
4314 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4317 case EAXSOURCE_DIRECTHF
:
4318 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4321 case EAXSOURCE_ROOM
:
4322 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4325 case EAXSOURCE_ROOMHF
:
4326 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4329 case EAXSOURCE_OBSTRUCTION
:
4330 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4333 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4334 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4337 case EAXSOURCE_OCCLUSION
:
4338 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4341 case EAXSOURCE_OCCLUSIONLFRATIO
:
4342 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4345 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4346 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4349 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4350 eax_defer
<Eax3SourceOcclusionDirectRatioValidator
>(call
, props
.flOcclusionDirectRatio
);
4353 case EAXSOURCE_EXCLUSION
:
4354 eax_defer
<Eax3SourceExclusionValidator
>(call
, props
.lExclusion
);
4357 case EAXSOURCE_EXCLUSIONLFRATIO
:
4358 eax_defer
<Eax3SourceExclusionLfRatioValidator
>(call
, props
.flExclusionLFRatio
);
4361 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4362 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4365 case EAXSOURCE_DOPPLERFACTOR
:
4366 eax_defer
<Eax3SourceDopplerFactorValidator
>(call
, props
.flDopplerFactor
);
4369 case EAXSOURCE_ROLLOFFFACTOR
:
4370 eax_defer
<Eax3SourceRolloffFactorValidator
>(call
, props
.flRolloffFactor
);
4373 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4374 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4377 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4378 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4381 case EAXSOURCE_FLAGS
:
4382 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.ulFlags
);
4386 eax_fail_unknown_property_id();
4390 void ALsource::eax4_set(const EaxCall
& call
, Eax4Props
& props
)
4392 switch (call
.get_property_id()) {
4393 case EAXSOURCE_NONE
:
4394 case EAXSOURCE_ALLPARAMETERS
:
4395 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4396 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4397 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4398 case EAXSOURCE_DIRECT
:
4399 case EAXSOURCE_DIRECTHF
:
4400 case EAXSOURCE_ROOM
:
4401 case EAXSOURCE_ROOMHF
:
4402 case EAXSOURCE_OBSTRUCTION
:
4403 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4404 case EAXSOURCE_OCCLUSION
:
4405 case EAXSOURCE_OCCLUSIONLFRATIO
:
4406 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4407 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4408 case EAXSOURCE_EXCLUSION
:
4409 case EAXSOURCE_EXCLUSIONLFRATIO
:
4410 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4411 case EAXSOURCE_DOPPLERFACTOR
:
4412 case EAXSOURCE_ROLLOFFFACTOR
:
4413 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4414 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4415 case EAXSOURCE_FLAGS
:
4416 eax3_set(call
, props
.source
);
4419 case EAXSOURCE_SENDPARAMETERS
:
4420 eax4_defer_sends
<Eax4SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4423 case EAXSOURCE_ALLSENDPARAMETERS
:
4424 eax4_defer_sends
<Eax4AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4427 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4428 eax4_defer_sends
<Eax4OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4431 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4432 eax4_defer_sends
<Eax4ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4435 case EAXSOURCE_ACTIVEFXSLOTID
:
4436 eax4_defer_active_fx_slot_id(call
, al::span
{props
.active_fx_slots
.guidActiveFXSlots
});
4440 eax_fail_unknown_property_id();
4444 void ALsource::eax5_defer_all_2d(const EaxCall
& call
, EAX50SOURCEPROPERTIES
& props
)
4446 const auto& src_props
= call
.get_value
<Exception
, const EAXSOURCE2DPROPERTIES
>();
4447 Eax5SourceAll2dValidator
{}(src_props
);
4448 props
.lDirect
= src_props
.lDirect
;
4449 props
.lDirectHF
= src_props
.lDirectHF
;
4450 props
.lRoom
= src_props
.lRoom
;
4451 props
.lRoomHF
= src_props
.lRoomHF
;
4452 props
.ulFlags
= src_props
.ulFlags
;
4455 void ALsource::eax5_defer_speaker_levels(const EaxCall
& call
, EaxSpeakerLevels
& props
)
4457 const auto values
= call
.get_values
<const EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4458 std::for_each(values
.cbegin(), values
.cend(), Eax5SpeakerAllValidator
{});
4460 for (const auto& value
: values
) {
4461 const auto index
= static_cast<size_t>(value
.lSpeakerID
- EAXSPEAKER_FRONT_LEFT
);
4462 props
[index
].lLevel
= value
.lLevel
;
4466 void ALsource::eax5_set(const EaxCall
& call
, Eax5Props
& props
)
4468 switch (call
.get_property_id()) {
4469 case EAXSOURCE_NONE
:
4472 case EAXSOURCE_ALLPARAMETERS
:
4473 eax_defer
<Eax5SourceAllValidator
>(call
, props
.source
);
4476 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4477 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4478 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4479 case EAXSOURCE_DIRECT
:
4480 case EAXSOURCE_DIRECTHF
:
4481 case EAXSOURCE_ROOM
:
4482 case EAXSOURCE_ROOMHF
:
4483 case EAXSOURCE_OBSTRUCTION
:
4484 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4485 case EAXSOURCE_OCCLUSION
:
4486 case EAXSOURCE_OCCLUSIONLFRATIO
:
4487 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4488 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4489 case EAXSOURCE_EXCLUSION
:
4490 case EAXSOURCE_EXCLUSIONLFRATIO
:
4491 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4492 case EAXSOURCE_DOPPLERFACTOR
:
4493 case EAXSOURCE_ROLLOFFFACTOR
:
4494 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4495 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4496 eax3_set(call
, props
.source
);
4499 case EAXSOURCE_FLAGS
:
4500 eax_defer
<Eax5SourceFlagsValidator
>(call
, props
.source
.ulFlags
);
4503 case EAXSOURCE_SENDPARAMETERS
:
4504 eax5_defer_sends
<Eax5SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4507 case EAXSOURCE_ALLSENDPARAMETERS
:
4508 eax5_defer_sends
<Eax5AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4511 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4512 eax5_defer_sends
<Eax5OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4515 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4516 eax5_defer_sends
<Eax5ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4519 case EAXSOURCE_ACTIVEFXSLOTID
:
4520 eax5_defer_active_fx_slot_id(call
, al::span
{props
.active_fx_slots
.guidActiveFXSlots
});
4523 case EAXSOURCE_MACROFXFACTOR
:
4524 eax_defer
<Eax5SourceMacroFXFactorValidator
>(call
, props
.source
.flMacroFXFactor
);
4527 case EAXSOURCE_SPEAKERLEVELS
:
4528 eax5_defer_speaker_levels(call
, props
.speaker_levels
);
4531 case EAXSOURCE_ALL2DPARAMETERS
:
4532 eax5_defer_all_2d(call
, props
.source
);
4536 eax_fail_unknown_property_id();
4540 void ALsource::eax_set(const EaxCall
& call
)
4542 const auto eax_version
= call
.get_version();
4545 case 1: eax1_set(call
, mEax1
.d
); break;
4546 case 2: eax2_set(call
, mEax2
.d
); break;
4547 case 3: eax3_set(call
, mEax3
.d
); break;
4548 case 4: eax4_set(call
, mEax4
.d
); break;
4549 case 5: eax5_set(call
, mEax5
.d
); break;
4550 default: eax_fail_unknown_property_id();
4553 mEaxVersion
= eax_version
;
4556 void ALsource::eax_get_active_fx_slot_id(const EaxCall
& call
, const al::span
<const GUID
> src_ids
)
4558 assert(src_ids
.size()==EAX40_MAX_ACTIVE_FXSLOTS
|| src_ids
.size()==EAX50_MAX_ACTIVE_FXSLOTS
);
4559 const auto dst_ids
= call
.get_values
<GUID
>(src_ids
.size());
4560 std::uninitialized_copy_n(src_ids
.begin(), dst_ids
.size(), dst_ids
.begin());
4563 void ALsource::eax1_get(const EaxCall
& call
, const Eax1Props
& props
)
4565 switch (call
.get_property_id()) {
4566 case DSPROPERTY_EAXBUFFER_ALL
:
4567 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4568 call
.set_value
<Exception
>(props
.fMix
);
4572 eax_fail_unknown_property_id();
4576 void ALsource::eax2_get(const EaxCall
& call
, const Eax2Props
& props
)
4578 switch (call
.get_property_id()) {
4579 case DSPROPERTY_EAX20BUFFER_NONE
:
4582 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4583 call
.set_value
<Exception
>(props
);
4586 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4587 call
.set_value
<Exception
>(props
.lDirect
);
4590 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4591 call
.set_value
<Exception
>(props
.lDirectHF
);
4594 case DSPROPERTY_EAX20BUFFER_ROOM
:
4595 call
.set_value
<Exception
>(props
.lRoom
);
4598 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4599 call
.set_value
<Exception
>(props
.lRoomHF
);
4602 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4603 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4606 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4607 call
.set_value
<Exception
>(props
.lObstruction
);
4610 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4611 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4614 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4615 call
.set_value
<Exception
>(props
.lOcclusion
);
4618 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4619 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4622 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4623 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4626 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4627 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4630 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4631 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4634 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4635 call
.set_value
<Exception
>(props
.dwFlags
);
4639 eax_fail_unknown_property_id();
4643 void ALsource::eax3_get_obstruction(const EaxCall
& call
, const Eax3Props
& props
)
4645 const auto& subprops
= reinterpret_cast<const EAXOBSTRUCTIONPROPERTIES
&>(props
.lObstruction
);
4646 call
.set_value
<Exception
>(subprops
);
4649 void ALsource::eax3_get_occlusion(const EaxCall
& call
, const Eax3Props
& props
)
4651 const auto& subprops
= reinterpret_cast<const EAXOCCLUSIONPROPERTIES
&>(props
.lOcclusion
);
4652 call
.set_value
<Exception
>(subprops
);
4655 void ALsource::eax3_get_exclusion(const EaxCall
& call
, const Eax3Props
& props
)
4657 const auto& subprops
= reinterpret_cast<const EAXEXCLUSIONPROPERTIES
&>(props
.lExclusion
);
4658 call
.set_value
<Exception
>(subprops
);
4661 void ALsource::eax3_get(const EaxCall
& call
, const Eax3Props
& props
)
4663 switch (call
.get_property_id()) {
4664 case EAXSOURCE_NONE
:
4667 case EAXSOURCE_ALLPARAMETERS
:
4668 call
.set_value
<Exception
>(props
);
4671 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4672 eax3_get_obstruction(call
, props
);
4675 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4676 eax3_get_occlusion(call
, props
);
4679 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4680 eax3_get_exclusion(call
, props
);
4683 case EAXSOURCE_DIRECT
:
4684 call
.set_value
<Exception
>(props
.lDirect
);
4687 case EAXSOURCE_DIRECTHF
:
4688 call
.set_value
<Exception
>(props
.lDirectHF
);
4691 case EAXSOURCE_ROOM
:
4692 call
.set_value
<Exception
>(props
.lRoom
);
4695 case EAXSOURCE_ROOMHF
:
4696 call
.set_value
<Exception
>(props
.lRoomHF
);
4699 case EAXSOURCE_OBSTRUCTION
:
4700 call
.set_value
<Exception
>(props
.lObstruction
);
4703 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4704 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4707 case EAXSOURCE_OCCLUSION
:
4708 call
.set_value
<Exception
>(props
.lOcclusion
);
4711 case EAXSOURCE_OCCLUSIONLFRATIO
:
4712 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4715 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4716 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4719 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4720 call
.set_value
<Exception
>(props
.flOcclusionDirectRatio
);
4723 case EAXSOURCE_EXCLUSION
:
4724 call
.set_value
<Exception
>(props
.lExclusion
);
4727 case EAXSOURCE_EXCLUSIONLFRATIO
:
4728 call
.set_value
<Exception
>(props
.flExclusionLFRatio
);
4731 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4732 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4735 case EAXSOURCE_DOPPLERFACTOR
:
4736 call
.set_value
<Exception
>(props
.flDopplerFactor
);
4739 case EAXSOURCE_ROLLOFFFACTOR
:
4740 call
.set_value
<Exception
>(props
.flRolloffFactor
);
4743 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4744 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4747 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4748 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4751 case EAXSOURCE_FLAGS
:
4752 call
.set_value
<Exception
>(props
.ulFlags
);
4756 eax_fail_unknown_property_id();
4760 void ALsource::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
4762 switch (call
.get_property_id()) {
4763 case EAXSOURCE_NONE
:
4766 case EAXSOURCE_ALLPARAMETERS
:
4767 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4768 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4769 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4770 case EAXSOURCE_DIRECT
:
4771 case EAXSOURCE_DIRECTHF
:
4772 case EAXSOURCE_ROOM
:
4773 case EAXSOURCE_ROOMHF
:
4774 case EAXSOURCE_OBSTRUCTION
:
4775 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4776 case EAXSOURCE_OCCLUSION
:
4777 case EAXSOURCE_OCCLUSIONLFRATIO
:
4778 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4779 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4780 case EAXSOURCE_EXCLUSION
:
4781 case EAXSOURCE_EXCLUSIONLFRATIO
:
4782 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4783 case EAXSOURCE_DOPPLERFACTOR
:
4784 case EAXSOURCE_ROLLOFFFACTOR
:
4785 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4786 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4787 case EAXSOURCE_FLAGS
:
4788 eax3_get(call
, props
.source
);
4791 case EAXSOURCE_SENDPARAMETERS
:
4792 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4795 case EAXSOURCE_ALLSENDPARAMETERS
:
4796 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4799 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4800 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4803 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4804 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4807 case EAXSOURCE_ACTIVEFXSLOTID
:
4808 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4812 eax_fail_unknown_property_id();
4816 void ALsource::eax5_get_all_2d(const EaxCall
& call
, const EAX50SOURCEPROPERTIES
& props
)
4818 auto& subprops
= call
.get_value
<Exception
, EAXSOURCE2DPROPERTIES
>();
4819 subprops
.lDirect
= props
.lDirect
;
4820 subprops
.lDirectHF
= props
.lDirectHF
;
4821 subprops
.lRoom
= props
.lRoom
;
4822 subprops
.lRoomHF
= props
.lRoomHF
;
4823 subprops
.ulFlags
= props
.ulFlags
;
4826 void ALsource::eax5_get_speaker_levels(const EaxCall
& call
, const EaxSpeakerLevels
& props
)
4828 const auto subprops
= call
.get_values
<EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4829 std::uninitialized_copy_n(props
.cbegin(), subprops
.size(), subprops
.begin());
4832 void ALsource::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
4834 switch (call
.get_property_id()) {
4835 case EAXSOURCE_NONE
:
4838 case EAXSOURCE_ALLPARAMETERS
:
4839 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4840 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4841 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4842 case EAXSOURCE_DIRECT
:
4843 case EAXSOURCE_DIRECTHF
:
4844 case EAXSOURCE_ROOM
:
4845 case EAXSOURCE_ROOMHF
:
4846 case EAXSOURCE_OBSTRUCTION
:
4847 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4848 case EAXSOURCE_OCCLUSION
:
4849 case EAXSOURCE_OCCLUSIONLFRATIO
:
4850 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4851 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4852 case EAXSOURCE_EXCLUSION
:
4853 case EAXSOURCE_EXCLUSIONLFRATIO
:
4854 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4855 case EAXSOURCE_DOPPLERFACTOR
:
4856 case EAXSOURCE_ROLLOFFFACTOR
:
4857 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4858 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4859 case EAXSOURCE_FLAGS
:
4860 eax3_get(call
, props
.source
);
4863 case EAXSOURCE_SENDPARAMETERS
:
4864 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4867 case EAXSOURCE_ALLSENDPARAMETERS
:
4868 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4871 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4872 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4875 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4876 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4879 case EAXSOURCE_ACTIVEFXSLOTID
:
4880 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4883 case EAXSOURCE_MACROFXFACTOR
:
4884 call
.set_value
<Exception
>(props
.source
.flMacroFXFactor
);
4887 case EAXSOURCE_SPEAKERLEVELS
:
4888 call
.set_value
<Exception
>(props
.speaker_levels
);
4891 case EAXSOURCE_ALL2DPARAMETERS
:
4892 eax5_get_all_2d(call
, props
.source
);
4896 eax_fail_unknown_property_id();
4900 void ALsource::eax_get(const EaxCall
& call
)
4902 switch (call
.get_version()) {
4903 case 1: eax1_get(call
, mEax1
.i
); break;
4904 case 2: eax2_get(call
, mEax2
.i
); break;
4905 case 3: eax3_get(call
, mEax3
.i
); break;
4906 case 4: eax4_get(call
, mEax4
.i
); break;
4907 case 5: eax5_get(call
, mEax5
.i
); break;
4908 default: eax_fail_unknown_version();
4912 void ALsource::eax_set_al_source_send(ALeffectslot
*slot
, size_t sendidx
, const EaxAlLowPassParam
&filter
)
4914 if(sendidx
>= EAX_MAX_FXSLOTS
)
4917 auto &send
= Send
[sendidx
];
4918 send
.Gain
= filter
.gain
;
4919 send
.GainHF
= filter
.gain_hf
;
4920 send
.HFReference
= LowPassFreqRef
;
4922 send
.LFReference
= HighPassFreqRef
;
4925 IncrementRef(slot
->ref
);
4926 if(auto *oldslot
= send
.Slot
)
4927 DecrementRef(oldslot
->ref
);
4933 void ALsource::eax_commit_active_fx_slots()
4935 // Clear all slots to an inactive state.
4936 mEaxActiveFxSlots
.fill(false);
4938 // Mark the set slots as active.
4939 for(const auto& slot_id
: mEax
.active_fx_slots
.guidActiveFXSlots
)
4941 if(slot_id
== EAX_NULL_GUID
)
4944 else if(slot_id
== EAX_PrimaryFXSlotID
)
4946 // Mark primary FX slot as active.
4947 if(mEaxPrimaryFxSlotId
.has_value())
4948 mEaxActiveFxSlots
[*mEaxPrimaryFxSlotId
] = true;
4950 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot0
)
4951 mEaxActiveFxSlots
[0] = true;
4952 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot1
)
4953 mEaxActiveFxSlots
[1] = true;
4954 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot2
)
4955 mEaxActiveFxSlots
[2] = true;
4956 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot3
)
4957 mEaxActiveFxSlots
[3] = true;
4960 // Deactivate EFX auxiliary effect slots for inactive slots. Active slots
4961 // will be updated with the room filters.
4962 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4964 if(!mEaxActiveFxSlots
[i
])
4965 eax_set_al_source_send(nullptr, i
, EaxAlLowPassParam
{1.0f
, 1.0f
});
4969 void ALsource::eax_commit_filters()
4971 eax_update_direct_filter();
4972 eax_update_room_filters();
4975 void ALsource::eaxCommit()
4977 const auto primary_fx_slot_id
= mEaxAlContext
->eaxGetPrimaryFxSlotIndex();
4978 const auto is_primary_fx_slot_id_changed
= (mEaxPrimaryFxSlotId
!= primary_fx_slot_id
);
4980 if(!mEaxChanged
&& !is_primary_fx_slot_id_changed
)
4983 mEaxPrimaryFxSlotId
= primary_fx_slot_id
;
4984 mEaxChanged
= false;
4990 eax1_translate(mEax1
.i
, mEax
);
4994 eax2_translate(mEax2
.i
, mEax
);
4998 eax3_translate(mEax3
.i
, mEax
);
5002 eax4_translate(mEax4
.i
, mEax
);
5010 eax_set_efx_outer_gain_hf();
5011 eax_set_efx_doppler_factor();
5012 eax_set_efx_rolloff_factor();
5013 eax_set_efx_room_rolloff_factor();
5014 eax_set_efx_air_absorption_factor();
5015 eax_set_efx_dry_gain_hf_auto();
5016 eax_set_efx_wet_gain_auto();
5017 eax_set_efx_wet_gain_hf_auto();
5019 eax_commit_active_fx_slots();
5020 eax_commit_filters();
5023 #endif // ALSOFT_EAX