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 * throwing a context error if it fails.
1414 * The second lambda tests the validity of the value check, throwing a context
1415 * error if it failed.
1417 template<typename T
, size_t N
>
1418 auto GetCheckers(const SourceProp prop
, const al::span
<T
,N
> values
)
1420 return std::make_pair(
1421 [=](size_t expect
) -> void
1423 if(values
.size() == expect
) return;
1424 throw al::context_error
{AL_INVALID_ENUM
,
1425 "Property 0x%04x expects %zu value(s), got %zu", prop
, expect
, values
.size()};
1427 [](bool passed
) -> void
1430 throw al::context_error
{AL_INVALID_VALUE
, "Value out of range"};
1435 template<typename T
>
1436 NOINLINE
void SetProperty(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
1437 const al::span
<const T
> values
)
1439 auto [CheckSize
, CheckValue
] = GetCheckers(prop
, values
);
1440 ALCdevice
*device
{Context
->mALDevice
.get()};
1444 case AL_SOURCE_STATE
:
1445 case AL_SOURCE_TYPE
:
1446 case AL_BUFFERS_QUEUED
:
1447 case AL_BUFFERS_PROCESSED
:
1448 if constexpr(std::is_integral_v
<T
>)
1451 throw al::context_error
{AL_INVALID_OPERATION
,
1452 "Setting read-only source property 0x%04x", prop
};
1456 case AL_BYTE_LENGTH_SOFT
:
1457 case AL_SAMPLE_LENGTH_SOFT
:
1458 case AL_SEC_LENGTH_SOFT
:
1459 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1460 case AL_SEC_OFFSET_LATENCY_SOFT
:
1461 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1462 case AL_SEC_OFFSET_CLOCK_SOFT
:
1464 throw al::context_error
{AL_INVALID_OPERATION
, "Setting read-only source property 0x%04x",
1469 if constexpr(std::is_floating_point_v
<T
>)
1470 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1472 CheckValue(values
[0] >= T
{0});
1474 Source
->Pitch
= static_cast<float>(values
[0]);
1475 return UpdateSourceProps(Source
, Context
);
1477 case AL_CONE_INNER_ANGLE
:
1479 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{360});
1481 Source
->InnerAngle
= static_cast<float>(values
[0]);
1482 return CommitAndUpdateSourceProps(Source
, Context
);
1484 case AL_CONE_OUTER_ANGLE
:
1486 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{360});
1488 Source
->OuterAngle
= static_cast<float>(values
[0]);
1489 return CommitAndUpdateSourceProps(Source
, Context
);
1493 if constexpr(std::is_floating_point_v
<T
>)
1494 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1496 CheckValue(values
[0] >= T
{0});
1498 Source
->Gain
= static_cast<float>(values
[0]);
1499 return UpdateSourceProps(Source
, Context
);
1501 case AL_MAX_DISTANCE
:
1503 if constexpr(std::is_floating_point_v
<T
>)
1504 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1506 CheckValue(values
[0] >= T
{0});
1508 Source
->MaxDistance
= static_cast<float>(values
[0]);
1509 return CommitAndUpdateSourceProps(Source
, Context
);
1511 case AL_ROLLOFF_FACTOR
:
1513 if constexpr(std::is_floating_point_v
<T
>)
1514 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1516 CheckValue(values
[0] >= T
{0});
1518 Source
->RolloffFactor
= static_cast<float>(values
[0]);
1519 return CommitAndUpdateSourceProps(Source
, Context
);
1521 case AL_REFERENCE_DISTANCE
:
1523 if constexpr(std::is_floating_point_v
<T
>)
1524 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1526 CheckValue(values
[0] >= T
{0});
1528 Source
->RefDistance
= static_cast<float>(values
[0]);
1529 return CommitAndUpdateSourceProps(Source
, Context
);
1533 if constexpr(std::is_floating_point_v
<T
>)
1534 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1536 CheckValue(values
[0] >= T
{0});
1538 Source
->MinGain
= static_cast<float>(values
[0]);
1539 return UpdateSourceProps(Source
, Context
);
1543 if constexpr(std::is_floating_point_v
<T
>)
1544 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1546 CheckValue(values
[0] >= T
{0});
1548 Source
->MaxGain
= static_cast<float>(values
[0]);
1549 return UpdateSourceProps(Source
, Context
);
1551 case AL_CONE_OUTER_GAIN
:
1553 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1555 Source
->OuterGain
= static_cast<float>(values
[0]);
1556 return UpdateSourceProps(Source
, Context
);
1558 case AL_CONE_OUTER_GAINHF
:
1560 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1562 Source
->OuterGainHF
= static_cast<float>(values
[0]);
1563 return UpdateSourceProps(Source
, Context
);
1565 case AL_AIR_ABSORPTION_FACTOR
:
1567 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{10});
1569 Source
->AirAbsorptionFactor
= static_cast<float>(values
[0]);
1570 return UpdateSourceProps(Source
, Context
);
1572 case AL_ROOM_ROLLOFF_FACTOR
:
1574 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1576 Source
->RoomRolloffFactor
= static_cast<float>(values
[0]);
1577 return UpdateSourceProps(Source
, Context
);
1579 case AL_DOPPLER_FACTOR
:
1581 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1583 Source
->DopplerFactor
= static_cast<float>(values
[0]);
1584 return UpdateSourceProps(Source
, Context
);
1587 case AL_SOURCE_RELATIVE
:
1588 if constexpr(std::is_integral_v
<T
>)
1591 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1593 Source
->HeadRelative
= values
[0] != AL_FALSE
;
1594 return CommitAndUpdateSourceProps(Source
, Context
);
1599 if constexpr(std::is_integral_v
<T
>)
1602 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1604 Source
->Looping
= values
[0] != AL_FALSE
;
1605 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1608 voice
->mLoopBuffer
.store(&Source
->mQueue
.front(), std::memory_order_release
);
1610 voice
->mLoopBuffer
.store(nullptr, std::memory_order_release
);
1612 /* If the source is playing, wait for the current mix to finish
1613 * to ensure it isn't currently looping back or reaching the
1616 std::ignore
= device
->waitForMix();
1623 if constexpr(std::is_integral_v
<T
>)
1626 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1627 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1628 throw al::context_error
{AL_INVALID_OPERATION
,
1629 "Setting buffer on playing or paused source %u", Source
->id
};
1631 std::deque
<ALbufferQueueItem
> oldlist
;
1634 using UT
= std::make_unsigned_t
<T
>;
1635 std::lock_guard
<std::mutex
> buflock
{device
->BufferLock
};
1636 ALbuffer
*buffer
{LookupBuffer(device
, static_cast<UT
>(values
[0]))};
1638 throw al::context_error
{AL_INVALID_VALUE
, "Invalid buffer ID %s",
1639 std::to_string(values
[0]).c_str()};
1640 if(buffer
->MappedAccess
&& !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
1641 throw al::context_error
{AL_INVALID_OPERATION
,
1642 "Setting non-persistently mapped buffer %u", buffer
->id
};
1643 if(buffer
->mCallback
&& buffer
->ref
.load(std::memory_order_relaxed
) != 0)
1644 throw al::context_error
{AL_INVALID_OPERATION
,
1645 "Setting already-set callback buffer %u", buffer
->id
};
1647 /* Add the selected buffer to a one-item queue */
1648 std::deque
<ALbufferQueueItem
> newlist
;
1649 newlist
.emplace_back();
1650 newlist
.back().mCallback
= buffer
->mCallback
;
1651 newlist
.back().mUserData
= buffer
->mUserData
;
1652 newlist
.back().mBlockAlign
= buffer
->mBlockAlign
;
1653 newlist
.back().mSampleLen
= buffer
->mSampleLen
;
1654 newlist
.back().mLoopStart
= buffer
->mLoopStart
;
1655 newlist
.back().mLoopEnd
= buffer
->mLoopEnd
;
1656 newlist
.back().mSamples
= buffer
->mData
;
1657 newlist
.back().mBuffer
= buffer
;
1658 IncrementRef(buffer
->ref
);
1660 /* Source is now Static */
1661 Source
->SourceType
= AL_STATIC
;
1662 Source
->mQueue
.swap(oldlist
);
1663 Source
->mQueue
.swap(newlist
);
1667 /* Source is now Undetermined */
1668 Source
->SourceType
= AL_UNDETERMINED
;
1669 Source
->mQueue
.swap(oldlist
);
1672 /* Delete all elements in the previous queue */
1673 for(auto &item
: oldlist
)
1675 if(ALbuffer
*buffer
{item
.mBuffer
})
1676 DecrementRef(buffer
->ref
);
1684 case AL_SAMPLE_OFFSET
:
1685 case AL_BYTE_OFFSET
:
1687 if constexpr(std::is_floating_point_v
<T
>)
1688 CheckValue(std::isfinite(values
[0]));
1690 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1692 auto vpos
= GetSampleOffset(Source
->mQueue
, prop
, static_cast<double>(values
[0]));
1693 if(!vpos
) throw al::context_error
{AL_INVALID_VALUE
, "Invalid offset"};
1695 if(SetVoiceOffset(voice
, *vpos
, Source
, Context
, Context
->mALDevice
.get()))
1698 Source
->OffsetType
= prop
;
1699 Source
->Offset
= static_cast<double>(values
[0]);
1702 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1703 if(sBufferSubDataCompat
)
1705 if constexpr(std::is_integral_v
<T
>)
1708 throw al::context_error
{AL_INVALID_OPERATION
,
1709 "Setting read-only source property 0x%04x", prop
};
1714 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1715 if(sBufferSubDataCompat
)
1717 if constexpr(std::is_integral_v
<T
>)
1720 throw al::context_error
{AL_INVALID_OPERATION
,
1721 "Setting read-only source property 0x%04x", prop
};
1726 if constexpr(std::is_floating_point_v
<T
>)
1727 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1729 CheckValue(values
[0] >= T
{0});
1731 Source
->Radius
= static_cast<float>(values
[0]);
1732 return UpdateSourceProps(Source
, Context
);
1734 case AL_SUPER_STEREO_WIDTH_SOFT
:
1736 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1738 Source
->EnhWidth
= static_cast<float>(values
[0]);
1739 return UpdateSourceProps(Source
, Context
);
1741 case AL_PANNING_ENABLED_SOFT
:
1743 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1744 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1745 throw al::context_error
{AL_INVALID_OPERATION
,
1746 "Modifying panning enabled on playing or paused source %u", Source
->id
};
1748 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1750 Source
->mPanningEnabled
= values
[0] != AL_FALSE
;
1751 return UpdateSourceProps(Source
, Context
);
1755 CheckValue(values
[0] >= T
{-1} && values
[0] <= T
{1});
1757 Source
->mPan
= static_cast<float>(values
[0]);
1758 return UpdateSourceProps(Source
, Context
);
1760 case AL_STEREO_ANGLES
:
1762 if constexpr(std::is_floating_point_v
<T
>)
1763 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1764 && std::isfinite(static_cast<float>(values
[1])));
1766 Source
->StereoPan
[0] = static_cast<float>(values
[0]);
1767 Source
->StereoPan
[1] = static_cast<float>(values
[1]);
1768 return UpdateSourceProps(Source
, Context
);
1773 if constexpr(std::is_floating_point_v
<T
>)
1774 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1775 && std::isfinite(static_cast<float>(values
[1]))
1776 && std::isfinite(static_cast<float>(values
[2])));
1778 Source
->Position
[0] = static_cast<float>(values
[0]);
1779 Source
->Position
[1] = static_cast<float>(values
[1]);
1780 Source
->Position
[2] = static_cast<float>(values
[2]);
1781 return CommitAndUpdateSourceProps(Source
, Context
);
1785 if constexpr(std::is_floating_point_v
<T
>)
1786 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1787 && std::isfinite(static_cast<float>(values
[1]))
1788 && std::isfinite(static_cast<float>(values
[2])));
1790 Source
->Velocity
[0] = static_cast<float>(values
[0]);
1791 Source
->Velocity
[1] = static_cast<float>(values
[1]);
1792 Source
->Velocity
[2] = static_cast<float>(values
[2]);
1793 return CommitAndUpdateSourceProps(Source
, Context
);
1797 if constexpr(std::is_floating_point_v
<T
>)
1798 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1799 && std::isfinite(static_cast<float>(values
[1]))
1800 && std::isfinite(static_cast<float>(values
[2])));
1802 Source
->Direction
[0] = static_cast<float>(values
[0]);
1803 Source
->Direction
[1] = static_cast<float>(values
[1]);
1804 Source
->Direction
[2] = static_cast<float>(values
[2]);
1805 return CommitAndUpdateSourceProps(Source
, Context
);
1807 case AL_ORIENTATION
:
1809 if constexpr(std::is_floating_point_v
<T
>)
1810 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1811 && std::isfinite(static_cast<float>(values
[1]))
1812 && std::isfinite(static_cast<float>(values
[2]))
1813 && std::isfinite(static_cast<float>(values
[3]))
1814 && std::isfinite(static_cast<float>(values
[4]))
1815 && std::isfinite(static_cast<float>(values
[5])));
1817 Source
->OrientAt
[0] = static_cast<float>(values
[0]);
1818 Source
->OrientAt
[1] = static_cast<float>(values
[1]);
1819 Source
->OrientAt
[2] = static_cast<float>(values
[2]);
1820 Source
->OrientUp
[0] = static_cast<float>(values
[3]);
1821 Source
->OrientUp
[1] = static_cast<float>(values
[4]);
1822 Source
->OrientUp
[2] = static_cast<float>(values
[5]);
1823 return UpdateSourceProps(Source
, Context
);
1826 case AL_DIRECT_FILTER
:
1827 if constexpr(std::is_integral_v
<T
>)
1830 const auto filterid
= static_cast<std::make_unsigned_t
<T
>>(values
[0]);
1833 std::lock_guard
<std::mutex
> filterlock
{device
->FilterLock
};
1834 ALfilter
*filter
{LookupFilter(device
, filterid
)};
1836 throw al::context_error
{AL_INVALID_VALUE
, "Invalid filter ID %s",
1837 std::to_string(filterid
).c_str()};
1838 Source
->Direct
.Gain
= filter
->Gain
;
1839 Source
->Direct
.GainHF
= filter
->GainHF
;
1840 Source
->Direct
.HFReference
= filter
->HFReference
;
1841 Source
->Direct
.GainLF
= filter
->GainLF
;
1842 Source
->Direct
.LFReference
= filter
->LFReference
;
1846 Source
->Direct
.Gain
= 1.0f
;
1847 Source
->Direct
.GainHF
= 1.0f
;
1848 Source
->Direct
.HFReference
= LowPassFreqRef
;
1849 Source
->Direct
.GainLF
= 1.0f
;
1850 Source
->Direct
.LFReference
= HighPassFreqRef
;
1852 return UpdateSourceProps(Source
, Context
);
1856 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1857 if constexpr(std::is_integral_v
<T
>)
1860 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1862 Source
->DryGainHFAuto
= values
[0] != AL_FALSE
;
1863 return UpdateSourceProps(Source
, Context
);
1867 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1868 if constexpr(std::is_integral_v
<T
>)
1871 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1873 Source
->WetGainAuto
= values
[0] != AL_FALSE
;
1874 return UpdateSourceProps(Source
, Context
);
1878 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1879 if constexpr(std::is_integral_v
<T
>)
1882 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1884 Source
->WetGainHFAuto
= values
[0] != AL_FALSE
;
1885 return UpdateSourceProps(Source
, Context
);
1889 case AL_DIRECT_CHANNELS_SOFT
:
1890 if constexpr(std::is_integral_v
<T
>)
1893 if(auto mode
= DirectModeFromEnum(values
[0]))
1895 Source
->DirectChannels
= *mode
;
1896 return UpdateSourceProps(Source
, Context
);
1898 throw al::context_error
{AL_INVALID_VALUE
, "Invalid direct channels mode: %s\n",
1899 HexPrinter
{values
[0]}.c_str()};
1903 case AL_DISTANCE_MODEL
:
1904 if constexpr(std::is_integral_v
<T
>)
1907 if(auto model
= DistanceModelFromALenum(values
[0]))
1909 Source
->mDistanceModel
= *model
;
1910 if(Context
->mSourceDistanceModel
)
1911 UpdateSourceProps(Source
, Context
);
1914 throw al::context_error
{AL_INVALID_VALUE
, "Invalid distance model: %s\n",
1915 HexPrinter
{values
[0]}.c_str()};
1919 case AL_SOURCE_RESAMPLER_SOFT
:
1920 if constexpr(std::is_integral_v
<T
>)
1923 CheckValue(values
[0] >= 0 && values
[0] <= static_cast<int>(Resampler::Max
));
1925 Source
->mResampler
= static_cast<Resampler
>(values
[0]);
1926 return UpdateSourceProps(Source
, Context
);
1930 case AL_SOURCE_SPATIALIZE_SOFT
:
1931 if constexpr(std::is_integral_v
<T
>)
1934 if(auto mode
= SpatializeModeFromEnum(values
[0]))
1936 Source
->mSpatialize
= *mode
;
1937 return UpdateSourceProps(Source
, Context
);
1939 throw al::context_error
{AL_INVALID_VALUE
, "Invalid source spatialize mode: %s\n",
1940 HexPrinter
{values
[0]}.c_str()};
1944 case AL_STEREO_MODE_SOFT
:
1945 if constexpr(std::is_integral_v
<T
>)
1948 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1949 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1950 throw al::context_error
{AL_INVALID_OPERATION
,
1951 "Modifying stereo mode on playing or paused source %u", Source
->id
};
1953 if(auto mode
= StereoModeFromEnum(values
[0]))
1955 Source
->mStereoMode
= *mode
;
1958 throw al::context_error
{AL_INVALID_VALUE
, "Invalid stereo mode: %s\n",
1959 HexPrinter
{values
[0]}.c_str()};
1963 case AL_AUXILIARY_SEND_FILTER
:
1964 if constexpr(std::is_integral_v
<T
>)
1967 const auto slotid
= static_cast<std::make_unsigned_t
<T
>>(values
[0]);
1968 const auto sendidx
= static_cast<std::make_unsigned_t
<T
>>(values
[1]);
1969 const auto filterid
= static_cast<std::make_unsigned_t
<T
>>(values
[2]);
1971 std::unique_lock slotlock
{Context
->mEffectSlotLock
};
1972 ALeffectslot
*slot
{};
1975 slot
= LookupEffectSlot(Context
, slotid
);
1977 throw al::context_error
{AL_INVALID_VALUE
, "Invalid effect ID %s",
1978 std::to_string(slotid
).c_str()};
1981 if(sendidx
>= device
->NumAuxSends
)
1982 throw al::context_error
{AL_INVALID_VALUE
, "Invalid send %s",
1983 std::to_string(sendidx
).c_str()};
1984 auto &send
= Source
->Send
[static_cast<size_t>(sendidx
)];
1988 std::lock_guard
<std::mutex
> filterlock
{device
->FilterLock
};
1989 ALfilter
*filter
{LookupFilter(device
, filterid
)};
1991 throw al::context_error
{AL_INVALID_VALUE
, "Invalid filter ID %s",
1992 std::to_string(filterid
).c_str()};
1994 send
.Gain
= filter
->Gain
;
1995 send
.GainHF
= filter
->GainHF
;
1996 send
.HFReference
= filter
->HFReference
;
1997 send
.GainLF
= filter
->GainLF
;
1998 send
.LFReference
= filter
->LFReference
;
2002 /* Disable filter */
2005 send
.HFReference
= LowPassFreqRef
;
2007 send
.LFReference
= HighPassFreqRef
;
2010 /* We must force an update if the current auxiliary slot is valid
2011 * and about to be changed on an active source, in case the old
2012 * slot is about to be deleted.
2014 if(send
.Slot
&& slot
!= send
.Slot
&& IsPlayingOrPaused(Source
))
2016 /* Add refcount on the new slot, and release the previous slot */
2017 if(slot
) IncrementRef(slot
->ref
);
2018 if(auto *oldslot
= send
.Slot
)
2019 DecrementRef(oldslot
->ref
);
2022 Voice
*voice
{GetSourceVoice(Source
, Context
)};
2023 if(voice
) UpdateSourceProps(Source
, voice
, Context
);
2024 else Source
->mPropsDirty
= true;
2028 if(slot
) IncrementRef(slot
->ref
);
2029 if(auto *oldslot
= send
.Slot
)
2030 DecrementRef(oldslot
->ref
);
2032 UpdateSourceProps(Source
, Context
);
2039 ERR("Unexpected %s property: 0x%04x\n", PropType
<T
>::Name(), prop
);
2040 throw al::context_error
{AL_INVALID_ENUM
, "Invalid source %s property 0x%04x",
2041 PropType
<T
>::Name(), prop
};
2045 template<typename T
, size_t N
>
2046 auto GetSizeChecker(const SourceProp prop
, const al::span
<T
,N
> values
)
2048 return [=](size_t expect
) -> void
2050 if(values
.size() == expect
) LIKELY
return;
2051 throw al::context_error
{AL_INVALID_ENUM
, "Property 0x%04x expects %zu value(s), got %zu",
2052 prop
, expect
, values
.size()};
2056 template<typename T
>
2057 NOINLINE
void GetProperty(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
2058 const al::span
<T
> values
)
2060 using std::chrono::duration_cast
;
2061 auto CheckSize
= GetSizeChecker(prop
, values
);
2062 ALCdevice
*device
{Context
->mALDevice
.get()};
2068 values
[0] = static_cast<T
>(Source
->Gain
);
2073 values
[0] = static_cast<T
>(Source
->Pitch
);
2076 case AL_MAX_DISTANCE
:
2078 values
[0] = static_cast<T
>(Source
->MaxDistance
);
2081 case AL_ROLLOFF_FACTOR
:
2083 values
[0] = static_cast<T
>(Source
->RolloffFactor
);
2086 case AL_REFERENCE_DISTANCE
:
2088 values
[0] = static_cast<T
>(Source
->RefDistance
);
2091 case AL_CONE_INNER_ANGLE
:
2093 values
[0] = static_cast<T
>(Source
->InnerAngle
);
2096 case AL_CONE_OUTER_ANGLE
:
2098 values
[0] = static_cast<T
>(Source
->OuterAngle
);
2103 values
[0] = static_cast<T
>(Source
->MinGain
);
2108 values
[0] = static_cast<T
>(Source
->MaxGain
);
2111 case AL_CONE_OUTER_GAIN
:
2113 values
[0] = static_cast<T
>(Source
->OuterGain
);
2117 case AL_SAMPLE_OFFSET
:
2118 case AL_BYTE_OFFSET
:
2120 values
[0] = GetSourceOffset
<T
>(Source
, prop
, Context
);
2123 case AL_CONE_OUTER_GAINHF
:
2125 values
[0] = static_cast<T
>(Source
->OuterGainHF
);
2128 case AL_AIR_ABSORPTION_FACTOR
:
2130 values
[0] = static_cast<T
>(Source
->AirAbsorptionFactor
);
2133 case AL_ROOM_ROLLOFF_FACTOR
:
2135 values
[0] = static_cast<T
>(Source
->RoomRolloffFactor
);
2138 case AL_DOPPLER_FACTOR
:
2140 values
[0] = static_cast<T
>(Source
->DopplerFactor
);
2143 case AL_SAMPLE_RW_OFFSETS_SOFT
:
2144 if constexpr(std::is_integral_v
<T
>)
2146 if(sBufferSubDataCompat
)
2149 values
[0] = GetSourceOffset
<T
>(Source
, AL_SAMPLE_OFFSET
, Context
);
2150 /* FIXME: values[1] should be ahead of values[0] by the device
2151 * update time. It needs to clamp or wrap the length of the
2154 values
[1] = values
[0];
2159 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
2160 if constexpr(std::is_floating_point_v
<T
>)
2162 if(sBufferSubDataCompat
)
2166 values
[0] = static_cast<T
>(Source
->Radius
);
2171 if(sBufferSubDataCompat
)
2174 values
[0] = GetSourceOffset
<T
>(Source
, AL_BYTE_OFFSET
, Context
);
2175 /* FIXME: values[1] should be ahead of values[0] by the device
2176 * update time. It needs to clamp or wrap the length of the
2179 values
[1] = values
[0];
2185 case AL_SUPER_STEREO_WIDTH_SOFT
:
2187 values
[0] = static_cast<T
>(Source
->EnhWidth
);
2190 case AL_BYTE_LENGTH_SOFT
:
2191 case AL_SAMPLE_LENGTH_SOFT
:
2192 case AL_SEC_LENGTH_SOFT
:
2194 values
[0] = GetSourceLength
<T
>(Source
, prop
);
2197 case AL_PANNING_ENABLED_SOFT
:
2199 values
[0] = Source
->mPanningEnabled
;
2204 values
[0] = static_cast<T
>(Source
->mPan
);
2207 case AL_STEREO_ANGLES
:
2208 if constexpr(std::is_floating_point_v
<T
>)
2211 values
[0] = static_cast<T
>(Source
->StereoPan
[0]);
2212 values
[1] = static_cast<T
>(Source
->StereoPan
[1]);
2217 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
2218 if constexpr(std::is_same_v
<T
,int64_t>)
2221 /* Get the source offset with the clock time first. Then get the
2222 * clock time with the device latency. Order is important.
2224 ClockLatency clocktime
{};
2225 nanoseconds srcclock
{};
2226 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2228 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2229 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2231 if(srcclock
== clocktime
.ClockTime
)
2232 values
[1] = nanoseconds
{clocktime
.Latency
}.count();
2235 /* If the clock time incremented, reduce the latency by that
2236 * much since it's that much closer to the source offset it got
2239 const auto diff
= std::min(clocktime
.Latency
, clocktime
.ClockTime
-srcclock
);
2240 values
[1] = nanoseconds
{clocktime
.Latency
- diff
}.count();
2246 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
2247 if constexpr(std::is_same_v
<T
,int64_t>)
2250 nanoseconds srcclock
{};
2251 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2252 values
[1] = srcclock
.count();
2257 case AL_SEC_OFFSET_LATENCY_SOFT
:
2258 if constexpr(std::is_same_v
<T
,double>)
2261 /* Get the source offset with the clock time first. Then get the
2262 * clock time with the device latency. Order is important.
2264 ClockLatency clocktime
{};
2265 nanoseconds srcclock
{};
2266 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2268 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2269 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2271 if(srcclock
== clocktime
.ClockTime
)
2272 values
[1] = duration_cast
<seconds_d
>(clocktime
.Latency
).count();
2275 /* If the clock time incremented, reduce the latency by that
2276 * much since it's that much closer to the source offset it got
2279 const auto diff
= std::min(clocktime
.Latency
, clocktime
.ClockTime
-srcclock
);
2280 values
[1] = duration_cast
<seconds_d
>(clocktime
.Latency
- diff
).count();
2286 case AL_SEC_OFFSET_CLOCK_SOFT
:
2287 if constexpr(std::is_same_v
<T
,double>)
2290 nanoseconds srcclock
{};
2291 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2292 values
[1] = duration_cast
<seconds_d
>(srcclock
).count();
2299 values
[0] = static_cast<T
>(Source
->Position
[0]);
2300 values
[1] = static_cast<T
>(Source
->Position
[1]);
2301 values
[2] = static_cast<T
>(Source
->Position
[2]);
2306 values
[0] = static_cast<T
>(Source
->Velocity
[0]);
2307 values
[1] = static_cast<T
>(Source
->Velocity
[1]);
2308 values
[2] = static_cast<T
>(Source
->Velocity
[2]);
2313 values
[0] = static_cast<T
>(Source
->Direction
[0]);
2314 values
[1] = static_cast<T
>(Source
->Direction
[1]);
2315 values
[2] = static_cast<T
>(Source
->Direction
[2]);
2318 case AL_ORIENTATION
:
2320 values
[0] = static_cast<T
>(Source
->OrientAt
[0]);
2321 values
[1] = static_cast<T
>(Source
->OrientAt
[1]);
2322 values
[2] = static_cast<T
>(Source
->OrientAt
[2]);
2323 values
[3] = static_cast<T
>(Source
->OrientUp
[0]);
2324 values
[4] = static_cast<T
>(Source
->OrientUp
[1]);
2325 values
[5] = static_cast<T
>(Source
->OrientUp
[2]);
2329 case AL_SOURCE_RELATIVE
:
2330 if constexpr(std::is_integral_v
<T
>)
2333 values
[0] = Source
->HeadRelative
;
2339 if constexpr(std::is_integral_v
<T
>)
2342 values
[0] = Source
->Looping
;
2348 if constexpr(std::is_integral_v
<T
>)
2351 const ALbufferQueueItem
*BufferList
{};
2352 /* HACK: This query should technically only return the buffer set
2353 * on a static source. However, some apps had used it to detect
2354 * when a streaming source changed buffers, so report the current
2355 * buffer's ID when playing.
2357 if(Source
->SourceType
== AL_STATIC
|| Source
->state
== AL_INITIAL
)
2359 if(!Source
->mQueue
.empty())
2360 BufferList
= &Source
->mQueue
.front();
2362 else if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2364 VoiceBufferItem
*Current
{voice
->mCurrentBuffer
.load(std::memory_order_relaxed
)};
2365 const auto iter
= std::find_if(Source
->mQueue
.cbegin(), Source
->mQueue
.cend(),
2366 [Current
](const ALbufferQueueItem
&item
) noexcept
-> bool
2367 { return &item
== Current
; });
2368 BufferList
= (iter
!= Source
->mQueue
.cend()) ? al::to_address(iter
) : nullptr;
2370 ALbuffer
*buffer
{BufferList
? BufferList
->mBuffer
: nullptr};
2371 values
[0] = buffer
? static_cast<T
>(buffer
->id
) : T
{0};
2376 case AL_SOURCE_STATE
:
2377 if constexpr(std::is_integral_v
<T
>)
2380 values
[0] = GetSourceState(Source
, GetSourceVoice(Source
, Context
));
2385 case AL_BUFFERS_QUEUED
:
2386 if constexpr(std::is_integral_v
<T
>)
2389 values
[0] = static_cast<T
>(Source
->mQueue
.size());
2394 case AL_BUFFERS_PROCESSED
:
2395 if constexpr(std::is_integral_v
<T
>)
2398 if(Source
->Looping
|| Source
->SourceType
!= AL_STREAMING
)
2400 /* Buffers on a looping source are in a perpetual state of
2401 * PENDING, so don't report any as PROCESSED
2408 if(Source
->state
!= AL_INITIAL
)
2410 const VoiceBufferItem
*Current
{nullptr};
2411 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2412 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
2413 for(auto &item
: Source
->mQueue
)
2415 if(&item
== Current
)
2426 case AL_SOURCE_TYPE
:
2427 if constexpr(std::is_integral_v
<T
>)
2430 values
[0] = Source
->SourceType
;
2435 case AL_DIRECT_FILTER_GAINHF_AUTO
:
2436 if constexpr(std::is_integral_v
<T
>)
2439 values
[0] = Source
->DryGainHFAuto
;
2444 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
2445 if constexpr(std::is_integral_v
<T
>)
2448 values
[0] = Source
->WetGainAuto
;
2453 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
2454 if constexpr(std::is_integral_v
<T
>)
2457 values
[0] = Source
->WetGainHFAuto
;
2462 case AL_DIRECT_CHANNELS_SOFT
:
2463 if constexpr(std::is_integral_v
<T
>)
2466 values
[0] = EnumFromDirectMode(Source
->DirectChannels
);
2471 case AL_DISTANCE_MODEL
:
2472 if constexpr(std::is_integral_v
<T
>)
2475 values
[0] = ALenumFromDistanceModel(Source
->mDistanceModel
);
2480 case AL_SOURCE_RESAMPLER_SOFT
:
2481 if constexpr(std::is_integral_v
<T
>)
2484 values
[0] = static_cast<T
>(Source
->mResampler
);
2489 case AL_SOURCE_SPATIALIZE_SOFT
:
2490 if constexpr(std::is_integral_v
<T
>)
2493 values
[0] = EnumFromSpatializeMode(Source
->mSpatialize
);
2498 case AL_STEREO_MODE_SOFT
:
2499 if constexpr(std::is_integral_v
<T
>)
2502 values
[0] = EnumFromStereoMode(Source
->mStereoMode
);
2507 case AL_DIRECT_FILTER
:
2508 case AL_AUXILIARY_SEND_FILTER
:
2512 ERR("Unexpected %s query property: 0x%04x\n", PropType
<T
>::Name(), prop
);
2513 throw al::context_error
{AL_INVALID_ENUM
, "Invalid source %s query property 0x%04x",
2514 PropType
<T
>::Name(), prop
};
2518 void StartSources(ALCcontext
*const context
, const al::span
<ALsource
*> srchandles
,
2519 const nanoseconds start_time
=nanoseconds::min())
2521 ALCdevice
*device
{context
->mALDevice
.get()};
2522 /* If the device is disconnected, and voices stop on disconnect, go right
2525 if(!device
->Connected
.load(std::memory_order_acquire
)) UNLIKELY
2527 if(context
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
2529 for(ALsource
*source
: srchandles
)
2531 /* TODO: Send state change event? */
2532 source
->Offset
= 0.0;
2533 source
->OffsetType
= AL_NONE
;
2534 source
->state
= AL_STOPPED
;
2540 /* Count the number of reusable voices. */
2541 auto voicelist
= context
->getVoicesSpan();
2542 size_t free_voices
{0};
2543 for(const Voice
*voice
: voicelist
)
2545 free_voices
+= (voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2546 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2547 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false);
2548 if(free_voices
== srchandles
.size())
2551 if(srchandles
.size() != free_voices
) UNLIKELY
2553 const size_t inc_amount
{srchandles
.size() - free_voices
};
2554 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
2555 if(inc_amount
> allvoices
.size() - voicelist
.size())
2557 /* Increase the number of voices to handle the request. */
2558 context
->allocVoices(inc_amount
- (allvoices
.size() - voicelist
.size()));
2560 context
->mActiveVoiceCount
.fetch_add(inc_amount
, std::memory_order_release
);
2561 voicelist
= context
->getVoicesSpan();
2564 auto voiceiter
= voicelist
.begin();
2566 VoiceChange
*tail
{}, *cur
{};
2567 for(ALsource
*source
: srchandles
)
2569 /* Check that there is a queue containing at least one valid, non zero
2572 auto find_buffer
= [](ALbufferQueueItem
&entry
) noexcept
2573 { return entry
.mSampleLen
!= 0 || entry
.mCallback
!= nullptr; };
2574 auto BufferList
= std::find_if(source
->mQueue
.begin(), source
->mQueue
.end(), find_buffer
);
2576 /* If there's nothing to play, go right to stopped. */
2577 if(BufferList
== source
->mQueue
.end()) UNLIKELY
2579 /* NOTE: A source without any playable buffers should not have a
2580 * Voice since it shouldn't be in a playing or paused state. So
2581 * there's no need to look up its voice and clear the source.
2583 source
->Offset
= 0.0;
2584 source
->OffsetType
= AL_NONE
;
2585 source
->state
= AL_STOPPED
;
2590 cur
= tail
= GetVoiceChanger(context
);
2593 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
2594 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
2597 Voice
*voice
{GetSourceVoice(source
, context
)};
2598 switch(GetSourceState(source
, voice
))
2601 /* A source that's paused simply resumes. If there's no voice, it
2602 * was lost from a disconnect, so just start over with a new one.
2604 cur
->mOldVoice
= nullptr;
2606 cur
->mVoice
= voice
;
2607 cur
->mSourceID
= source
->id
;
2608 cur
->mState
= VChangeState::Play
;
2609 source
->state
= AL_PLAYING
;
2611 if(context
->hasEax())
2612 source
->eaxCommit();
2613 #endif // ALSOFT_EAX
2617 /* A source that's already playing is restarted from the beginning.
2618 * Stop the current voice and start a new one so it properly cross-
2619 * fades back to the beginning.
2622 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
2623 cur
->mOldVoice
= voice
;
2628 assert(voice
== nullptr);
2629 cur
->mOldVoice
= nullptr;
2631 if(context
->hasEax())
2632 source
->eaxCommit();
2633 #endif // ALSOFT_EAX
2637 /* Find the next unused voice to play this source with. */
2638 for(;voiceiter
!= voicelist
.end();++voiceiter
,++vidx
)
2640 Voice
*v
{*voiceiter
};
2641 if(v
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2642 && v
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2643 && v
->mPendingChange
.load(std::memory_order_relaxed
) == false)
2649 ASSUME(voice
!= nullptr);
2651 voice
->mPosition
.store(0, std::memory_order_relaxed
);
2652 voice
->mPositionFrac
.store(0, std::memory_order_relaxed
);
2653 voice
->mCurrentBuffer
.store(&source
->mQueue
.front(), std::memory_order_relaxed
);
2654 voice
->mStartTime
= start_time
;
2655 voice
->mFlags
.reset();
2656 /* A source that's not playing or paused has any offset applied when it
2659 if(const ALenum offsettype
{source
->OffsetType
})
2661 const double offset
{source
->Offset
};
2662 source
->OffsetType
= AL_NONE
;
2663 source
->Offset
= 0.0;
2664 if(auto vpos
= GetSampleOffset(source
->mQueue
, offsettype
, offset
))
2666 voice
->mPosition
.store(vpos
->pos
, std::memory_order_relaxed
);
2667 voice
->mPositionFrac
.store(vpos
->frac
, std::memory_order_relaxed
);
2668 voice
->mCurrentBuffer
.store(vpos
->bufferitem
, std::memory_order_relaxed
);
2669 if(vpos
->pos
> 0 || (vpos
->pos
== 0 && vpos
->frac
> 0)
2670 || vpos
->bufferitem
!= &source
->mQueue
.front())
2671 voice
->mFlags
.set(VoiceIsFading
);
2674 InitVoice(voice
, source
, al::to_address(BufferList
), context
, device
);
2676 source
->VoiceIdx
= vidx
;
2677 source
->state
= AL_PLAYING
;
2679 cur
->mVoice
= voice
;
2680 cur
->mSourceID
= source
->id
;
2681 cur
->mState
= VChangeState::Play
;
2684 SendVoiceChanges(context
, tail
);
2689 AL_API
DECL_FUNC2(void, alGenSources
, ALsizei
,n
, ALuint
*,sources
)
2690 FORCE_ALIGN
void AL_APIENTRY
alGenSourcesDirect(ALCcontext
*context
, ALsizei n
, ALuint
*sources
) noexcept
2693 throw al::context_error
{AL_INVALID_VALUE
, "Generating %d sources", n
};
2694 if(n
<= 0) UNLIKELY
return;
2696 std::unique_lock
<std::mutex
> srclock
{context
->mSourceLock
};
2697 ALCdevice
*device
{context
->mALDevice
.get()};
2699 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2700 if(sids
.size() > device
->SourcesMax
-context
->mNumSources
)
2701 throw al::context_error
{AL_OUT_OF_MEMORY
, "Exceeding %u source limit (%u + %d)",
2702 device
->SourcesMax
, context
->mNumSources
, n
};
2703 if(!EnsureSources(context
, sids
.size()))
2704 throw al::context_error
{AL_OUT_OF_MEMORY
, "Failed to allocate %d source%s", n
,
2705 (n
== 1) ? "" : "s"};
2707 std::generate(sids
.begin(), sids
.end(), [context
]{ return AllocSource(context
)->id
; });
2709 catch(al::context_error
& e
) {
2710 context
->setError(e
.errorCode(), "%s", e
.what());
2713 AL_API
DECL_FUNC2(void, alDeleteSources
, ALsizei
,n
, const ALuint
*,sources
)
2714 FORCE_ALIGN
void AL_APIENTRY
alDeleteSourcesDirect(ALCcontext
*context
, ALsizei n
,
2715 const ALuint
*sources
) noexcept
2718 throw al::context_error
{AL_INVALID_VALUE
, "Deleting %d sources", n
};
2719 if(n
<= 0) UNLIKELY
return;
2721 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2723 /* Check that all Sources are valid */
2724 auto validate_source
= [context
](const ALuint sid
) -> bool
2725 { return LookupSource(context
, sid
) != nullptr; };
2727 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2728 auto invsrc
= std::find_if_not(sids
.begin(), sids
.end(), validate_source
);
2729 if(invsrc
!= sids
.end())
2730 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", *invsrc
};
2732 /* All good. Delete source IDs. */
2733 auto delete_source
= [&context
](const ALuint sid
) -> void
2735 if(ALsource
*src
{LookupSource(context
, sid
)})
2736 FreeSource(context
, src
);
2738 std::for_each(sids
.begin(), sids
.end(), delete_source
);
2740 catch(al::context_error
& e
) {
2741 context
->setError(e
.errorCode(), "%s", e
.what());
2744 AL_API
DECL_FUNC1(ALboolean
, alIsSource
, ALuint
,source
)
2745 FORCE_ALIGN ALboolean AL_APIENTRY
alIsSourceDirect(ALCcontext
*context
, ALuint source
) noexcept
2747 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2748 if(LookupSource(context
, source
) != nullptr)
2754 AL_API
DECL_FUNC3(void, alSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
,value
)
2755 FORCE_ALIGN
void AL_APIENTRY
alSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2756 ALfloat value
) noexcept
2758 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2759 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2760 ALsource
*Source
{LookupSource(context
, source
)};
2762 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2764 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2766 catch(al::context_error
& e
) {
2767 context
->setError(e
.errorCode(), "%s", e
.what());
2770 AL_API
DECL_FUNC5(void, alSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
,value1
, ALfloat
,value2
, ALfloat
,value3
)
2771 FORCE_ALIGN
void AL_APIENTRY
alSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2772 ALfloat value1
, ALfloat value2
, ALfloat value3
) noexcept
2774 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2775 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2776 ALsource
*Source
{LookupSource(context
, source
)};
2778 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2780 const std::array fvals
{value1
, value2
, value3
};
2781 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
2783 catch(al::context_error
& e
) {
2784 context
->setError(e
.errorCode(), "%s", e
.what());
2787 AL_API
DECL_FUNC3(void, alSourcefv
, ALuint
,source
, ALenum
,param
, const ALfloat
*,values
)
2788 FORCE_ALIGN
void AL_APIENTRY
alSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2789 const ALfloat
*values
) noexcept
2791 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2792 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2793 ALsource
*Source
{LookupSource(context
, source
)};
2795 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2797 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2799 const ALuint count
{FloatValsByProp(param
)};
2800 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2802 catch(al::context_error
& e
) {
2803 context
->setError(e
.errorCode(), "%s", e
.what());
2807 AL_API
DECL_FUNCEXT3(void, alSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value
)
2808 FORCE_ALIGN
void AL_APIENTRY
alSourcedDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2809 ALdouble value
) noexcept
2811 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2812 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2813 ALsource
*Source
{LookupSource(context
, source
)};
2815 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2817 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1});
2819 catch(al::context_error
& e
) {
2820 context
->setError(e
.errorCode(), "%s", e
.what());
2823 AL_API
DECL_FUNCEXT5(void, alSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value1
, ALdouble
,value2
, ALdouble
,value3
)
2824 FORCE_ALIGN
void AL_APIENTRY
alSource3dDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2825 ALdouble value1
, ALdouble value2
, ALdouble value3
) noexcept
2827 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2828 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2829 ALsource
*Source
{LookupSource(context
, source
)};
2831 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2833 const std::array dvals
{value1
, value2
, value3
};
2834 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
2836 catch(al::context_error
& e
) {
2837 context
->setError(e
.errorCode(), "%s", e
.what());
2840 AL_API
DECL_FUNCEXT3(void, alSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, const ALdouble
*,values
)
2841 FORCE_ALIGN
void AL_APIENTRY
alSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2842 const ALdouble
*values
) noexcept
2844 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2845 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2846 ALsource
*Source
{LookupSource(context
, source
)};
2848 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2850 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2852 const ALuint count
{DoubleValsByProp(param
)};
2853 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2855 catch(al::context_error
& e
) {
2856 context
->setError(e
.errorCode(), "%s", e
.what());
2860 AL_API
DECL_FUNC3(void, alSourcei
, ALuint
,source
, ALenum
,param
, ALint
,value
)
2861 FORCE_ALIGN
void AL_APIENTRY
alSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2862 ALint value
) noexcept
2864 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2865 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2866 ALsource
*Source
{LookupSource(context
, source
)};
2868 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2870 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2872 catch(al::context_error
& e
) {
2873 context
->setError(e
.errorCode(), "%s", e
.what());
2876 AL_API
DECL_FUNC5(void, alSource3i
, ALuint
,buffer
, ALenum
,param
, ALint
,value1
, ALint
,value2
, ALint
,value3
)
2877 FORCE_ALIGN
void AL_APIENTRY
alSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2878 ALint value1
, ALint value2
, ALint value3
) noexcept
2880 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2881 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2882 ALsource
*Source
{LookupSource(context
, source
)};
2884 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2886 const std::array ivals
{value1
, value2
, value3
};
2887 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
2889 catch(al::context_error
& e
) {
2890 context
->setError(e
.errorCode(), "%s", e
.what());
2893 AL_API
DECL_FUNC3(void, alSourceiv
, ALuint
,source
, ALenum
,param
, const ALint
*,values
)
2894 FORCE_ALIGN
void AL_APIENTRY
alSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2895 const ALint
*values
) noexcept
2897 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2898 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2899 ALsource
*Source
{LookupSource(context
, source
)};
2901 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2903 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2905 const ALuint count
{IntValsByProp(param
)};
2906 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2908 catch(al::context_error
& e
) {
2909 context
->setError(e
.errorCode(), "%s", e
.what());
2913 AL_API
DECL_FUNCEXT3(void, alSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value
)
2914 FORCE_ALIGN
void AL_APIENTRY
alSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
,
2915 ALenum param
, ALint64SOFT value
) noexcept
2917 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2918 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2919 ALsource
*Source
{LookupSource(context
, source
)};
2921 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2923 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2925 catch(al::context_error
& e
) {
2926 context
->setError(e
.errorCode(), "%s", e
.what());
2929 AL_API
DECL_FUNCEXT5(void, alSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value1
, ALint64SOFT
,value2
, ALint64SOFT
,value3
)
2930 FORCE_ALIGN
void AL_APIENTRY
alSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
2931 ALenum param
, ALint64SOFT value1
, ALint64SOFT value2
, ALint64SOFT value3
) noexcept
2933 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2934 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2935 ALsource
*Source
{LookupSource(context
, source
)};
2937 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2939 const std::array i64vals
{value1
, value2
, value3
};
2940 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
2942 catch(al::context_error
& e
) {
2943 context
->setError(e
.errorCode(), "%s", e
.what());
2946 AL_API
DECL_FUNCEXT3(void, alSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, const ALint64SOFT
*,values
)
2947 FORCE_ALIGN
void AL_APIENTRY
alSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
2948 ALenum param
, const ALint64SOFT
*values
) noexcept
2950 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2951 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2952 ALsource
*Source
{LookupSource(context
, source
)};
2954 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2956 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2958 const ALuint count
{Int64ValsByProp(param
)};
2959 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2961 catch(al::context_error
& e
) {
2962 context
->setError(e
.errorCode(), "%s", e
.what());
2966 AL_API
DECL_FUNC3(void, alGetSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
*,value
)
2967 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2968 ALfloat
*value
) noexcept
2970 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2971 ALsource
*Source
{LookupSource(context
, source
)};
2973 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2975 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2977 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
2979 catch(al::context_error
& e
) {
2980 context
->setError(e
.errorCode(), "%s", e
.what());
2983 AL_API
DECL_FUNC5(void, alGetSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
*,value1
, ALfloat
*,value2
, ALfloat
*,value3
)
2984 FORCE_ALIGN
void AL_APIENTRY
alGetSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2985 ALfloat
*value1
, ALfloat
*value2
, ALfloat
*value3
) noexcept
2987 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2988 ALsource
*Source
{LookupSource(context
, source
)};
2990 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2991 if(!(value1
&& value2
&& value3
))
2992 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2994 std::array
<float,3> fvals
{};
2995 GetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
3000 catch(al::context_error
& e
) {
3001 context
->setError(e
.errorCode(), "%s", e
.what());
3004 AL_API
DECL_FUNC3(void, alGetSourcefv
, ALuint
,source
, ALenum
,param
, ALfloat
*,values
)
3005 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3006 ALfloat
*values
) noexcept
3008 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3009 ALsource
*Source
{LookupSource(context
, source
)};
3011 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3013 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3015 const ALuint count
{FloatValsByProp(param
)};
3016 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3018 catch(al::context_error
& e
) {
3019 context
->setError(e
.errorCode(), "%s", e
.what());
3023 AL_API
DECL_FUNCEXT3(void, alGetSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value
)
3024 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedDirectSOFT(ALCcontext
*context
, ALuint source
,
3025 ALenum param
, ALdouble
*value
) noexcept
3027 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3028 ALsource
*Source
{LookupSource(context
, source
)};
3030 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3032 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3034 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3036 catch(al::context_error
& e
) {
3037 context
->setError(e
.errorCode(), "%s", e
.what());
3040 AL_API
DECL_FUNCEXT5(void, alGetSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value1
, ALdouble
*,value2
, ALdouble
*,value3
)
3041 FORCE_ALIGN
void AL_APIENTRY
alGetSource3dDirectSOFT(ALCcontext
*context
, ALuint source
,
3042 ALenum param
, ALdouble
*value1
, ALdouble
*value2
, ALdouble
*value3
) noexcept
3044 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3045 ALsource
*Source
{LookupSource(context
, source
)};
3047 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3048 if(!(value1
&& value2
&& value3
))
3049 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3051 std::array
<double,3> dvals
{};
3052 GetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
3057 catch(al::context_error
& e
) {
3058 context
->setError(e
.errorCode(), "%s", e
.what());
3061 AL_API
DECL_FUNCEXT3(void, alGetSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,values
)
3062 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
,
3063 ALenum param
, ALdouble
*values
) noexcept
3065 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3066 ALsource
*Source
{LookupSource(context
, source
)};
3068 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3070 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3072 const ALuint count
{DoubleValsByProp(param
)};
3073 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3075 catch(al::context_error
& e
) {
3076 context
->setError(e
.errorCode(), "%s", e
.what());
3080 AL_API
DECL_FUNC3(void, alGetSourcei
, ALuint
,source
, ALenum
,param
, ALint
*,value
)
3081 FORCE_ALIGN
void AL_APIENTRY
alGetSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3082 ALint
*value
) noexcept
3084 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3085 ALsource
*Source
{LookupSource(context
, source
)};
3087 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3089 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3091 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3093 catch(al::context_error
& e
) {
3094 context
->setError(e
.errorCode(), "%s", e
.what());
3097 AL_API
DECL_FUNC5(void, alGetSource3i
, ALuint
,source
, ALenum
,param
, ALint
*,value1
, ALint
*,value2
, ALint
*,value3
)
3098 FORCE_ALIGN
void AL_APIENTRY
alGetSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3099 ALint
*value1
, ALint
*value2
, ALint
*value3
) noexcept
3101 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3102 ALsource
*Source
{LookupSource(context
, source
)};
3104 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3105 if(!(value1
&& value2
&& value3
))
3106 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3108 std::array
<int,3> ivals
{};
3109 GetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
3114 catch(al::context_error
& e
) {
3115 context
->setError(e
.errorCode(), "%s", e
.what());
3118 AL_API
DECL_FUNC3(void, alGetSourceiv
, ALuint
,source
, ALenum
,param
, ALint
*,values
)
3119 FORCE_ALIGN
void AL_APIENTRY
alGetSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3120 ALint
*values
) noexcept
3122 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3123 ALsource
*Source
{LookupSource(context
, source
)};
3125 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3127 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3129 const ALuint count
{IntValsByProp(param
)};
3130 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3132 catch(al::context_error
& e
) {
3133 context
->setError(e
.errorCode(), "%s", e
.what());
3137 AL_API
DECL_FUNCEXT3(void, alGetSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value
)
3138 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
, ALint64SOFT
*value
) noexcept
3140 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3141 ALsource
*Source
{LookupSource(context
, source
)};
3143 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3145 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3147 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3149 catch(al::context_error
& e
) {
3150 context
->setError(e
.errorCode(), "%s", e
.what());
3153 AL_API
DECL_FUNCEXT5(void, alGetSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value1
, ALint64SOFT
*,value2
, ALint64SOFT
*,value3
)
3154 FORCE_ALIGN
void AL_APIENTRY
alGetSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
3155 ALenum param
, ALint64SOFT
*value1
, ALint64SOFT
*value2
, ALint64SOFT
*value3
) noexcept
3157 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3158 ALsource
*Source
{LookupSource(context
, source
)};
3160 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3161 if(!(value1
&& value2
&& value3
))
3162 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3164 std::array
<int64_t,3> i64vals
{};
3165 GetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
3166 *value1
= i64vals
[0];
3167 *value2
= i64vals
[1];
3168 *value3
= i64vals
[2];
3170 catch(al::context_error
& e
) {
3171 context
->setError(e
.errorCode(), "%s", e
.what());
3174 AL_API
DECL_FUNCEXT3(void, alGetSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,values
)
3175 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
3176 ALenum param
, ALint64SOFT
*values
) noexcept
3178 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3179 ALsource
*Source
{LookupSource(context
, source
)};
3181 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3183 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3185 const ALuint count
{Int64ValsByProp(param
)};
3186 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3188 catch(al::context_error
& e
) {
3189 context
->setError(e
.errorCode(), "%s", e
.what());
3193 AL_API
DECL_FUNC1(void, alSourcePlay
, ALuint
,source
)
3194 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayDirect(ALCcontext
*context
, ALuint source
) noexcept
3196 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3197 ALsource
*Source
{LookupSource(context
, source
)};
3199 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3201 StartSources(context
, {&Source
, 1});
3203 catch(al::context_error
& e
) {
3204 context
->setError(e
.errorCode(), "%s", e
.what());
3207 FORCE_ALIGN
DECL_FUNCEXT2(void, alSourcePlayAtTime
,SOFT
, ALuint
,source
, ALint64SOFT
,start_time
)
3208 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimeDirectSOFT(ALCcontext
*context
, ALuint source
,
3209 ALint64SOFT start_time
) noexcept
3212 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3214 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3215 ALsource
*Source
{LookupSource(context
, source
)};
3217 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3219 StartSources(context
, {&Source
, 1}, nanoseconds
{start_time
});
3221 catch(al::context_error
& e
) {
3222 context
->setError(e
.errorCode(), "%s", e
.what());
3225 AL_API
DECL_FUNC2(void, alSourcePlayv
, ALsizei
,n
, const ALuint
*,sources
)
3226 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayvDirect(ALCcontext
*context
, ALsizei n
,
3227 const ALuint
*sources
) noexcept
3230 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3231 if(n
<= 0) UNLIKELY
return;
3233 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3234 source_store_variant source_store
;
3235 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3237 if(count
> std::tuple_size_v
<source_store_array
>)
3238 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3239 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3242 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3243 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3245 if(ALsource
*src
{LookupSource(context
, sid
)})
3247 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3249 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3251 StartSources(context
, srchandles
);
3253 catch(al::context_error
& e
) {
3254 context
->setError(e
.errorCode(), "%s", e
.what());
3257 FORCE_ALIGN
DECL_FUNCEXT3(void, alSourcePlayAtTimev
,SOFT
, ALsizei
,n
, const ALuint
*,sources
, ALint64SOFT
,start_time
)
3258 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimevDirectSOFT(ALCcontext
*context
, ALsizei n
,
3259 const ALuint
*sources
, ALint64SOFT start_time
) noexcept
3262 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3263 if(n
<= 0) UNLIKELY
return;
3266 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3268 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3269 source_store_variant source_store
;
3270 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3272 if(count
> std::tuple_size_v
<source_store_array
>)
3273 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3274 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3277 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3278 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3280 if(ALsource
*src
{LookupSource(context
, sid
)})
3282 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3284 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3286 StartSources(context
, srchandles
, nanoseconds
{start_time
});
3288 catch(al::context_error
& e
) {
3289 context
->setError(e
.errorCode(), "%s", e
.what());
3293 AL_API
DECL_FUNC1(void, alSourcePause
, ALuint
,source
)
3294 FORCE_ALIGN
void AL_APIENTRY
alSourcePauseDirect(ALCcontext
*context
, ALuint source
) noexcept
3295 { alSourcePausevDirect(context
, 1, &source
); }
3297 AL_API
DECL_FUNC2(void, alSourcePausev
, ALsizei
,n
, const ALuint
*,sources
)
3298 FORCE_ALIGN
void AL_APIENTRY
alSourcePausevDirect(ALCcontext
*context
, ALsizei n
,
3299 const ALuint
*sources
) noexcept
3302 throw al::context_error
{AL_INVALID_VALUE
, "Pausing %d sources", n
};
3303 if(n
<= 0) UNLIKELY
return;
3305 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3306 source_store_variant source_store
;
3307 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3309 if(count
> std::tuple_size_v
<source_store_array
>)
3310 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3311 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3314 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3315 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3317 if(ALsource
*src
{LookupSource(context
, sid
)})
3319 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3321 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3323 /* Pausing has to be done in two steps. First, for each source that's
3324 * detected to be playing, chamge the voice (asynchronously) to
3327 VoiceChange
*tail
{}, *cur
{};
3328 for(ALsource
*source
: srchandles
)
3330 Voice
*voice
{GetSourceVoice(source
, context
)};
3331 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3334 cur
= tail
= GetVoiceChanger(context
);
3337 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3338 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3340 cur
->mVoice
= voice
;
3341 cur
->mSourceID
= source
->id
;
3342 cur
->mState
= VChangeState::Pause
;
3347 SendVoiceChanges(context
, tail
);
3348 /* Second, now that the voice changes have been sent, because it's
3349 * possible that the voice stopped after it was detected playing and
3350 * before the voice got paused, recheck that the source is still
3351 * considered playing and set it to paused if so.
3353 for(ALsource
*source
: srchandles
)
3355 Voice
*voice
{GetSourceVoice(source
, context
)};
3356 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3357 source
->state
= AL_PAUSED
;
3361 catch(al::context_error
& e
) {
3362 context
->setError(e
.errorCode(), "%s", e
.what());
3366 AL_API
DECL_FUNC1(void, alSourceStop
, ALuint
,source
)
3367 FORCE_ALIGN
void AL_APIENTRY
alSourceStopDirect(ALCcontext
*context
, ALuint source
) noexcept
3368 { alSourceStopvDirect(context
, 1, &source
); }
3370 AL_API
DECL_FUNC2(void, alSourceStopv
, ALsizei
,n
, const ALuint
*,sources
)
3371 FORCE_ALIGN
void AL_APIENTRY
alSourceStopvDirect(ALCcontext
*context
, ALsizei n
,
3372 const ALuint
*sources
) noexcept
3375 throw al::context_error
{AL_INVALID_VALUE
, "Stopping %d sources", n
};
3376 if(n
<= 0) UNLIKELY
return;
3378 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3379 source_store_variant source_store
;
3380 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3382 if(count
> std::tuple_size_v
<source_store_array
>)
3383 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3384 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3387 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3388 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3390 if(ALsource
*src
{LookupSource(context
, sid
)})
3392 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3394 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3396 VoiceChange
*tail
{}, *cur
{};
3397 for(ALsource
*source
: srchandles
)
3399 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3402 cur
= tail
= GetVoiceChanger(context
);
3405 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3406 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3408 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3409 cur
->mVoice
= voice
;
3410 cur
->mSourceID
= source
->id
;
3411 cur
->mState
= VChangeState::Stop
;
3412 source
->state
= AL_STOPPED
;
3414 source
->Offset
= 0.0;
3415 source
->OffsetType
= AL_NONE
;
3416 source
->VoiceIdx
= InvalidVoiceIndex
;
3419 SendVoiceChanges(context
, tail
);
3421 catch(al::context_error
& e
) {
3422 context
->setError(e
.errorCode(), "%s", e
.what());
3426 AL_API
DECL_FUNC1(void, alSourceRewind
, ALuint
,source
)
3427 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindDirect(ALCcontext
*context
, ALuint source
) noexcept
3428 { alSourceRewindvDirect(context
, 1, &source
); }
3430 AL_API
DECL_FUNC2(void, alSourceRewindv
, ALsizei
,n
, const ALuint
*,sources
)
3431 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindvDirect(ALCcontext
*context
, ALsizei n
,
3432 const ALuint
*sources
) noexcept
3435 throw al::context_error
{AL_INVALID_VALUE
, "Rewinding %d sources", n
};
3436 if(n
<= 0) UNLIKELY
return;
3438 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3439 source_store_variant source_store
;
3440 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3442 if(count
> std::tuple_size_v
<source_store_array
>)
3443 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3444 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3447 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3448 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3450 if(ALsource
*src
{LookupSource(context
, sid
)})
3452 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3454 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3456 VoiceChange
*tail
{}, *cur
{};
3457 for(ALsource
*source
: srchandles
)
3459 Voice
*voice
{GetSourceVoice(source
, context
)};
3460 if(source
->state
!= AL_INITIAL
)
3463 cur
= tail
= GetVoiceChanger(context
);
3466 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3467 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3470 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3471 cur
->mVoice
= voice
;
3472 cur
->mSourceID
= source
->id
;
3473 cur
->mState
= VChangeState::Reset
;
3474 source
->state
= AL_INITIAL
;
3476 source
->Offset
= 0.0;
3477 source
->OffsetType
= AL_NONE
;
3478 source
->VoiceIdx
= InvalidVoiceIndex
;
3481 SendVoiceChanges(context
, tail
);
3483 catch(al::context_error
& e
) {
3484 context
->setError(e
.errorCode(), "%s", e
.what());
3488 AL_API
DECL_FUNC3(void, alSourceQueueBuffers
, ALuint
,source
, ALsizei
,nb
, const ALuint
*,buffers
)
3489 FORCE_ALIGN
void AL_APIENTRY
alSourceQueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3490 ALsizei nb
, const ALuint
*buffers
) noexcept
3493 throw al::context_error
{AL_INVALID_VALUE
, "Queueing %d buffers", nb
};
3494 if(nb
<= 0) UNLIKELY
return;
3496 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3497 ALsource
*source
{LookupSource(context
,src
)};
3499 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3501 /* Can't queue on a Static Source */
3502 if(source
->SourceType
== AL_STATIC
)
3503 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing onto static source %u", src
};
3505 /* Check for a valid Buffer, for its frequency and format */
3506 ALCdevice
*device
{context
->mALDevice
.get()};
3507 ALbuffer
*BufferFmt
{nullptr};
3508 for(auto &item
: source
->mQueue
)
3510 BufferFmt
= item
.mBuffer
;
3511 if(BufferFmt
) break;
3514 std::unique_lock
<std::mutex
> buflock
{device
->BufferLock
};
3515 const auto bids
= al::span
{buffers
, static_cast<ALuint
>(nb
)};
3516 const size_t NewListStart
{source
->mQueue
.size()};
3518 ALbufferQueueItem
*BufferList
{nullptr};
3519 std::for_each(bids
.cbegin(), bids
.cend(),
3520 [source
,device
,&BufferFmt
,&BufferList
](const ALuint bid
)
3522 ALbuffer
*buffer
{bid
? LookupBuffer(device
, bid
) : nullptr};
3524 throw al::context_error
{AL_INVALID_NAME
, "Queueing invalid buffer ID %u", bid
};
3528 if(buffer
->mSampleRate
< 1)
3529 throw al::context_error
{AL_INVALID_OPERATION
,
3530 "Queueing buffer %u with no format", buffer
->id
};
3532 if(buffer
->mCallback
)
3533 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing callback buffer %u",
3536 if(buffer
->MappedAccess
!= 0 && !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
3537 throw al::context_error
{AL_INVALID_OPERATION
,
3538 "Queueing non-persistently mapped buffer %u", buffer
->id
};
3541 source
->mQueue
.emplace_back();
3543 BufferList
= &source
->mQueue
.back();
3546 auto &item
= source
->mQueue
.back();
3547 BufferList
->mNext
.store(&item
, std::memory_order_relaxed
);
3551 BufferList
->mBlockAlign
= buffer
->mBlockAlign
;
3552 BufferList
->mSampleLen
= buffer
->mSampleLen
;
3553 BufferList
->mLoopEnd
= buffer
->mSampleLen
;
3554 BufferList
->mSamples
= buffer
->mData
;
3555 BufferList
->mBuffer
= buffer
;
3556 IncrementRef(buffer
->ref
);
3558 bool fmt_mismatch
{false};
3559 if(BufferFmt
== nullptr)
3563 fmt_mismatch
|= BufferFmt
->mSampleRate
!= buffer
->mSampleRate
;
3564 fmt_mismatch
|= BufferFmt
->mChannels
!= buffer
->mChannels
;
3565 fmt_mismatch
|= BufferFmt
->mType
!= buffer
->mType
;
3566 if(BufferFmt
->isBFormat())
3568 fmt_mismatch
|= BufferFmt
->mAmbiLayout
!= buffer
->mAmbiLayout
;
3569 fmt_mismatch
|= BufferFmt
->mAmbiScaling
!= buffer
->mAmbiScaling
;
3571 fmt_mismatch
|= BufferFmt
->mAmbiOrder
!= buffer
->mAmbiOrder
;
3574 throw al::context_error
{AL_INVALID_OPERATION
,
3575 "Queueing buffer with mismatched format\n"
3576 " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt
->mSampleRate
,
3577 NameFromFormat(BufferFmt
->mType
), NameFromFormat(BufferFmt
->mChannels
),
3578 buffer
->mSampleRate
, NameFromFormat(buffer
->mType
),
3579 NameFromFormat(buffer
->mChannels
)};
3583 /* A buffer failed (invalid ID or format), or there was some other
3584 * unexpected error, so unlock and release each buffer we had.
3586 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3587 for(;iter
!= source
->mQueue
.end();++iter
)
3589 if(ALbuffer
*buf
{iter
->mBuffer
})
3590 DecrementRef(buf
->ref
);
3592 source
->mQueue
.resize(NewListStart
);
3595 /* All buffers good. */
3598 /* Source is now streaming */
3599 source
->SourceType
= AL_STREAMING
;
3601 if(NewListStart
!= 0)
3603 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3604 (iter
-1)->mNext
.store(al::to_address(iter
), std::memory_order_release
);
3607 catch(al::context_error
& e
) {
3608 context
->setError(e
.errorCode(), "%s", e
.what());
3611 AL_API
DECL_FUNC3(void, alSourceUnqueueBuffers
, ALuint
,source
, ALsizei
,nb
, ALuint
*,buffers
)
3612 FORCE_ALIGN
void AL_APIENTRY
alSourceUnqueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3613 ALsizei nb
, ALuint
*buffers
) noexcept
3616 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffers", nb
};
3617 if(nb
<= 0) UNLIKELY
return;
3619 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3620 ALsource
*source
{LookupSource(context
,src
)};
3622 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3624 if(source
->SourceType
!= AL_STREAMING
)
3625 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from a non-streaming source %u",src
};
3627 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from looping source %u", src
};
3629 /* Make sure enough buffers have been processed to unqueue. */
3630 const al::span bids
{buffers
, static_cast<ALuint
>(nb
)};
3631 size_t processed
{0};
3632 if(source
->state
!= AL_INITIAL
) LIKELY
3634 VoiceBufferItem
*Current
{nullptr};
3635 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3636 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
3637 for(auto &item
: source
->mQueue
)
3639 if(&item
== Current
)
3644 if(processed
< bids
.size())
3645 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffer%s (only %zu processed)",
3646 nb
, (nb
==1)?"":"s", processed
};
3648 std::generate(bids
.begin(), bids
.end(), [source
]() noexcept
-> ALuint
3650 auto &head
= source
->mQueue
.front();
3652 if(ALbuffer
*buffer
{head
.mBuffer
})
3655 DecrementRef(buffer
->ref
);
3657 source
->mQueue
.pop_front();
3661 catch(al::context_error
& e
) {
3662 context
->setError(e
.errorCode(), "%s", e
.what());
3666 AL_API
void AL_APIENTRY
alSourceQueueBufferLayersSOFT(ALuint
, ALsizei
, const ALuint
*) noexcept
3668 ContextRef context
{GetContextRef()};
3669 if(!context
) UNLIKELY
return;
3671 context
->setError(AL_INVALID_OPERATION
, "alSourceQueueBufferLayersSOFT not supported");
3675 ALsource::ALsource() noexcept
3678 Direct
.GainHF
= 1.0f
;
3679 Direct
.HFReference
= LowPassFreqRef
;
3680 Direct
.GainLF
= 1.0f
;
3681 Direct
.LFReference
= HighPassFreqRef
;
3682 for(auto &send
: Send
)
3684 send
.Slot
= nullptr;
3687 send
.HFReference
= LowPassFreqRef
;
3689 send
.LFReference
= HighPassFreqRef
;
3693 ALsource::~ALsource()
3695 for(auto &item
: mQueue
)
3697 if(ALbuffer
*buffer
{item
.mBuffer
})
3698 DecrementRef(buffer
->ref
);
3701 auto clear_send
= [](ALsource::SendData
&send
) -> void
3702 { if(send
.Slot
) DecrementRef(send
.Slot
->ref
); };
3703 std::for_each(Send
.begin(), Send
.end(), clear_send
);
3706 void UpdateAllSourceProps(ALCcontext
*context
)
3708 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3709 auto voicelist
= context
->getVoicesSpan();
3711 for(Voice
*voice
: voicelist
)
3713 ALuint sid
{voice
->mSourceID
.load(std::memory_order_acquire
)};
3714 ALsource
*source
{sid
? LookupSource(context
, sid
) : nullptr};
3715 if(source
&& source
->VoiceIdx
== vidx
)
3717 if(std::exchange(source
->mPropsDirty
, false))
3718 UpdateSourceProps(source
, voice
, context
);
3724 void ALsource::SetName(ALCcontext
*context
, ALuint id
, std::string_view name
)
3726 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3728 auto source
= LookupSource(context
, id
);
3730 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", id
};
3732 context
->mSourceNames
.insert_or_assign(id
, name
);
3736 SourceSubList::~SourceSubList()
3741 uint64_t usemask
{~FreeMask
};
3744 const int idx
{al::countr_zero(usemask
)};
3745 usemask
&= ~(1_u64
<< idx
);
3746 std::destroy_at(al::to_address(Sources
->begin() + idx
));
3748 FreeMask
= ~usemask
;
3749 SubListAllocator
{}.deallocate(Sources
, 1);
3755 void ALsource::eaxInitialize(ALCcontext
*context
) noexcept
3757 assert(context
!= nullptr);
3758 mEaxAlContext
= context
;
3760 mEaxPrimaryFxSlotId
= context
->eaxGetPrimaryFxSlotIndex();
3763 eax1_translate(mEax1
.i
, mEax
);
3768 void ALsource::eaxDispatch(const EaxCall
& call
)
3770 call
.is_get() ? eax_get(call
) : eax_set(call
);
3773 ALsource
* ALsource::EaxLookupSource(ALCcontext
& al_context
, ALuint source_id
) noexcept
3775 return LookupSource(&al_context
, source_id
);
3778 [[noreturn
]] void ALsource::eax_fail(const char* message
)
3780 throw Exception
{message
};
3783 [[noreturn
]] void ALsource::eax_fail_unknown_property_id()
3785 eax_fail("Unknown property id.");
3788 [[noreturn
]] void ALsource::eax_fail_unknown_version()
3790 eax_fail("Unknown version.");
3793 [[noreturn
]] void ALsource::eax_fail_unknown_active_fx_slot_id()
3795 eax_fail("Unknown active FX slot ID.");
3798 [[noreturn
]] void ALsource::eax_fail_unknown_receiving_fx_slot_id()
3800 eax_fail("Unknown receiving FX slot ID.");
3803 void ALsource::eax_set_sends_defaults(EaxSends
& sends
, const EaxFxSlotIds
& ids
) noexcept
3805 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
3807 auto& send
= sends
[i
];
3808 send
.guidReceivingFXSlotID
= *(ids
[i
]);
3809 send
.lSend
= EAXSOURCE_DEFAULTSEND
;
3810 send
.lSendHF
= EAXSOURCE_DEFAULTSENDHF
;
3811 send
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3812 send
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3813 send
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3814 send
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3815 send
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3816 send
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3820 void ALsource::eax1_set_defaults(Eax1Props
& props
) noexcept
3822 props
.fMix
= EAX_REVERBMIX_USEDISTANCE
;
3825 void ALsource::eax1_set_defaults() noexcept
3827 eax1_set_defaults(mEax1
.i
);
3831 void ALsource::eax2_set_defaults(Eax2Props
& props
) noexcept
3833 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3834 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3835 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3836 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
3837 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3838 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
3839 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
3840 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3841 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3842 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3843 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3844 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3845 props
.dwFlags
= EAXSOURCE_DEFAULTFLAGS
;
3848 void ALsource::eax2_set_defaults() noexcept
3850 eax2_set_defaults(mEax2
.i
);
3854 void ALsource::eax3_set_defaults(Eax3Props
& props
) noexcept
3856 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3857 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3858 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3859 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
3860 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
3861 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
3862 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3863 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3864 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3865 props
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3866 props
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3867 props
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3868 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3869 props
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3870 props
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3871 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3872 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3873 props
.ulFlags
= EAXSOURCE_DEFAULTFLAGS
;
3876 void ALsource::eax3_set_defaults() noexcept
3878 eax3_set_defaults(mEax3
.i
);
3882 void ALsource::eax4_set_sends_defaults(EaxSends
& sends
) noexcept
3884 eax_set_sends_defaults(sends
, eax4_fx_slot_ids
);
3887 void ALsource::eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS
& slots
) noexcept
3889 slots
= EAX40SOURCE_DEFAULTACTIVEFXSLOTID
;
3892 void ALsource::eax4_set_defaults() noexcept
3894 eax3_set_defaults(mEax4
.i
.source
);
3895 eax4_set_sends_defaults(mEax4
.i
.sends
);
3896 eax4_set_active_fx_slots_defaults(mEax4
.i
.active_fx_slots
);
3900 void ALsource::eax5_set_source_defaults(EAX50SOURCEPROPERTIES
& props
) noexcept
3902 eax3_set_defaults(static_cast<Eax3Props
&>(props
));
3903 props
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3906 void ALsource::eax5_set_sends_defaults(EaxSends
& sends
) noexcept
3908 eax_set_sends_defaults(sends
, eax5_fx_slot_ids
);
3911 void ALsource::eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS
& slots
) noexcept
3913 slots
= EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID
;
3916 void ALsource::eax5_set_speaker_levels_defaults(EaxSpeakerLevels
& speaker_levels
) noexcept
3918 for(size_t i
{0};i
< eax_max_speakers
;++i
)
3920 auto& speaker_level
= speaker_levels
[i
];
3921 speaker_level
.lSpeakerID
= static_cast<long>(EAXSPEAKER_FRONT_LEFT
+ i
);
3922 speaker_level
.lLevel
= EAXSOURCE_DEFAULTSPEAKERLEVEL
;
3926 void ALsource::eax5_set_defaults(Eax5Props
& props
) noexcept
3928 eax5_set_source_defaults(props
.source
);
3929 eax5_set_sends_defaults(props
.sends
);
3930 eax5_set_active_fx_slots_defaults(props
.active_fx_slots
);
3931 eax5_set_speaker_levels_defaults(props
.speaker_levels
);
3934 void ALsource::eax5_set_defaults() noexcept
3936 eax5_set_defaults(mEax5
.i
);
3940 void ALsource::eax_set_defaults() noexcept
3942 eax1_set_defaults();
3943 eax2_set_defaults();
3944 eax3_set_defaults();
3945 eax4_set_defaults();
3946 eax5_set_defaults();
3949 void ALsource::eax1_translate(const Eax1Props
& src
, Eax5Props
& dst
) noexcept
3951 eax5_set_defaults(dst
);
3953 if (src
.fMix
== EAX_REVERBMIX_USEDISTANCE
)
3955 dst
.source
.ulFlags
|= EAXSOURCEFLAGS_ROOMAUTO
;
3956 dst
.sends
[0].lSend
= 0;
3960 dst
.source
.ulFlags
&= ~EAXSOURCEFLAGS_ROOMAUTO
;
3961 dst
.sends
[0].lSend
= std::clamp(static_cast<long>(gain_to_level_mb(src
.fMix
)),
3962 EAXSOURCE_MINSEND
, EAXSOURCE_MAXSEND
);
3966 void ALsource::eax2_translate(const Eax2Props
& src
, Eax5Props
& dst
) noexcept
3970 dst
.source
.lDirect
= src
.lDirect
;
3971 dst
.source
.lDirectHF
= src
.lDirectHF
;
3972 dst
.source
.lRoom
= src
.lRoom
;
3973 dst
.source
.lRoomHF
= src
.lRoomHF
;
3974 dst
.source
.lObstruction
= src
.lObstruction
;
3975 dst
.source
.flObstructionLFRatio
= src
.flObstructionLFRatio
;
3976 dst
.source
.lOcclusion
= src
.lOcclusion
;
3977 dst
.source
.flOcclusionLFRatio
= src
.flOcclusionLFRatio
;
3978 dst
.source
.flOcclusionRoomRatio
= src
.flOcclusionRoomRatio
;
3979 dst
.source
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3980 dst
.source
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3981 dst
.source
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3982 dst
.source
.lOutsideVolumeHF
= src
.lOutsideVolumeHF
;
3983 dst
.source
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3984 dst
.source
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3985 dst
.source
.flRoomRolloffFactor
= src
.flRoomRolloffFactor
;
3986 dst
.source
.flAirAbsorptionFactor
= src
.flAirAbsorptionFactor
;
3987 dst
.source
.ulFlags
= src
.dwFlags
;
3988 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3990 // Set everything else to defaults.
3992 eax5_set_sends_defaults(dst
.sends
);
3993 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
3994 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
3997 void ALsource::eax3_translate(const Eax3Props
& src
, Eax5Props
& dst
) noexcept
4001 static_cast<Eax3Props
&>(dst
.source
) = src
;
4002 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4004 // Set everything else to defaults.
4006 eax5_set_sends_defaults(dst
.sends
);
4007 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
4008 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4011 void ALsource::eax4_translate(const Eax4Props
& src
, Eax5Props
& dst
) noexcept
4015 static_cast<Eax3Props
&>(dst
.source
) = src
.source
;
4016 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4020 dst
.sends
= src
.sends
;
4022 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4023 dst
.sends
[i
].guidReceivingFXSlotID
= *(eax5_fx_slot_ids
[i
]);
4027 auto translate_slotid
= [](const GUID
&src_id
) -> GUID
4029 if(src_id
== EAX_NULL_GUID
)
4030 return EAX_NULL_GUID
;
4031 if(src_id
== EAX_PrimaryFXSlotID
)
4032 return EAX_PrimaryFXSlotID
;
4033 if(src_id
== EAXPROPERTYID_EAX40_FXSlot0
)
4034 return EAXPROPERTYID_EAX50_FXSlot0
;
4035 if(src_id
== EAXPROPERTYID_EAX40_FXSlot1
)
4036 return EAXPROPERTYID_EAX50_FXSlot1
;
4037 if(src_id
== EAXPROPERTYID_EAX40_FXSlot2
)
4038 return EAXPROPERTYID_EAX50_FXSlot2
;
4039 if(src_id
== EAXPROPERTYID_EAX40_FXSlot3
)
4040 return EAXPROPERTYID_EAX50_FXSlot3
;
4043 ERR("Unexpected active FX slot ID\n");
4044 return EAX_NULL_GUID
;
4046 const auto src_slots
= al::span
{src
.active_fx_slots
.guidActiveFXSlots
};
4047 const auto dst_slots
= al::span
{dst
.active_fx_slots
.guidActiveFXSlots
};
4048 auto dstiter
= std::transform(src_slots
.cbegin(), src_slots
.cend(), dst_slots
.begin(),
4050 std::fill(dstiter
, dst_slots
.end(), EAX_NULL_GUID
);
4054 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4057 float ALsource::eax_calculate_dst_occlusion_mb(
4058 long src_occlusion_mb
,
4060 float lf_ratio
) noexcept
4062 const auto ratio_1
= path_ratio
+ lf_ratio
- 1.0F
;
4063 const auto ratio_2
= path_ratio
* lf_ratio
;
4064 const auto ratio
= (ratio_2
> ratio_1
) ? ratio_2
: ratio_1
;
4065 const auto dst_occlustion_mb
= static_cast<float>(src_occlusion_mb
) * ratio
;
4066 return dst_occlustion_mb
;
4069 EaxAlLowPassParam
ALsource::eax_create_direct_filter_param() const noexcept
4071 const auto &source
= mEax
.source
;
4073 auto gain_mb
= static_cast<float>(source
.lObstruction
) * source
.flObstructionLFRatio
;
4074 auto gainhf_mb
= static_cast<float>(source
.lObstruction
);
4076 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4078 if(!mEaxActiveFxSlots
[i
])
4081 if(source
.lOcclusion
!= 0)
4083 const auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4084 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4085 const auto is_environmental_fx
= ((fx_slot_eax
.ulFlags
&EAXFXSLOTFLAGS_ENVIRONMENT
) != 0);
4086 const auto is_primary
= (mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index());
4088 if(is_environmental_fx
&& is_primary
)
4090 gain_mb
+= eax_calculate_dst_occlusion_mb(source
.lOcclusion
,
4091 source
.flOcclusionDirectRatio
, source
.flOcclusionLFRatio
);
4093 gainhf_mb
+= static_cast<float>(source
.lOcclusion
) * source
.flOcclusionDirectRatio
;
4097 const auto& send
= mEax
.sends
[i
];
4098 if(send
.lOcclusion
!= 0)
4100 gain_mb
+= eax_calculate_dst_occlusion_mb(send
.lOcclusion
, send
.flOcclusionDirectRatio
,
4101 send
.flOcclusionLFRatio
);
4103 gainhf_mb
+= static_cast<float>(send
.lOcclusion
) * send
.flOcclusionDirectRatio
;
4107 /* gainhf_mb is the absolute mBFS of the filter's high-frequency volume,
4108 * and gain_mb is the absolute mBFS of the filter's low-frequency volume.
4109 * Adjust the HF volume to be relative to the LF volume, to make the
4110 * appropriate main and relative HF filter volumes.
4112 * Also add the Direct and DirectHF properties to the filter, which are
4113 * already the main and relative HF volumes.
4115 gainhf_mb
-= gain_mb
- static_cast<float>(source
.lDirectHF
);
4116 gain_mb
+= static_cast<float>(source
.lDirect
);
4118 return EaxAlLowPassParam
{level_mb_to_gain(gain_mb
),
4119 std::min(level_mb_to_gain(gainhf_mb
), 1.0f
)};
4122 EaxAlLowPassParam
ALsource::eax_create_room_filter_param(
4123 const ALeffectslot
& fx_slot
,
4124 const EAXSOURCEALLSENDPROPERTIES
& send
) const noexcept
4126 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4127 const auto is_environmental_fx
= bool{(fx_slot_eax
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0};
4128 const auto is_primary
= bool{mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index()};
4130 auto gain_mb
= (static_cast<float>(fx_slot_eax
.lOcclusion
) * fx_slot_eax
.flOcclusionLFRatio
)
4131 + eax_calculate_dst_occlusion_mb(send
.lOcclusion
, send
.flOcclusionRoomRatio
,
4132 send
.flOcclusionLFRatio
)
4133 + (static_cast<float>(send
.lExclusion
) * send
.flExclusionLFRatio
);
4135 auto gainhf_mb
= static_cast<float>(fx_slot_eax
.lOcclusion
)
4136 + (static_cast<float>(send
.lOcclusion
) * send
.flOcclusionRoomRatio
);
4138 if(is_environmental_fx
&& is_primary
)
4140 const auto &source
= mEax
.source
;
4142 gain_mb
+= eax_calculate_dst_occlusion_mb(source
.lOcclusion
, source
.flOcclusionRoomRatio
,
4143 source
.flOcclusionLFRatio
);
4144 gain_mb
+= static_cast<float>(source
.lExclusion
) * source
.flExclusionLFRatio
;
4146 gainhf_mb
+= static_cast<float>(source
.lOcclusion
) * source
.flOcclusionRoomRatio
;
4147 gainhf_mb
+= static_cast<float>(source
.lExclusion
+ send
.lExclusion
);
4150 gainhf_mb
-= gain_mb
- static_cast<float>(send
.lSendHF
);
4151 gain_mb
+= static_cast<float>(send
.lSend
);
4152 if(is_environmental_fx
)
4154 const auto &source
= mEax
.source
;
4155 gain_mb
+= static_cast<float>(source
.lRoom
);
4156 gainhf_mb
+= static_cast<float>(source
.lRoomHF
);
4159 return EaxAlLowPassParam
{level_mb_to_gain(gain_mb
),
4160 std::min(level_mb_to_gain(gainhf_mb
), 1.0f
)};
4163 void ALsource::eax_update_direct_filter()
4165 const auto& direct_param
= eax_create_direct_filter_param();
4166 Direct
.Gain
= direct_param
.gain
;
4167 Direct
.GainHF
= direct_param
.gain_hf
;
4168 Direct
.HFReference
= LowPassFreqRef
;
4169 Direct
.GainLF
= 1.0f
;
4170 Direct
.LFReference
= HighPassFreqRef
;
4174 void ALsource::eax_update_room_filters()
4176 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4178 if(!mEaxActiveFxSlots
[i
])
4181 auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4182 const auto& send
= mEax
.sends
[i
];
4183 const auto& room_param
= eax_create_room_filter_param(fx_slot
, send
);
4184 eax_set_al_source_send(&fx_slot
, i
, room_param
);
4188 void ALsource::eax_set_efx_outer_gain_hf()
4190 OuterGainHF
= std::clamp(
4191 level_mb_to_gain(static_cast<float>(mEax
.source
.lOutsideVolumeHF
)),
4192 AL_MIN_CONE_OUTER_GAINHF
,
4193 AL_MAX_CONE_OUTER_GAINHF
);
4196 void ALsource::eax_set_efx_doppler_factor()
4198 DopplerFactor
= mEax
.source
.flDopplerFactor
;
4201 void ALsource::eax_set_efx_rolloff_factor()
4203 RolloffFactor2
= mEax
.source
.flRolloffFactor
;
4206 void ALsource::eax_set_efx_room_rolloff_factor()
4208 RoomRolloffFactor
= mEax
.source
.flRoomRolloffFactor
;
4211 void ALsource::eax_set_efx_air_absorption_factor()
4213 AirAbsorptionFactor
= mEax
.source
.flAirAbsorptionFactor
;
4216 void ALsource::eax_set_efx_dry_gain_hf_auto()
4218 DryGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_DIRECTHFAUTO
) != 0);
4221 void ALsource::eax_set_efx_wet_gain_auto()
4223 WetGainAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMAUTO
) != 0);
4226 void ALsource::eax_set_efx_wet_gain_hf_auto()
4228 WetGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMHFAUTO
) != 0);
4231 void ALsource::eax1_set(const EaxCall
& call
, Eax1Props
& props
)
4233 switch (call
.get_property_id()) {
4234 case DSPROPERTY_EAXBUFFER_ALL
:
4235 eax_defer
<Eax1SourceAllValidator
>(call
, props
);
4238 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4239 eax_defer
<Eax1SourceReverbMixValidator
>(call
, props
.fMix
);
4243 eax_fail_unknown_property_id();
4247 void ALsource::eax2_set(const EaxCall
& call
, Eax2Props
& props
)
4249 switch (call
.get_property_id()) {
4250 case DSPROPERTY_EAX20BUFFER_NONE
:
4253 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4254 eax_defer
<Eax2SourceAllValidator
>(call
, props
);
4257 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4258 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4261 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4262 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4265 case DSPROPERTY_EAX20BUFFER_ROOM
:
4266 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4269 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4270 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4273 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4274 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4277 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4278 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4281 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4282 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4285 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4286 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4289 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4290 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4293 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4294 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4297 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4298 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4301 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4302 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4305 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4306 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.dwFlags
);
4310 eax_fail_unknown_property_id();
4314 void ALsource::eax3_set(const EaxCall
& call
, Eax3Props
& props
)
4316 switch (call
.get_property_id()) {
4317 case EAXSOURCE_NONE
:
4320 case EAXSOURCE_ALLPARAMETERS
:
4321 eax_defer
<Eax3SourceAllValidator
>(call
, props
);
4324 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4325 eax_defer_sub
<Eax4ObstructionValidator
, EAXOBSTRUCTIONPROPERTIES
>(call
, props
.lObstruction
);
4328 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4329 eax_defer_sub
<Eax4OcclusionValidator
, EAXOCCLUSIONPROPERTIES
>(call
, props
.lOcclusion
);
4332 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4333 eax_defer_sub
<Eax4ExclusionValidator
, EAXEXCLUSIONPROPERTIES
>(call
, props
.lExclusion
);
4336 case EAXSOURCE_DIRECT
:
4337 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4340 case EAXSOURCE_DIRECTHF
:
4341 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4344 case EAXSOURCE_ROOM
:
4345 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4348 case EAXSOURCE_ROOMHF
:
4349 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4352 case EAXSOURCE_OBSTRUCTION
:
4353 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4356 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4357 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4360 case EAXSOURCE_OCCLUSION
:
4361 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4364 case EAXSOURCE_OCCLUSIONLFRATIO
:
4365 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4368 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4369 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4372 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4373 eax_defer
<Eax3SourceOcclusionDirectRatioValidator
>(call
, props
.flOcclusionDirectRatio
);
4376 case EAXSOURCE_EXCLUSION
:
4377 eax_defer
<Eax3SourceExclusionValidator
>(call
, props
.lExclusion
);
4380 case EAXSOURCE_EXCLUSIONLFRATIO
:
4381 eax_defer
<Eax3SourceExclusionLfRatioValidator
>(call
, props
.flExclusionLFRatio
);
4384 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4385 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4388 case EAXSOURCE_DOPPLERFACTOR
:
4389 eax_defer
<Eax3SourceDopplerFactorValidator
>(call
, props
.flDopplerFactor
);
4392 case EAXSOURCE_ROLLOFFFACTOR
:
4393 eax_defer
<Eax3SourceRolloffFactorValidator
>(call
, props
.flRolloffFactor
);
4396 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4397 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4400 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4401 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4404 case EAXSOURCE_FLAGS
:
4405 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.ulFlags
);
4409 eax_fail_unknown_property_id();
4413 void ALsource::eax4_set(const EaxCall
& call
, Eax4Props
& props
)
4415 switch (call
.get_property_id()) {
4416 case EAXSOURCE_NONE
:
4417 case EAXSOURCE_ALLPARAMETERS
:
4418 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4419 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4420 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4421 case EAXSOURCE_DIRECT
:
4422 case EAXSOURCE_DIRECTHF
:
4423 case EAXSOURCE_ROOM
:
4424 case EAXSOURCE_ROOMHF
:
4425 case EAXSOURCE_OBSTRUCTION
:
4426 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4427 case EAXSOURCE_OCCLUSION
:
4428 case EAXSOURCE_OCCLUSIONLFRATIO
:
4429 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4430 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4431 case EAXSOURCE_EXCLUSION
:
4432 case EAXSOURCE_EXCLUSIONLFRATIO
:
4433 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4434 case EAXSOURCE_DOPPLERFACTOR
:
4435 case EAXSOURCE_ROLLOFFFACTOR
:
4436 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4437 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4438 case EAXSOURCE_FLAGS
:
4439 eax3_set(call
, props
.source
);
4442 case EAXSOURCE_SENDPARAMETERS
:
4443 eax4_defer_sends
<Eax4SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4446 case EAXSOURCE_ALLSENDPARAMETERS
:
4447 eax4_defer_sends
<Eax4AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4450 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4451 eax4_defer_sends
<Eax4OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4454 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4455 eax4_defer_sends
<Eax4ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4458 case EAXSOURCE_ACTIVEFXSLOTID
:
4459 eax4_defer_active_fx_slot_id(call
, al::span
{props
.active_fx_slots
.guidActiveFXSlots
});
4463 eax_fail_unknown_property_id();
4467 void ALsource::eax5_defer_all_2d(const EaxCall
& call
, EAX50SOURCEPROPERTIES
& props
)
4469 const auto& src_props
= call
.get_value
<Exception
, const EAXSOURCE2DPROPERTIES
>();
4470 Eax5SourceAll2dValidator
{}(src_props
);
4471 props
.lDirect
= src_props
.lDirect
;
4472 props
.lDirectHF
= src_props
.lDirectHF
;
4473 props
.lRoom
= src_props
.lRoom
;
4474 props
.lRoomHF
= src_props
.lRoomHF
;
4475 props
.ulFlags
= src_props
.ulFlags
;
4478 void ALsource::eax5_defer_speaker_levels(const EaxCall
& call
, EaxSpeakerLevels
& props
)
4480 const auto values
= call
.get_values
<const EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4481 std::for_each(values
.cbegin(), values
.cend(), Eax5SpeakerAllValidator
{});
4483 for (const auto& value
: values
) {
4484 const auto index
= static_cast<size_t>(value
.lSpeakerID
- EAXSPEAKER_FRONT_LEFT
);
4485 props
[index
].lLevel
= value
.lLevel
;
4489 void ALsource::eax5_set(const EaxCall
& call
, Eax5Props
& props
)
4491 switch (call
.get_property_id()) {
4492 case EAXSOURCE_NONE
:
4495 case EAXSOURCE_ALLPARAMETERS
:
4496 eax_defer
<Eax5SourceAllValidator
>(call
, props
.source
);
4499 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4500 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4501 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4502 case EAXSOURCE_DIRECT
:
4503 case EAXSOURCE_DIRECTHF
:
4504 case EAXSOURCE_ROOM
:
4505 case EAXSOURCE_ROOMHF
:
4506 case EAXSOURCE_OBSTRUCTION
:
4507 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4508 case EAXSOURCE_OCCLUSION
:
4509 case EAXSOURCE_OCCLUSIONLFRATIO
:
4510 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4511 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4512 case EAXSOURCE_EXCLUSION
:
4513 case EAXSOURCE_EXCLUSIONLFRATIO
:
4514 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4515 case EAXSOURCE_DOPPLERFACTOR
:
4516 case EAXSOURCE_ROLLOFFFACTOR
:
4517 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4518 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4519 eax3_set(call
, props
.source
);
4522 case EAXSOURCE_FLAGS
:
4523 eax_defer
<Eax5SourceFlagsValidator
>(call
, props
.source
.ulFlags
);
4526 case EAXSOURCE_SENDPARAMETERS
:
4527 eax5_defer_sends
<Eax5SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4530 case EAXSOURCE_ALLSENDPARAMETERS
:
4531 eax5_defer_sends
<Eax5AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4534 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4535 eax5_defer_sends
<Eax5OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4538 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4539 eax5_defer_sends
<Eax5ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4542 case EAXSOURCE_ACTIVEFXSLOTID
:
4543 eax5_defer_active_fx_slot_id(call
, al::span
{props
.active_fx_slots
.guidActiveFXSlots
});
4546 case EAXSOURCE_MACROFXFACTOR
:
4547 eax_defer
<Eax5SourceMacroFXFactorValidator
>(call
, props
.source
.flMacroFXFactor
);
4550 case EAXSOURCE_SPEAKERLEVELS
:
4551 eax5_defer_speaker_levels(call
, props
.speaker_levels
);
4554 case EAXSOURCE_ALL2DPARAMETERS
:
4555 eax5_defer_all_2d(call
, props
.source
);
4559 eax_fail_unknown_property_id();
4563 void ALsource::eax_set(const EaxCall
& call
)
4565 const auto eax_version
= call
.get_version();
4568 case 1: eax1_set(call
, mEax1
.d
); break;
4569 case 2: eax2_set(call
, mEax2
.d
); break;
4570 case 3: eax3_set(call
, mEax3
.d
); break;
4571 case 4: eax4_set(call
, mEax4
.d
); break;
4572 case 5: eax5_set(call
, mEax5
.d
); break;
4573 default: eax_fail_unknown_property_id();
4576 mEaxVersion
= eax_version
;
4579 void ALsource::eax_get_active_fx_slot_id(const EaxCall
& call
, const al::span
<const GUID
> src_ids
)
4581 assert(src_ids
.size()==EAX40_MAX_ACTIVE_FXSLOTS
|| src_ids
.size()==EAX50_MAX_ACTIVE_FXSLOTS
);
4582 const auto dst_ids
= call
.get_values
<GUID
>(src_ids
.size());
4583 std::uninitialized_copy_n(src_ids
.begin(), dst_ids
.size(), dst_ids
.begin());
4586 void ALsource::eax1_get(const EaxCall
& call
, const Eax1Props
& props
)
4588 switch (call
.get_property_id()) {
4589 case DSPROPERTY_EAXBUFFER_ALL
:
4590 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4591 call
.set_value
<Exception
>(props
.fMix
);
4595 eax_fail_unknown_property_id();
4599 void ALsource::eax2_get(const EaxCall
& call
, const Eax2Props
& props
)
4601 switch (call
.get_property_id()) {
4602 case DSPROPERTY_EAX20BUFFER_NONE
:
4605 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4606 call
.set_value
<Exception
>(props
);
4609 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4610 call
.set_value
<Exception
>(props
.lDirect
);
4613 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4614 call
.set_value
<Exception
>(props
.lDirectHF
);
4617 case DSPROPERTY_EAX20BUFFER_ROOM
:
4618 call
.set_value
<Exception
>(props
.lRoom
);
4621 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4622 call
.set_value
<Exception
>(props
.lRoomHF
);
4625 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4626 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4629 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4630 call
.set_value
<Exception
>(props
.lObstruction
);
4633 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4634 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4637 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4638 call
.set_value
<Exception
>(props
.lOcclusion
);
4641 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4642 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4645 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4646 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4649 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4650 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4653 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4654 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4657 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4658 call
.set_value
<Exception
>(props
.dwFlags
);
4662 eax_fail_unknown_property_id();
4666 void ALsource::eax3_get_obstruction(const EaxCall
& call
, const Eax3Props
& props
)
4668 const auto& subprops
= reinterpret_cast<const EAXOBSTRUCTIONPROPERTIES
&>(props
.lObstruction
);
4669 call
.set_value
<Exception
>(subprops
);
4672 void ALsource::eax3_get_occlusion(const EaxCall
& call
, const Eax3Props
& props
)
4674 const auto& subprops
= reinterpret_cast<const EAXOCCLUSIONPROPERTIES
&>(props
.lOcclusion
);
4675 call
.set_value
<Exception
>(subprops
);
4678 void ALsource::eax3_get_exclusion(const EaxCall
& call
, const Eax3Props
& props
)
4680 const auto& subprops
= reinterpret_cast<const EAXEXCLUSIONPROPERTIES
&>(props
.lExclusion
);
4681 call
.set_value
<Exception
>(subprops
);
4684 void ALsource::eax3_get(const EaxCall
& call
, const Eax3Props
& props
)
4686 switch (call
.get_property_id()) {
4687 case EAXSOURCE_NONE
:
4690 case EAXSOURCE_ALLPARAMETERS
:
4691 call
.set_value
<Exception
>(props
);
4694 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4695 eax3_get_obstruction(call
, props
);
4698 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4699 eax3_get_occlusion(call
, props
);
4702 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4703 eax3_get_exclusion(call
, props
);
4706 case EAXSOURCE_DIRECT
:
4707 call
.set_value
<Exception
>(props
.lDirect
);
4710 case EAXSOURCE_DIRECTHF
:
4711 call
.set_value
<Exception
>(props
.lDirectHF
);
4714 case EAXSOURCE_ROOM
:
4715 call
.set_value
<Exception
>(props
.lRoom
);
4718 case EAXSOURCE_ROOMHF
:
4719 call
.set_value
<Exception
>(props
.lRoomHF
);
4722 case EAXSOURCE_OBSTRUCTION
:
4723 call
.set_value
<Exception
>(props
.lObstruction
);
4726 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4727 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4730 case EAXSOURCE_OCCLUSION
:
4731 call
.set_value
<Exception
>(props
.lOcclusion
);
4734 case EAXSOURCE_OCCLUSIONLFRATIO
:
4735 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4738 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4739 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4742 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4743 call
.set_value
<Exception
>(props
.flOcclusionDirectRatio
);
4746 case EAXSOURCE_EXCLUSION
:
4747 call
.set_value
<Exception
>(props
.lExclusion
);
4750 case EAXSOURCE_EXCLUSIONLFRATIO
:
4751 call
.set_value
<Exception
>(props
.flExclusionLFRatio
);
4754 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4755 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4758 case EAXSOURCE_DOPPLERFACTOR
:
4759 call
.set_value
<Exception
>(props
.flDopplerFactor
);
4762 case EAXSOURCE_ROLLOFFFACTOR
:
4763 call
.set_value
<Exception
>(props
.flRolloffFactor
);
4766 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4767 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4770 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4771 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4774 case EAXSOURCE_FLAGS
:
4775 call
.set_value
<Exception
>(props
.ulFlags
);
4779 eax_fail_unknown_property_id();
4783 void ALsource::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
4785 switch (call
.get_property_id()) {
4786 case EAXSOURCE_NONE
:
4789 case EAXSOURCE_ALLPARAMETERS
:
4790 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4791 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4792 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4793 case EAXSOURCE_DIRECT
:
4794 case EAXSOURCE_DIRECTHF
:
4795 case EAXSOURCE_ROOM
:
4796 case EAXSOURCE_ROOMHF
:
4797 case EAXSOURCE_OBSTRUCTION
:
4798 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4799 case EAXSOURCE_OCCLUSION
:
4800 case EAXSOURCE_OCCLUSIONLFRATIO
:
4801 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4802 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4803 case EAXSOURCE_EXCLUSION
:
4804 case EAXSOURCE_EXCLUSIONLFRATIO
:
4805 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4806 case EAXSOURCE_DOPPLERFACTOR
:
4807 case EAXSOURCE_ROLLOFFFACTOR
:
4808 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4809 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4810 case EAXSOURCE_FLAGS
:
4811 eax3_get(call
, props
.source
);
4814 case EAXSOURCE_SENDPARAMETERS
:
4815 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4818 case EAXSOURCE_ALLSENDPARAMETERS
:
4819 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4822 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4823 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4826 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4827 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4830 case EAXSOURCE_ACTIVEFXSLOTID
:
4831 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4835 eax_fail_unknown_property_id();
4839 void ALsource::eax5_get_all_2d(const EaxCall
& call
, const EAX50SOURCEPROPERTIES
& props
)
4841 auto& subprops
= call
.get_value
<Exception
, EAXSOURCE2DPROPERTIES
>();
4842 subprops
.lDirect
= props
.lDirect
;
4843 subprops
.lDirectHF
= props
.lDirectHF
;
4844 subprops
.lRoom
= props
.lRoom
;
4845 subprops
.lRoomHF
= props
.lRoomHF
;
4846 subprops
.ulFlags
= props
.ulFlags
;
4849 void ALsource::eax5_get_speaker_levels(const EaxCall
& call
, const EaxSpeakerLevels
& props
)
4851 const auto subprops
= call
.get_values
<EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4852 std::uninitialized_copy_n(props
.cbegin(), subprops
.size(), subprops
.begin());
4855 void ALsource::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
4857 switch (call
.get_property_id()) {
4858 case EAXSOURCE_NONE
:
4861 case EAXSOURCE_ALLPARAMETERS
:
4862 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4863 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4864 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4865 case EAXSOURCE_DIRECT
:
4866 case EAXSOURCE_DIRECTHF
:
4867 case EAXSOURCE_ROOM
:
4868 case EAXSOURCE_ROOMHF
:
4869 case EAXSOURCE_OBSTRUCTION
:
4870 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4871 case EAXSOURCE_OCCLUSION
:
4872 case EAXSOURCE_OCCLUSIONLFRATIO
:
4873 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4874 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4875 case EAXSOURCE_EXCLUSION
:
4876 case EAXSOURCE_EXCLUSIONLFRATIO
:
4877 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4878 case EAXSOURCE_DOPPLERFACTOR
:
4879 case EAXSOURCE_ROLLOFFFACTOR
:
4880 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4881 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4882 case EAXSOURCE_FLAGS
:
4883 eax3_get(call
, props
.source
);
4886 case EAXSOURCE_SENDPARAMETERS
:
4887 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4890 case EAXSOURCE_ALLSENDPARAMETERS
:
4891 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4894 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4895 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4898 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4899 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4902 case EAXSOURCE_ACTIVEFXSLOTID
:
4903 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4906 case EAXSOURCE_MACROFXFACTOR
:
4907 call
.set_value
<Exception
>(props
.source
.flMacroFXFactor
);
4910 case EAXSOURCE_SPEAKERLEVELS
:
4911 call
.set_value
<Exception
>(props
.speaker_levels
);
4914 case EAXSOURCE_ALL2DPARAMETERS
:
4915 eax5_get_all_2d(call
, props
.source
);
4919 eax_fail_unknown_property_id();
4923 void ALsource::eax_get(const EaxCall
& call
)
4925 switch (call
.get_version()) {
4926 case 1: eax1_get(call
, mEax1
.i
); break;
4927 case 2: eax2_get(call
, mEax2
.i
); break;
4928 case 3: eax3_get(call
, mEax3
.i
); break;
4929 case 4: eax4_get(call
, mEax4
.i
); break;
4930 case 5: eax5_get(call
, mEax5
.i
); break;
4931 default: eax_fail_unknown_version();
4935 void ALsource::eax_set_al_source_send(ALeffectslot
*slot
, size_t sendidx
, const EaxAlLowPassParam
&filter
)
4937 if(sendidx
>= EAX_MAX_FXSLOTS
)
4940 auto &send
= Send
[sendidx
];
4941 send
.Gain
= filter
.gain
;
4942 send
.GainHF
= filter
.gain_hf
;
4943 send
.HFReference
= LowPassFreqRef
;
4945 send
.LFReference
= HighPassFreqRef
;
4948 IncrementRef(slot
->ref
);
4949 if(auto *oldslot
= send
.Slot
)
4950 DecrementRef(oldslot
->ref
);
4956 void ALsource::eax_commit_active_fx_slots()
4958 // Clear all slots to an inactive state.
4959 mEaxActiveFxSlots
.fill(false);
4961 // Mark the set slots as active.
4962 for(const auto& slot_id
: mEax
.active_fx_slots
.guidActiveFXSlots
)
4964 if(slot_id
== EAX_NULL_GUID
)
4967 else if(slot_id
== EAX_PrimaryFXSlotID
)
4969 // Mark primary FX slot as active.
4970 if(mEaxPrimaryFxSlotId
.has_value())
4971 mEaxActiveFxSlots
[*mEaxPrimaryFxSlotId
] = true;
4973 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot0
)
4974 mEaxActiveFxSlots
[0] = true;
4975 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot1
)
4976 mEaxActiveFxSlots
[1] = true;
4977 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot2
)
4978 mEaxActiveFxSlots
[2] = true;
4979 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot3
)
4980 mEaxActiveFxSlots
[3] = true;
4983 // Deactivate EFX auxiliary effect slots for inactive slots. Active slots
4984 // will be updated with the room filters.
4985 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4987 if(!mEaxActiveFxSlots
[i
])
4988 eax_set_al_source_send(nullptr, i
, EaxAlLowPassParam
{1.0f
, 1.0f
});
4992 void ALsource::eax_commit_filters()
4994 eax_update_direct_filter();
4995 eax_update_room_filters();
4998 void ALsource::eaxCommit()
5000 const auto primary_fx_slot_id
= mEaxAlContext
->eaxGetPrimaryFxSlotIndex();
5001 const auto is_primary_fx_slot_id_changed
= (mEaxPrimaryFxSlotId
!= primary_fx_slot_id
);
5003 if(!mEaxChanged
&& !is_primary_fx_slot_id_changed
)
5006 mEaxPrimaryFxSlotId
= primary_fx_slot_id
;
5007 mEaxChanged
= false;
5013 eax1_translate(mEax1
.i
, mEax
);
5017 eax2_translate(mEax2
.i
, mEax
);
5021 eax3_translate(mEax3
.i
, mEax
);
5025 eax4_translate(mEax4
.i
, mEax
);
5033 eax_set_efx_outer_gain_hf();
5034 eax_set_efx_doppler_factor();
5035 eax_set_efx_rolloff_factor();
5036 eax_set_efx_room_rolloff_factor();
5037 eax_set_efx_air_absorption_factor();
5038 eax_set_efx_dry_gain_hf_auto();
5039 eax_set_efx_wet_gain_auto();
5040 eax_set_efx_wet_gain_hf_auto();
5042 eax_commit_active_fx_slots();
5043 eax_commit_filters();
5046 #endif // ALSOFT_EAX