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 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 BufferList
= static_cast<ALbufferQueueItem
*>(Current
);
2367 ALbuffer
*buffer
{BufferList
? BufferList
->mBuffer
: nullptr};
2368 values
[0] = buffer
? static_cast<T
>(buffer
->id
) : T
{0};
2373 case AL_SOURCE_STATE
:
2374 if constexpr(std::is_integral_v
<T
>)
2377 values
[0] = GetSourceState(Source
, GetSourceVoice(Source
, Context
));
2382 case AL_BUFFERS_QUEUED
:
2383 if constexpr(std::is_integral_v
<T
>)
2386 values
[0] = static_cast<T
>(Source
->mQueue
.size());
2391 case AL_BUFFERS_PROCESSED
:
2392 if constexpr(std::is_integral_v
<T
>)
2395 if(Source
->Looping
|| Source
->SourceType
!= AL_STREAMING
)
2397 /* Buffers on a looping source are in a perpetual state of
2398 * PENDING, so don't report any as PROCESSED
2405 if(Source
->state
!= AL_INITIAL
)
2407 const VoiceBufferItem
*Current
{nullptr};
2408 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2409 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
2410 for(auto &item
: Source
->mQueue
)
2412 if(&item
== Current
)
2423 case AL_SOURCE_TYPE
:
2424 if constexpr(std::is_integral_v
<T
>)
2427 values
[0] = Source
->SourceType
;
2432 case AL_DIRECT_FILTER_GAINHF_AUTO
:
2433 if constexpr(std::is_integral_v
<T
>)
2436 values
[0] = Source
->DryGainHFAuto
;
2441 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
2442 if constexpr(std::is_integral_v
<T
>)
2445 values
[0] = Source
->WetGainAuto
;
2450 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
2451 if constexpr(std::is_integral_v
<T
>)
2454 values
[0] = Source
->WetGainHFAuto
;
2459 case AL_DIRECT_CHANNELS_SOFT
:
2460 if constexpr(std::is_integral_v
<T
>)
2463 values
[0] = EnumFromDirectMode(Source
->DirectChannels
);
2468 case AL_DISTANCE_MODEL
:
2469 if constexpr(std::is_integral_v
<T
>)
2472 values
[0] = ALenumFromDistanceModel(Source
->mDistanceModel
);
2477 case AL_SOURCE_RESAMPLER_SOFT
:
2478 if constexpr(std::is_integral_v
<T
>)
2481 values
[0] = static_cast<T
>(Source
->mResampler
);
2486 case AL_SOURCE_SPATIALIZE_SOFT
:
2487 if constexpr(std::is_integral_v
<T
>)
2490 values
[0] = EnumFromSpatializeMode(Source
->mSpatialize
);
2495 case AL_STEREO_MODE_SOFT
:
2496 if constexpr(std::is_integral_v
<T
>)
2499 values
[0] = EnumFromStereoMode(Source
->mStereoMode
);
2504 case AL_DIRECT_FILTER
:
2505 case AL_AUXILIARY_SEND_FILTER
:
2509 ERR("Unexpected %s query property: 0x%04x\n", PropType
<T
>::Name(), prop
);
2510 throw al::context_error
{AL_INVALID_ENUM
, "Invalid source %s query property 0x%04x",
2511 PropType
<T
>::Name(), prop
};
2515 void StartSources(ALCcontext
*const context
, const al::span
<ALsource
*> srchandles
,
2516 const nanoseconds start_time
=nanoseconds::min())
2518 ALCdevice
*device
{context
->mALDevice
.get()};
2519 /* If the device is disconnected, and voices stop on disconnect, go right
2522 if(!device
->Connected
.load(std::memory_order_acquire
)) UNLIKELY
2524 if(context
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
2526 for(ALsource
*source
: srchandles
)
2528 /* TODO: Send state change event? */
2529 source
->Offset
= 0.0;
2530 source
->OffsetType
= AL_NONE
;
2531 source
->state
= AL_STOPPED
;
2537 /* Count the number of reusable voices. */
2538 auto voicelist
= context
->getVoicesSpan();
2539 size_t free_voices
{0};
2540 for(const Voice
*voice
: voicelist
)
2542 free_voices
+= (voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2543 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2544 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false);
2545 if(free_voices
== srchandles
.size())
2548 if(srchandles
.size() != free_voices
) UNLIKELY
2550 const size_t inc_amount
{srchandles
.size() - free_voices
};
2551 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
2552 if(inc_amount
> allvoices
.size() - voicelist
.size())
2554 /* Increase the number of voices to handle the request. */
2555 context
->allocVoices(inc_amount
- (allvoices
.size() - voicelist
.size()));
2557 context
->mActiveVoiceCount
.fetch_add(inc_amount
, std::memory_order_release
);
2558 voicelist
= context
->getVoicesSpan();
2561 auto voiceiter
= voicelist
.begin();
2563 VoiceChange
*tail
{}, *cur
{};
2564 for(ALsource
*source
: srchandles
)
2566 /* Check that there is a queue containing at least one valid, non zero
2569 auto find_buffer
= [](ALbufferQueueItem
&entry
) noexcept
2570 { return entry
.mSampleLen
!= 0 || entry
.mCallback
!= nullptr; };
2571 auto BufferList
= std::find_if(source
->mQueue
.begin(), source
->mQueue
.end(), find_buffer
);
2573 /* If there's nothing to play, go right to stopped. */
2574 if(BufferList
== source
->mQueue
.end()) UNLIKELY
2576 /* NOTE: A source without any playable buffers should not have a
2577 * Voice since it shouldn't be in a playing or paused state. So
2578 * there's no need to look up its voice and clear the source.
2580 source
->Offset
= 0.0;
2581 source
->OffsetType
= AL_NONE
;
2582 source
->state
= AL_STOPPED
;
2587 cur
= tail
= GetVoiceChanger(context
);
2590 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
2591 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
2594 Voice
*voice
{GetSourceVoice(source
, context
)};
2595 switch(GetSourceState(source
, voice
))
2598 /* A source that's paused simply resumes. If there's no voice, it
2599 * was lost from a disconnect, so just start over with a new one.
2601 cur
->mOldVoice
= nullptr;
2603 cur
->mVoice
= voice
;
2604 cur
->mSourceID
= source
->id
;
2605 cur
->mState
= VChangeState::Play
;
2606 source
->state
= AL_PLAYING
;
2608 if(context
->hasEax())
2609 source
->eaxCommit();
2610 #endif // ALSOFT_EAX
2614 /* A source that's already playing is restarted from the beginning.
2615 * Stop the current voice and start a new one so it properly cross-
2616 * fades back to the beginning.
2619 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
2620 cur
->mOldVoice
= voice
;
2625 assert(voice
== nullptr);
2626 cur
->mOldVoice
= nullptr;
2628 if(context
->hasEax())
2629 source
->eaxCommit();
2630 #endif // ALSOFT_EAX
2634 /* Find the next unused voice to play this source with. */
2635 for(;voiceiter
!= voicelist
.end();++voiceiter
,++vidx
)
2637 Voice
*v
{*voiceiter
};
2638 if(v
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2639 && v
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2640 && v
->mPendingChange
.load(std::memory_order_relaxed
) == false)
2646 ASSUME(voice
!= nullptr);
2648 voice
->mPosition
.store(0, std::memory_order_relaxed
);
2649 voice
->mPositionFrac
.store(0, std::memory_order_relaxed
);
2650 voice
->mCurrentBuffer
.store(&source
->mQueue
.front(), std::memory_order_relaxed
);
2651 voice
->mStartTime
= start_time
;
2652 voice
->mFlags
.reset();
2653 /* A source that's not playing or paused has any offset applied when it
2656 if(const ALenum offsettype
{source
->OffsetType
})
2658 const double offset
{source
->Offset
};
2659 source
->OffsetType
= AL_NONE
;
2660 source
->Offset
= 0.0;
2661 if(auto vpos
= GetSampleOffset(source
->mQueue
, offsettype
, offset
))
2663 voice
->mPosition
.store(vpos
->pos
, std::memory_order_relaxed
);
2664 voice
->mPositionFrac
.store(vpos
->frac
, std::memory_order_relaxed
);
2665 voice
->mCurrentBuffer
.store(vpos
->bufferitem
, std::memory_order_relaxed
);
2666 if(vpos
->pos
> 0 || (vpos
->pos
== 0 && vpos
->frac
> 0)
2667 || vpos
->bufferitem
!= &source
->mQueue
.front())
2668 voice
->mFlags
.set(VoiceIsFading
);
2671 InitVoice(voice
, source
, al::to_address(BufferList
), context
, device
);
2673 source
->VoiceIdx
= vidx
;
2674 source
->state
= AL_PLAYING
;
2676 cur
->mVoice
= voice
;
2677 cur
->mSourceID
= source
->id
;
2678 cur
->mState
= VChangeState::Play
;
2681 SendVoiceChanges(context
, tail
);
2686 AL_API
DECL_FUNC2(void, alGenSources
, ALsizei
,n
, ALuint
*,sources
)
2687 FORCE_ALIGN
void AL_APIENTRY
alGenSourcesDirect(ALCcontext
*context
, ALsizei n
, ALuint
*sources
) noexcept
2690 throw al::context_error
{AL_INVALID_VALUE
, "Generating %d sources", n
};
2691 if(n
<= 0) UNLIKELY
return;
2693 std::unique_lock
<std::mutex
> srclock
{context
->mSourceLock
};
2694 ALCdevice
*device
{context
->mALDevice
.get()};
2696 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2697 if(sids
.size() > device
->SourcesMax
-context
->mNumSources
)
2698 throw al::context_error
{AL_OUT_OF_MEMORY
, "Exceeding %u source limit (%u + %d)",
2699 device
->SourcesMax
, context
->mNumSources
, n
};
2700 if(!EnsureSources(context
, sids
.size()))
2701 throw al::context_error
{AL_OUT_OF_MEMORY
, "Failed to allocate %d source%s", n
,
2702 (n
== 1) ? "" : "s"};
2704 std::generate(sids
.begin(), sids
.end(), [context
]{ return AllocSource(context
)->id
; });
2706 catch(al::context_error
& e
) {
2707 context
->setError(e
.errorCode(), "%s", e
.what());
2710 AL_API
DECL_FUNC2(void, alDeleteSources
, ALsizei
,n
, const ALuint
*,sources
)
2711 FORCE_ALIGN
void AL_APIENTRY
alDeleteSourcesDirect(ALCcontext
*context
, ALsizei n
,
2712 const ALuint
*sources
) noexcept
2715 throw al::context_error
{AL_INVALID_VALUE
, "Deleting %d sources", n
};
2716 if(n
<= 0) UNLIKELY
return;
2718 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2720 /* Check that all Sources are valid */
2721 auto validate_source
= [context
](const ALuint sid
) -> bool
2722 { return LookupSource(context
, sid
) != nullptr; };
2724 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2725 auto invsrc
= std::find_if_not(sids
.begin(), sids
.end(), validate_source
);
2726 if(invsrc
!= sids
.end())
2727 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", *invsrc
};
2729 /* All good. Delete source IDs. */
2730 auto delete_source
= [&context
](const ALuint sid
) -> void
2732 if(ALsource
*src
{LookupSource(context
, sid
)})
2733 FreeSource(context
, src
);
2735 std::for_each(sids
.begin(), sids
.end(), delete_source
);
2737 catch(al::context_error
& e
) {
2738 context
->setError(e
.errorCode(), "%s", e
.what());
2741 AL_API
DECL_FUNC1(ALboolean
, alIsSource
, ALuint
,source
)
2742 FORCE_ALIGN ALboolean AL_APIENTRY
alIsSourceDirect(ALCcontext
*context
, ALuint source
) noexcept
2744 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2745 if(LookupSource(context
, source
) != nullptr)
2751 AL_API
DECL_FUNC3(void, alSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
,value
)
2752 FORCE_ALIGN
void AL_APIENTRY
alSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2753 ALfloat value
) noexcept
2755 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2756 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2757 ALsource
*Source
{LookupSource(context
, source
)};
2759 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2761 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2763 catch(al::context_error
& e
) {
2764 context
->setError(e
.errorCode(), "%s", e
.what());
2767 AL_API
DECL_FUNC5(void, alSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
,value1
, ALfloat
,value2
, ALfloat
,value3
)
2768 FORCE_ALIGN
void AL_APIENTRY
alSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2769 ALfloat value1
, ALfloat value2
, ALfloat value3
) noexcept
2771 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2772 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2773 ALsource
*Source
{LookupSource(context
, source
)};
2775 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2777 const std::array fvals
{value1
, value2
, value3
};
2778 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
2780 catch(al::context_error
& e
) {
2781 context
->setError(e
.errorCode(), "%s", e
.what());
2784 AL_API
DECL_FUNC3(void, alSourcefv
, ALuint
,source
, ALenum
,param
, const ALfloat
*,values
)
2785 FORCE_ALIGN
void AL_APIENTRY
alSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2786 const ALfloat
*values
) noexcept
2788 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2789 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2790 ALsource
*Source
{LookupSource(context
, source
)};
2792 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2794 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2796 const ALuint count
{FloatValsByProp(param
)};
2797 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2799 catch(al::context_error
& e
) {
2800 context
->setError(e
.errorCode(), "%s", e
.what());
2804 AL_API
DECL_FUNCEXT3(void, alSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value
)
2805 FORCE_ALIGN
void AL_APIENTRY
alSourcedDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2806 ALdouble value
) noexcept
2808 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2809 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2810 ALsource
*Source
{LookupSource(context
, source
)};
2812 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2814 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1});
2816 catch(al::context_error
& e
) {
2817 context
->setError(e
.errorCode(), "%s", e
.what());
2820 AL_API
DECL_FUNCEXT5(void, alSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value1
, ALdouble
,value2
, ALdouble
,value3
)
2821 FORCE_ALIGN
void AL_APIENTRY
alSource3dDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2822 ALdouble value1
, ALdouble value2
, ALdouble value3
) noexcept
2824 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2825 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2826 ALsource
*Source
{LookupSource(context
, source
)};
2828 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2830 const std::array dvals
{value1
, value2
, value3
};
2831 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
2833 catch(al::context_error
& e
) {
2834 context
->setError(e
.errorCode(), "%s", e
.what());
2837 AL_API
DECL_FUNCEXT3(void, alSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, const ALdouble
*,values
)
2838 FORCE_ALIGN
void AL_APIENTRY
alSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2839 const ALdouble
*values
) noexcept
2841 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2842 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2843 ALsource
*Source
{LookupSource(context
, source
)};
2845 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2847 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2849 const ALuint count
{DoubleValsByProp(param
)};
2850 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2852 catch(al::context_error
& e
) {
2853 context
->setError(e
.errorCode(), "%s", e
.what());
2857 AL_API
DECL_FUNC3(void, alSourcei
, ALuint
,source
, ALenum
,param
, ALint
,value
)
2858 FORCE_ALIGN
void AL_APIENTRY
alSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2859 ALint value
) noexcept
2861 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2862 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2863 ALsource
*Source
{LookupSource(context
, source
)};
2865 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2867 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2869 catch(al::context_error
& e
) {
2870 context
->setError(e
.errorCode(), "%s", e
.what());
2873 AL_API
DECL_FUNC5(void, alSource3i
, ALuint
,buffer
, ALenum
,param
, ALint
,value1
, ALint
,value2
, ALint
,value3
)
2874 FORCE_ALIGN
void AL_APIENTRY
alSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2875 ALint value1
, ALint value2
, ALint value3
) noexcept
2877 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2878 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2879 ALsource
*Source
{LookupSource(context
, source
)};
2881 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2883 const std::array ivals
{value1
, value2
, value3
};
2884 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
2886 catch(al::context_error
& e
) {
2887 context
->setError(e
.errorCode(), "%s", e
.what());
2890 AL_API
DECL_FUNC3(void, alSourceiv
, ALuint
,source
, ALenum
,param
, const ALint
*,values
)
2891 FORCE_ALIGN
void AL_APIENTRY
alSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2892 const ALint
*values
) noexcept
2894 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2895 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2896 ALsource
*Source
{LookupSource(context
, source
)};
2898 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2900 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2902 const ALuint count
{IntValsByProp(param
)};
2903 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2905 catch(al::context_error
& e
) {
2906 context
->setError(e
.errorCode(), "%s", e
.what());
2910 AL_API
DECL_FUNCEXT3(void, alSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value
)
2911 FORCE_ALIGN
void AL_APIENTRY
alSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
,
2912 ALenum param
, ALint64SOFT value
) noexcept
2914 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2915 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2916 ALsource
*Source
{LookupSource(context
, source
)};
2918 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2920 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2922 catch(al::context_error
& e
) {
2923 context
->setError(e
.errorCode(), "%s", e
.what());
2926 AL_API
DECL_FUNCEXT5(void, alSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value1
, ALint64SOFT
,value2
, ALint64SOFT
,value3
)
2927 FORCE_ALIGN
void AL_APIENTRY
alSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
2928 ALenum param
, ALint64SOFT value1
, ALint64SOFT value2
, ALint64SOFT value3
) noexcept
2930 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2931 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2932 ALsource
*Source
{LookupSource(context
, source
)};
2934 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2936 const std::array i64vals
{value1
, value2
, value3
};
2937 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
2939 catch(al::context_error
& e
) {
2940 context
->setError(e
.errorCode(), "%s", e
.what());
2943 AL_API
DECL_FUNCEXT3(void, alSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, const ALint64SOFT
*,values
)
2944 FORCE_ALIGN
void AL_APIENTRY
alSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
2945 ALenum param
, const ALint64SOFT
*values
) noexcept
2947 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2948 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2949 ALsource
*Source
{LookupSource(context
, source
)};
2951 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2953 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2955 const ALuint count
{Int64ValsByProp(param
)};
2956 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2958 catch(al::context_error
& e
) {
2959 context
->setError(e
.errorCode(), "%s", e
.what());
2963 AL_API
DECL_FUNC3(void, alGetSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
*,value
)
2964 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2965 ALfloat
*value
) noexcept
2967 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2968 ALsource
*Source
{LookupSource(context
, source
)};
2970 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2972 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2974 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
2976 catch(al::context_error
& e
) {
2977 context
->setError(e
.errorCode(), "%s", e
.what());
2980 AL_API
DECL_FUNC5(void, alGetSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
*,value1
, ALfloat
*,value2
, ALfloat
*,value3
)
2981 FORCE_ALIGN
void AL_APIENTRY
alGetSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2982 ALfloat
*value1
, ALfloat
*value2
, ALfloat
*value3
) noexcept
2984 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2985 ALsource
*Source
{LookupSource(context
, source
)};
2987 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2988 if(!(value1
&& value2
&& value3
))
2989 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2991 std::array
<float,3> fvals
{};
2992 GetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
2997 catch(al::context_error
& e
) {
2998 context
->setError(e
.errorCode(), "%s", e
.what());
3001 AL_API
DECL_FUNC3(void, alGetSourcefv
, ALuint
,source
, ALenum
,param
, ALfloat
*,values
)
3002 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3003 ALfloat
*values
) noexcept
3005 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3006 ALsource
*Source
{LookupSource(context
, source
)};
3008 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3010 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3012 const ALuint count
{FloatValsByProp(param
)};
3013 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3015 catch(al::context_error
& e
) {
3016 context
->setError(e
.errorCode(), "%s", e
.what());
3020 AL_API
DECL_FUNCEXT3(void, alGetSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value
)
3021 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedDirectSOFT(ALCcontext
*context
, ALuint source
,
3022 ALenum param
, ALdouble
*value
) noexcept
3024 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3025 ALsource
*Source
{LookupSource(context
, source
)};
3027 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3029 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3031 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3033 catch(al::context_error
& e
) {
3034 context
->setError(e
.errorCode(), "%s", e
.what());
3037 AL_API
DECL_FUNCEXT5(void, alGetSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value1
, ALdouble
*,value2
, ALdouble
*,value3
)
3038 FORCE_ALIGN
void AL_APIENTRY
alGetSource3dDirectSOFT(ALCcontext
*context
, ALuint source
,
3039 ALenum param
, ALdouble
*value1
, ALdouble
*value2
, ALdouble
*value3
) noexcept
3041 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3042 ALsource
*Source
{LookupSource(context
, source
)};
3044 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3045 if(!(value1
&& value2
&& value3
))
3046 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3048 std::array
<double,3> dvals
{};
3049 GetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
3054 catch(al::context_error
& e
) {
3055 context
->setError(e
.errorCode(), "%s", e
.what());
3058 AL_API
DECL_FUNCEXT3(void, alGetSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,values
)
3059 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
,
3060 ALenum param
, ALdouble
*values
) noexcept
3062 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3063 ALsource
*Source
{LookupSource(context
, source
)};
3065 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3067 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3069 const ALuint count
{DoubleValsByProp(param
)};
3070 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3072 catch(al::context_error
& e
) {
3073 context
->setError(e
.errorCode(), "%s", e
.what());
3077 AL_API
DECL_FUNC3(void, alGetSourcei
, ALuint
,source
, ALenum
,param
, ALint
*,value
)
3078 FORCE_ALIGN
void AL_APIENTRY
alGetSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3079 ALint
*value
) noexcept
3081 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3082 ALsource
*Source
{LookupSource(context
, source
)};
3084 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3086 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3088 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3090 catch(al::context_error
& e
) {
3091 context
->setError(e
.errorCode(), "%s", e
.what());
3094 AL_API
DECL_FUNC5(void, alGetSource3i
, ALuint
,source
, ALenum
,param
, ALint
*,value1
, ALint
*,value2
, ALint
*,value3
)
3095 FORCE_ALIGN
void AL_APIENTRY
alGetSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3096 ALint
*value1
, ALint
*value2
, ALint
*value3
) noexcept
3098 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3099 ALsource
*Source
{LookupSource(context
, source
)};
3101 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3102 if(!(value1
&& value2
&& value3
))
3103 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3105 std::array
<int,3> ivals
{};
3106 GetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
3111 catch(al::context_error
& e
) {
3112 context
->setError(e
.errorCode(), "%s", e
.what());
3115 AL_API
DECL_FUNC3(void, alGetSourceiv
, ALuint
,source
, ALenum
,param
, ALint
*,values
)
3116 FORCE_ALIGN
void AL_APIENTRY
alGetSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3117 ALint
*values
) noexcept
3119 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3120 ALsource
*Source
{LookupSource(context
, source
)};
3122 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3124 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3126 const ALuint count
{IntValsByProp(param
)};
3127 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3129 catch(al::context_error
& e
) {
3130 context
->setError(e
.errorCode(), "%s", e
.what());
3134 AL_API
DECL_FUNCEXT3(void, alGetSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value
)
3135 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
, ALint64SOFT
*value
) noexcept
3137 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3138 ALsource
*Source
{LookupSource(context
, source
)};
3140 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3142 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3144 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3146 catch(al::context_error
& e
) {
3147 context
->setError(e
.errorCode(), "%s", e
.what());
3150 AL_API
DECL_FUNCEXT5(void, alGetSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value1
, ALint64SOFT
*,value2
, ALint64SOFT
*,value3
)
3151 FORCE_ALIGN
void AL_APIENTRY
alGetSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
3152 ALenum param
, ALint64SOFT
*value1
, ALint64SOFT
*value2
, ALint64SOFT
*value3
) noexcept
3154 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3155 ALsource
*Source
{LookupSource(context
, source
)};
3157 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3158 if(!(value1
&& value2
&& value3
))
3159 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3161 std::array
<int64_t,3> i64vals
{};
3162 GetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
3163 *value1
= i64vals
[0];
3164 *value2
= i64vals
[1];
3165 *value3
= i64vals
[2];
3167 catch(al::context_error
& e
) {
3168 context
->setError(e
.errorCode(), "%s", e
.what());
3171 AL_API
DECL_FUNCEXT3(void, alGetSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,values
)
3172 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
3173 ALenum param
, ALint64SOFT
*values
) noexcept
3175 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3176 ALsource
*Source
{LookupSource(context
, source
)};
3178 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3180 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3182 const ALuint count
{Int64ValsByProp(param
)};
3183 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3185 catch(al::context_error
& e
) {
3186 context
->setError(e
.errorCode(), "%s", e
.what());
3190 AL_API
DECL_FUNC1(void, alSourcePlay
, ALuint
,source
)
3191 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayDirect(ALCcontext
*context
, ALuint source
) noexcept
3193 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3194 ALsource
*Source
{LookupSource(context
, source
)};
3196 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3198 StartSources(context
, {&Source
, 1});
3200 catch(al::context_error
& e
) {
3201 context
->setError(e
.errorCode(), "%s", e
.what());
3204 FORCE_ALIGN
DECL_FUNCEXT2(void, alSourcePlayAtTime
,SOFT
, ALuint
,source
, ALint64SOFT
,start_time
)
3205 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimeDirectSOFT(ALCcontext
*context
, ALuint source
,
3206 ALint64SOFT start_time
) noexcept
3209 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3211 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3212 ALsource
*Source
{LookupSource(context
, source
)};
3214 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3216 StartSources(context
, {&Source
, 1}, nanoseconds
{start_time
});
3218 catch(al::context_error
& e
) {
3219 context
->setError(e
.errorCode(), "%s", e
.what());
3222 AL_API
DECL_FUNC2(void, alSourcePlayv
, ALsizei
,n
, const ALuint
*,sources
)
3223 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayvDirect(ALCcontext
*context
, ALsizei n
,
3224 const ALuint
*sources
) noexcept
3227 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3228 if(n
<= 0) UNLIKELY
return;
3230 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3231 source_store_variant source_store
;
3232 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3234 if(count
> std::tuple_size_v
<source_store_array
>)
3235 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3236 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3239 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3240 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3242 if(ALsource
*src
{LookupSource(context
, sid
)})
3244 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3246 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3248 StartSources(context
, srchandles
);
3250 catch(al::context_error
& e
) {
3251 context
->setError(e
.errorCode(), "%s", e
.what());
3254 FORCE_ALIGN
DECL_FUNCEXT3(void, alSourcePlayAtTimev
,SOFT
, ALsizei
,n
, const ALuint
*,sources
, ALint64SOFT
,start_time
)
3255 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimevDirectSOFT(ALCcontext
*context
, ALsizei n
,
3256 const ALuint
*sources
, ALint64SOFT start_time
) noexcept
3259 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3260 if(n
<= 0) UNLIKELY
return;
3263 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3265 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3266 source_store_variant source_store
;
3267 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3269 if(count
> std::tuple_size_v
<source_store_array
>)
3270 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3271 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3274 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3275 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3277 if(ALsource
*src
{LookupSource(context
, sid
)})
3279 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3281 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3283 StartSources(context
, srchandles
, nanoseconds
{start_time
});
3285 catch(al::context_error
& e
) {
3286 context
->setError(e
.errorCode(), "%s", e
.what());
3290 AL_API
DECL_FUNC1(void, alSourcePause
, ALuint
,source
)
3291 FORCE_ALIGN
void AL_APIENTRY
alSourcePauseDirect(ALCcontext
*context
, ALuint source
) noexcept
3292 { alSourcePausevDirect(context
, 1, &source
); }
3294 AL_API
DECL_FUNC2(void, alSourcePausev
, ALsizei
,n
, const ALuint
*,sources
)
3295 FORCE_ALIGN
void AL_APIENTRY
alSourcePausevDirect(ALCcontext
*context
, ALsizei n
,
3296 const ALuint
*sources
) noexcept
3299 throw al::context_error
{AL_INVALID_VALUE
, "Pausing %d sources", n
};
3300 if(n
<= 0) UNLIKELY
return;
3302 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3303 source_store_variant source_store
;
3304 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3306 if(count
> std::tuple_size_v
<source_store_array
>)
3307 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3308 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3311 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3312 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3314 if(ALsource
*src
{LookupSource(context
, sid
)})
3316 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3318 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3320 /* Pausing has to be done in two steps. First, for each source that's
3321 * detected to be playing, chamge the voice (asynchronously) to
3324 VoiceChange
*tail
{}, *cur
{};
3325 for(ALsource
*source
: srchandles
)
3327 Voice
*voice
{GetSourceVoice(source
, context
)};
3328 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3331 cur
= tail
= GetVoiceChanger(context
);
3334 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3335 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3337 cur
->mVoice
= voice
;
3338 cur
->mSourceID
= source
->id
;
3339 cur
->mState
= VChangeState::Pause
;
3344 SendVoiceChanges(context
, tail
);
3345 /* Second, now that the voice changes have been sent, because it's
3346 * possible that the voice stopped after it was detected playing and
3347 * before the voice got paused, recheck that the source is still
3348 * considered playing and set it to paused if so.
3350 for(ALsource
*source
: srchandles
)
3352 Voice
*voice
{GetSourceVoice(source
, context
)};
3353 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3354 source
->state
= AL_PAUSED
;
3358 catch(al::context_error
& e
) {
3359 context
->setError(e
.errorCode(), "%s", e
.what());
3363 AL_API
DECL_FUNC1(void, alSourceStop
, ALuint
,source
)
3364 FORCE_ALIGN
void AL_APIENTRY
alSourceStopDirect(ALCcontext
*context
, ALuint source
) noexcept
3365 { alSourceStopvDirect(context
, 1, &source
); }
3367 AL_API
DECL_FUNC2(void, alSourceStopv
, ALsizei
,n
, const ALuint
*,sources
)
3368 FORCE_ALIGN
void AL_APIENTRY
alSourceStopvDirect(ALCcontext
*context
, ALsizei n
,
3369 const ALuint
*sources
) noexcept
3372 throw al::context_error
{AL_INVALID_VALUE
, "Stopping %d sources", n
};
3373 if(n
<= 0) UNLIKELY
return;
3375 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3376 source_store_variant source_store
;
3377 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3379 if(count
> std::tuple_size_v
<source_store_array
>)
3380 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3381 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3384 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3385 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3387 if(ALsource
*src
{LookupSource(context
, sid
)})
3389 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3391 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3393 VoiceChange
*tail
{}, *cur
{};
3394 for(ALsource
*source
: srchandles
)
3396 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3399 cur
= tail
= GetVoiceChanger(context
);
3402 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3403 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3405 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3406 cur
->mVoice
= voice
;
3407 cur
->mSourceID
= source
->id
;
3408 cur
->mState
= VChangeState::Stop
;
3409 source
->state
= AL_STOPPED
;
3411 source
->Offset
= 0.0;
3412 source
->OffsetType
= AL_NONE
;
3413 source
->VoiceIdx
= InvalidVoiceIndex
;
3416 SendVoiceChanges(context
, tail
);
3418 catch(al::context_error
& e
) {
3419 context
->setError(e
.errorCode(), "%s", e
.what());
3423 AL_API
DECL_FUNC1(void, alSourceRewind
, ALuint
,source
)
3424 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindDirect(ALCcontext
*context
, ALuint source
) noexcept
3425 { alSourceRewindvDirect(context
, 1, &source
); }
3427 AL_API
DECL_FUNC2(void, alSourceRewindv
, ALsizei
,n
, const ALuint
*,sources
)
3428 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindvDirect(ALCcontext
*context
, ALsizei n
,
3429 const ALuint
*sources
) noexcept
3432 throw al::context_error
{AL_INVALID_VALUE
, "Rewinding %d sources", n
};
3433 if(n
<= 0) UNLIKELY
return;
3435 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3436 source_store_variant source_store
;
3437 const auto srchandles
= [&source_store
](size_t count
) -> al::span
<ALsource
*>
3439 if(count
> std::tuple_size_v
<source_store_array
>)
3440 return al::span
{source_store
.emplace
<source_store_vector
>(count
)};
3441 return al::span
{source_store
.emplace
<source_store_array
>()}.first(count
);
3444 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3445 auto lookup_src
= [context
](const ALuint sid
) -> ALsource
*
3447 if(ALsource
*src
{LookupSource(context
, sid
)})
3449 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3451 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(), lookup_src
);
3453 VoiceChange
*tail
{}, *cur
{};
3454 for(ALsource
*source
: srchandles
)
3456 Voice
*voice
{GetSourceVoice(source
, context
)};
3457 if(source
->state
!= AL_INITIAL
)
3460 cur
= tail
= GetVoiceChanger(context
);
3463 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3464 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3467 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3468 cur
->mVoice
= voice
;
3469 cur
->mSourceID
= source
->id
;
3470 cur
->mState
= VChangeState::Reset
;
3471 source
->state
= AL_INITIAL
;
3473 source
->Offset
= 0.0;
3474 source
->OffsetType
= AL_NONE
;
3475 source
->VoiceIdx
= InvalidVoiceIndex
;
3478 SendVoiceChanges(context
, tail
);
3480 catch(al::context_error
& e
) {
3481 context
->setError(e
.errorCode(), "%s", e
.what());
3485 AL_API
DECL_FUNC3(void, alSourceQueueBuffers
, ALuint
,source
, ALsizei
,nb
, const ALuint
*,buffers
)
3486 FORCE_ALIGN
void AL_APIENTRY
alSourceQueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3487 ALsizei nb
, const ALuint
*buffers
) noexcept
3490 throw al::context_error
{AL_INVALID_VALUE
, "Queueing %d buffers", nb
};
3491 if(nb
<= 0) UNLIKELY
return;
3493 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3494 ALsource
*source
{LookupSource(context
,src
)};
3496 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3498 /* Can't queue on a Static Source */
3499 if(source
->SourceType
== AL_STATIC
)
3500 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing onto static source %u", src
};
3502 /* Check for a valid Buffer, for its frequency and format */
3503 ALCdevice
*device
{context
->mALDevice
.get()};
3504 ALbuffer
*BufferFmt
{nullptr};
3505 for(auto &item
: source
->mQueue
)
3507 BufferFmt
= item
.mBuffer
;
3508 if(BufferFmt
) break;
3511 std::unique_lock
<std::mutex
> buflock
{device
->BufferLock
};
3512 const auto bids
= al::span
{buffers
, static_cast<ALuint
>(nb
)};
3513 const size_t NewListStart
{source
->mQueue
.size()};
3515 ALbufferQueueItem
*BufferList
{nullptr};
3516 std::for_each(bids
.cbegin(), bids
.cend(),
3517 [source
,device
,&BufferFmt
,&BufferList
](const ALuint bid
)
3519 ALbuffer
*buffer
{bid
? LookupBuffer(device
, bid
) : nullptr};
3521 throw al::context_error
{AL_INVALID_NAME
, "Queueing invalid buffer ID %u", bid
};
3525 if(buffer
->mSampleRate
< 1)
3526 throw al::context_error
{AL_INVALID_OPERATION
,
3527 "Queueing buffer %u with no format", buffer
->id
};
3529 if(buffer
->mCallback
)
3530 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing callback buffer %u",
3533 if(buffer
->MappedAccess
!= 0 && !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
3534 throw al::context_error
{AL_INVALID_OPERATION
,
3535 "Queueing non-persistently mapped buffer %u", buffer
->id
};
3538 source
->mQueue
.emplace_back();
3540 BufferList
= &source
->mQueue
.back();
3543 auto &item
= source
->mQueue
.back();
3544 BufferList
->mNext
.store(&item
, std::memory_order_relaxed
);
3548 BufferList
->mBlockAlign
= buffer
->mBlockAlign
;
3549 BufferList
->mSampleLen
= buffer
->mSampleLen
;
3550 BufferList
->mLoopEnd
= buffer
->mSampleLen
;
3551 BufferList
->mSamples
= buffer
->mData
;
3552 BufferList
->mBuffer
= buffer
;
3553 IncrementRef(buffer
->ref
);
3555 bool fmt_mismatch
{false};
3556 if(BufferFmt
== nullptr)
3560 fmt_mismatch
|= BufferFmt
->mSampleRate
!= buffer
->mSampleRate
;
3561 fmt_mismatch
|= BufferFmt
->mChannels
!= buffer
->mChannels
;
3562 fmt_mismatch
|= BufferFmt
->mType
!= buffer
->mType
;
3563 if(BufferFmt
->isBFormat())
3565 fmt_mismatch
|= BufferFmt
->mAmbiLayout
!= buffer
->mAmbiLayout
;
3566 fmt_mismatch
|= BufferFmt
->mAmbiScaling
!= buffer
->mAmbiScaling
;
3568 fmt_mismatch
|= BufferFmt
->mAmbiOrder
!= buffer
->mAmbiOrder
;
3571 throw al::context_error
{AL_INVALID_OPERATION
,
3572 "Queueing buffer with mismatched format\n"
3573 " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt
->mSampleRate
,
3574 NameFromFormat(BufferFmt
->mType
), NameFromFormat(BufferFmt
->mChannels
),
3575 buffer
->mSampleRate
, NameFromFormat(buffer
->mType
),
3576 NameFromFormat(buffer
->mChannels
)};
3580 /* A buffer failed (invalid ID or format), or there was some other
3581 * unexpected error, so unlock and release each buffer we had.
3583 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3584 for(;iter
!= source
->mQueue
.end();++iter
)
3586 if(ALbuffer
*buf
{iter
->mBuffer
})
3587 DecrementRef(buf
->ref
);
3589 source
->mQueue
.resize(NewListStart
);
3592 /* All buffers good. */
3595 /* Source is now streaming */
3596 source
->SourceType
= AL_STREAMING
;
3598 if(NewListStart
!= 0)
3600 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3601 (iter
-1)->mNext
.store(al::to_address(iter
), std::memory_order_release
);
3604 catch(al::context_error
& e
) {
3605 context
->setError(e
.errorCode(), "%s", e
.what());
3608 AL_API
DECL_FUNC3(void, alSourceUnqueueBuffers
, ALuint
,source
, ALsizei
,nb
, ALuint
*,buffers
)
3609 FORCE_ALIGN
void AL_APIENTRY
alSourceUnqueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3610 ALsizei nb
, ALuint
*buffers
) noexcept
3613 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffers", nb
};
3614 if(nb
<= 0) UNLIKELY
return;
3616 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3617 ALsource
*source
{LookupSource(context
,src
)};
3619 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3621 if(source
->SourceType
!= AL_STREAMING
)
3622 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from a non-streaming source %u",src
};
3624 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from looping source %u", src
};
3626 /* Make sure enough buffers have been processed to unqueue. */
3627 const al::span bids
{buffers
, static_cast<ALuint
>(nb
)};
3628 size_t processed
{0};
3629 if(source
->state
!= AL_INITIAL
) LIKELY
3631 VoiceBufferItem
*Current
{nullptr};
3632 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3633 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
3634 for(auto &item
: source
->mQueue
)
3636 if(&item
== Current
)
3641 if(processed
< bids
.size())
3642 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffer%s (only %zu processed)",
3643 nb
, (nb
==1)?"":"s", processed
};
3645 std::generate(bids
.begin(), bids
.end(), [source
]() noexcept
-> ALuint
3647 auto &head
= source
->mQueue
.front();
3649 if(ALbuffer
*buffer
{head
.mBuffer
})
3652 DecrementRef(buffer
->ref
);
3654 source
->mQueue
.pop_front();
3658 catch(al::context_error
& e
) {
3659 context
->setError(e
.errorCode(), "%s", e
.what());
3663 AL_API
void AL_APIENTRY
alSourceQueueBufferLayersSOFT(ALuint
, ALsizei
, const ALuint
*) noexcept
3665 ContextRef context
{GetContextRef()};
3666 if(!context
) UNLIKELY
return;
3668 context
->setError(AL_INVALID_OPERATION
, "alSourceQueueBufferLayersSOFT not supported");
3672 ALsource::ALsource() noexcept
3675 Direct
.GainHF
= 1.0f
;
3676 Direct
.HFReference
= LowPassFreqRef
;
3677 Direct
.GainLF
= 1.0f
;
3678 Direct
.LFReference
= HighPassFreqRef
;
3679 for(auto &send
: Send
)
3681 send
.Slot
= nullptr;
3684 send
.HFReference
= LowPassFreqRef
;
3686 send
.LFReference
= HighPassFreqRef
;
3690 ALsource::~ALsource()
3692 for(auto &item
: mQueue
)
3694 if(ALbuffer
*buffer
{item
.mBuffer
})
3695 DecrementRef(buffer
->ref
);
3698 auto clear_send
= [](ALsource::SendData
&send
) -> void
3699 { if(send
.Slot
) DecrementRef(send
.Slot
->ref
); };
3700 std::for_each(Send
.begin(), Send
.end(), clear_send
);
3703 void UpdateAllSourceProps(ALCcontext
*context
)
3705 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3706 auto voicelist
= context
->getVoicesSpan();
3708 for(Voice
*voice
: voicelist
)
3710 ALuint sid
{voice
->mSourceID
.load(std::memory_order_acquire
)};
3711 ALsource
*source
{sid
? LookupSource(context
, sid
) : nullptr};
3712 if(source
&& source
->VoiceIdx
== vidx
)
3714 if(std::exchange(source
->mPropsDirty
, false))
3715 UpdateSourceProps(source
, voice
, context
);
3721 void ALsource::SetName(ALCcontext
*context
, ALuint id
, std::string_view name
)
3723 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3725 auto source
= LookupSource(context
, id
);
3727 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", id
};
3729 context
->mSourceNames
.insert_or_assign(id
, name
);
3733 SourceSubList::~SourceSubList()
3738 uint64_t usemask
{~FreeMask
};
3741 const int idx
{al::countr_zero(usemask
)};
3742 usemask
&= ~(1_u64
<< idx
);
3743 std::destroy_at(al::to_address(Sources
->begin() + idx
));
3745 FreeMask
= ~usemask
;
3746 SubListAllocator
{}.deallocate(Sources
, 1);
3752 void ALsource::eaxInitialize(ALCcontext
*context
) noexcept
3754 assert(context
!= nullptr);
3755 mEaxAlContext
= context
;
3757 mEaxPrimaryFxSlotId
= context
->eaxGetPrimaryFxSlotIndex();
3760 eax1_translate(mEax1
.i
, mEax
);
3765 void ALsource::eaxDispatch(const EaxCall
& call
)
3767 call
.is_get() ? eax_get(call
) : eax_set(call
);
3770 ALsource
* ALsource::EaxLookupSource(ALCcontext
& al_context
, ALuint source_id
) noexcept
3772 return LookupSource(&al_context
, source_id
);
3775 [[noreturn
]] void ALsource::eax_fail(const char* message
)
3777 throw Exception
{message
};
3780 [[noreturn
]] void ALsource::eax_fail_unknown_property_id()
3782 eax_fail("Unknown property id.");
3785 [[noreturn
]] void ALsource::eax_fail_unknown_version()
3787 eax_fail("Unknown version.");
3790 [[noreturn
]] void ALsource::eax_fail_unknown_active_fx_slot_id()
3792 eax_fail("Unknown active FX slot ID.");
3795 [[noreturn
]] void ALsource::eax_fail_unknown_receiving_fx_slot_id()
3797 eax_fail("Unknown receiving FX slot ID.");
3800 void ALsource::eax_set_sends_defaults(EaxSends
& sends
, const EaxFxSlotIds
& ids
) noexcept
3802 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
3804 auto& send
= sends
[i
];
3805 send
.guidReceivingFXSlotID
= *(ids
[i
]);
3806 send
.lSend
= EAXSOURCE_DEFAULTSEND
;
3807 send
.lSendHF
= EAXSOURCE_DEFAULTSENDHF
;
3808 send
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3809 send
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3810 send
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3811 send
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3812 send
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3813 send
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3817 void ALsource::eax1_set_defaults(Eax1Props
& props
) noexcept
3819 props
.fMix
= EAX_REVERBMIX_USEDISTANCE
;
3822 void ALsource::eax1_set_defaults() noexcept
3824 eax1_set_defaults(mEax1
.i
);
3828 void ALsource::eax2_set_defaults(Eax2Props
& props
) noexcept
3830 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3831 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3832 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3833 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
3834 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3835 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
3836 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
3837 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3838 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3839 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3840 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3841 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3842 props
.dwFlags
= EAXSOURCE_DEFAULTFLAGS
;
3845 void ALsource::eax2_set_defaults() noexcept
3847 eax2_set_defaults(mEax2
.i
);
3851 void ALsource::eax3_set_defaults(Eax3Props
& props
) noexcept
3853 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3854 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3855 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3856 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
3857 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
3858 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
3859 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3860 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3861 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3862 props
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3863 props
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3864 props
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3865 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3866 props
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3867 props
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3868 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3869 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3870 props
.ulFlags
= EAXSOURCE_DEFAULTFLAGS
;
3873 void ALsource::eax3_set_defaults() noexcept
3875 eax3_set_defaults(mEax3
.i
);
3879 void ALsource::eax4_set_sends_defaults(EaxSends
& sends
) noexcept
3881 eax_set_sends_defaults(sends
, eax4_fx_slot_ids
);
3884 void ALsource::eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS
& slots
) noexcept
3886 slots
= EAX40SOURCE_DEFAULTACTIVEFXSLOTID
;
3889 void ALsource::eax4_set_defaults() noexcept
3891 eax3_set_defaults(mEax4
.i
.source
);
3892 eax4_set_sends_defaults(mEax4
.i
.sends
);
3893 eax4_set_active_fx_slots_defaults(mEax4
.i
.active_fx_slots
);
3897 void ALsource::eax5_set_source_defaults(EAX50SOURCEPROPERTIES
& props
) noexcept
3899 eax3_set_defaults(static_cast<Eax3Props
&>(props
));
3900 props
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3903 void ALsource::eax5_set_sends_defaults(EaxSends
& sends
) noexcept
3905 eax_set_sends_defaults(sends
, eax5_fx_slot_ids
);
3908 void ALsource::eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS
& slots
) noexcept
3910 slots
= EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID
;
3913 void ALsource::eax5_set_speaker_levels_defaults(EaxSpeakerLevels
& speaker_levels
) noexcept
3915 for(size_t i
{0};i
< eax_max_speakers
;++i
)
3917 auto& speaker_level
= speaker_levels
[i
];
3918 speaker_level
.lSpeakerID
= static_cast<long>(EAXSPEAKER_FRONT_LEFT
+ i
);
3919 speaker_level
.lLevel
= EAXSOURCE_DEFAULTSPEAKERLEVEL
;
3923 void ALsource::eax5_set_defaults(Eax5Props
& props
) noexcept
3925 eax5_set_source_defaults(props
.source
);
3926 eax5_set_sends_defaults(props
.sends
);
3927 eax5_set_active_fx_slots_defaults(props
.active_fx_slots
);
3928 eax5_set_speaker_levels_defaults(props
.speaker_levels
);
3931 void ALsource::eax5_set_defaults() noexcept
3933 eax5_set_defaults(mEax5
.i
);
3937 void ALsource::eax_set_defaults() noexcept
3939 eax1_set_defaults();
3940 eax2_set_defaults();
3941 eax3_set_defaults();
3942 eax4_set_defaults();
3943 eax5_set_defaults();
3946 void ALsource::eax1_translate(const Eax1Props
& src
, Eax5Props
& dst
) noexcept
3948 eax5_set_defaults(dst
);
3950 if (src
.fMix
== EAX_REVERBMIX_USEDISTANCE
)
3952 dst
.source
.ulFlags
|= EAXSOURCEFLAGS_ROOMAUTO
;
3953 dst
.sends
[0].lSend
= 0;
3957 dst
.source
.ulFlags
&= ~EAXSOURCEFLAGS_ROOMAUTO
;
3958 dst
.sends
[0].lSend
= std::clamp(static_cast<long>(gain_to_level_mb(src
.fMix
)),
3959 EAXSOURCE_MINSEND
, EAXSOURCE_MAXSEND
);
3963 void ALsource::eax2_translate(const Eax2Props
& src
, Eax5Props
& dst
) noexcept
3967 dst
.source
.lDirect
= src
.lDirect
;
3968 dst
.source
.lDirectHF
= src
.lDirectHF
;
3969 dst
.source
.lRoom
= src
.lRoom
;
3970 dst
.source
.lRoomHF
= src
.lRoomHF
;
3971 dst
.source
.lObstruction
= src
.lObstruction
;
3972 dst
.source
.flObstructionLFRatio
= src
.flObstructionLFRatio
;
3973 dst
.source
.lOcclusion
= src
.lOcclusion
;
3974 dst
.source
.flOcclusionLFRatio
= src
.flOcclusionLFRatio
;
3975 dst
.source
.flOcclusionRoomRatio
= src
.flOcclusionRoomRatio
;
3976 dst
.source
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3977 dst
.source
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3978 dst
.source
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3979 dst
.source
.lOutsideVolumeHF
= src
.lOutsideVolumeHF
;
3980 dst
.source
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3981 dst
.source
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3982 dst
.source
.flRoomRolloffFactor
= src
.flRoomRolloffFactor
;
3983 dst
.source
.flAirAbsorptionFactor
= src
.flAirAbsorptionFactor
;
3984 dst
.source
.ulFlags
= src
.dwFlags
;
3985 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3987 // Set everything else to defaults.
3989 eax5_set_sends_defaults(dst
.sends
);
3990 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
3991 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
3994 void ALsource::eax3_translate(const Eax3Props
& src
, Eax5Props
& dst
) noexcept
3998 static_cast<Eax3Props
&>(dst
.source
) = src
;
3999 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4001 // Set everything else to defaults.
4003 eax5_set_sends_defaults(dst
.sends
);
4004 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
4005 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4008 void ALsource::eax4_translate(const Eax4Props
& src
, Eax5Props
& dst
) noexcept
4012 static_cast<Eax3Props
&>(dst
.source
) = src
.source
;
4013 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4017 dst
.sends
= src
.sends
;
4019 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4020 dst
.sends
[i
].guidReceivingFXSlotID
= *(eax5_fx_slot_ids
[i
]);
4024 auto translate_slotid
= [](const GUID
&src_id
) -> GUID
4026 if(src_id
== EAX_NULL_GUID
)
4027 return EAX_NULL_GUID
;
4028 if(src_id
== EAX_PrimaryFXSlotID
)
4029 return EAX_PrimaryFXSlotID
;
4030 if(src_id
== EAXPROPERTYID_EAX40_FXSlot0
)
4031 return EAXPROPERTYID_EAX50_FXSlot0
;
4032 if(src_id
== EAXPROPERTYID_EAX40_FXSlot1
)
4033 return EAXPROPERTYID_EAX50_FXSlot1
;
4034 if(src_id
== EAXPROPERTYID_EAX40_FXSlot2
)
4035 return EAXPROPERTYID_EAX50_FXSlot2
;
4036 if(src_id
== EAXPROPERTYID_EAX40_FXSlot3
)
4037 return EAXPROPERTYID_EAX50_FXSlot3
;
4040 ERR("Unexpected active FX slot ID\n");
4041 return EAX_NULL_GUID
;
4043 const auto src_slots
= al::span
{src
.active_fx_slots
.guidActiveFXSlots
};
4044 const auto dst_slots
= al::span
{dst
.active_fx_slots
.guidActiveFXSlots
};
4045 auto dstiter
= std::transform(src_slots
.cbegin(), src_slots
.cend(), dst_slots
.begin(),
4047 std::fill(dstiter
, dst_slots
.end(), EAX_NULL_GUID
);
4051 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4054 float ALsource::eax_calculate_dst_occlusion_mb(
4055 long src_occlusion_mb
,
4057 float lf_ratio
) noexcept
4059 const auto ratio_1
= path_ratio
+ lf_ratio
- 1.0F
;
4060 const auto ratio_2
= path_ratio
* lf_ratio
;
4061 const auto ratio
= (ratio_2
> ratio_1
) ? ratio_2
: ratio_1
;
4062 const auto dst_occlustion_mb
= static_cast<float>(src_occlusion_mb
) * ratio
;
4063 return dst_occlustion_mb
;
4066 EaxAlLowPassParam
ALsource::eax_create_direct_filter_param() const noexcept
4068 const auto &source
= mEax
.source
;
4070 auto gain_mb
= static_cast<float>(source
.lObstruction
) * source
.flObstructionLFRatio
;
4071 auto gainhf_mb
= static_cast<float>(source
.lObstruction
);
4073 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4075 if(!mEaxActiveFxSlots
[i
])
4078 if(source
.lOcclusion
!= 0)
4080 const auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4081 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4082 const auto is_environmental_fx
= ((fx_slot_eax
.ulFlags
&EAXFXSLOTFLAGS_ENVIRONMENT
) != 0);
4083 const auto is_primary
= (mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index());
4085 if(is_environmental_fx
&& is_primary
)
4087 gain_mb
+= eax_calculate_dst_occlusion_mb(source
.lOcclusion
,
4088 source
.flOcclusionDirectRatio
, source
.flOcclusionLFRatio
);
4090 gainhf_mb
+= static_cast<float>(source
.lOcclusion
) * source
.flOcclusionDirectRatio
;
4094 const auto& send
= mEax
.sends
[i
];
4095 if(send
.lOcclusion
!= 0)
4097 gain_mb
+= eax_calculate_dst_occlusion_mb(send
.lOcclusion
, send
.flOcclusionDirectRatio
,
4098 send
.flOcclusionLFRatio
);
4100 gainhf_mb
+= static_cast<float>(send
.lOcclusion
) * send
.flOcclusionDirectRatio
;
4104 /* gainhf_mb is the absolute mBFS of the filter's high-frequency volume,
4105 * and gain_mb is the absolute mBFS of the filter's low-frequency volume.
4106 * Adjust the HF volume to be relative to the LF volume, to make the
4107 * appropriate main and relative HF filter volumes.
4109 * Also add the Direct and DirectHF properties to the filter, which are
4110 * already the main and relative HF volumes.
4112 gainhf_mb
-= gain_mb
- static_cast<float>(source
.lDirectHF
);
4113 gain_mb
+= static_cast<float>(source
.lDirect
);
4115 return EaxAlLowPassParam
{level_mb_to_gain(gain_mb
),
4116 std::min(level_mb_to_gain(gainhf_mb
), 1.0f
)};
4119 EaxAlLowPassParam
ALsource::eax_create_room_filter_param(
4120 const ALeffectslot
& fx_slot
,
4121 const EAXSOURCEALLSENDPROPERTIES
& send
) const noexcept
4123 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4124 const auto is_environmental_fx
= bool{(fx_slot_eax
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0};
4125 const auto is_primary
= bool{mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index()};
4127 auto gain_mb
= (static_cast<float>(fx_slot_eax
.lOcclusion
) * fx_slot_eax
.flOcclusionLFRatio
)
4128 + eax_calculate_dst_occlusion_mb(send
.lOcclusion
, send
.flOcclusionRoomRatio
,
4129 send
.flOcclusionLFRatio
)
4130 + (static_cast<float>(send
.lExclusion
) * send
.flExclusionLFRatio
);
4132 auto gainhf_mb
= static_cast<float>(fx_slot_eax
.lOcclusion
)
4133 + (static_cast<float>(send
.lOcclusion
) * send
.flOcclusionRoomRatio
);
4135 if(is_environmental_fx
&& is_primary
)
4137 const auto &source
= mEax
.source
;
4139 gain_mb
+= eax_calculate_dst_occlusion_mb(source
.lOcclusion
, source
.flOcclusionRoomRatio
,
4140 source
.flOcclusionLFRatio
);
4141 gain_mb
+= static_cast<float>(source
.lExclusion
) * source
.flExclusionLFRatio
;
4143 gainhf_mb
+= static_cast<float>(source
.lOcclusion
) * source
.flOcclusionRoomRatio
;
4144 gainhf_mb
+= static_cast<float>(source
.lExclusion
+ send
.lExclusion
);
4147 gainhf_mb
-= gain_mb
- static_cast<float>(send
.lSendHF
);
4148 gain_mb
+= static_cast<float>(send
.lSend
);
4149 if(is_environmental_fx
)
4151 const auto &source
= mEax
.source
;
4152 gain_mb
+= static_cast<float>(source
.lRoom
);
4153 gainhf_mb
+= static_cast<float>(source
.lRoomHF
);
4156 return EaxAlLowPassParam
{level_mb_to_gain(gain_mb
),
4157 std::min(level_mb_to_gain(gainhf_mb
), 1.0f
)};
4160 void ALsource::eax_update_direct_filter()
4162 const auto& direct_param
= eax_create_direct_filter_param();
4163 Direct
.Gain
= direct_param
.gain
;
4164 Direct
.GainHF
= direct_param
.gain_hf
;
4165 Direct
.HFReference
= LowPassFreqRef
;
4166 Direct
.GainLF
= 1.0f
;
4167 Direct
.LFReference
= HighPassFreqRef
;
4171 void ALsource::eax_update_room_filters()
4173 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4175 if(!mEaxActiveFxSlots
[i
])
4178 auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4179 const auto& send
= mEax
.sends
[i
];
4180 const auto& room_param
= eax_create_room_filter_param(fx_slot
, send
);
4181 eax_set_al_source_send(&fx_slot
, i
, room_param
);
4185 void ALsource::eax_set_efx_outer_gain_hf()
4187 OuterGainHF
= std::clamp(
4188 level_mb_to_gain(static_cast<float>(mEax
.source
.lOutsideVolumeHF
)),
4189 AL_MIN_CONE_OUTER_GAINHF
,
4190 AL_MAX_CONE_OUTER_GAINHF
);
4193 void ALsource::eax_set_efx_doppler_factor()
4195 DopplerFactor
= mEax
.source
.flDopplerFactor
;
4198 void ALsource::eax_set_efx_rolloff_factor()
4200 RolloffFactor2
= mEax
.source
.flRolloffFactor
;
4203 void ALsource::eax_set_efx_room_rolloff_factor()
4205 RoomRolloffFactor
= mEax
.source
.flRoomRolloffFactor
;
4208 void ALsource::eax_set_efx_air_absorption_factor()
4210 AirAbsorptionFactor
= mEax
.source
.flAirAbsorptionFactor
;
4213 void ALsource::eax_set_efx_dry_gain_hf_auto()
4215 DryGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_DIRECTHFAUTO
) != 0);
4218 void ALsource::eax_set_efx_wet_gain_auto()
4220 WetGainAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMAUTO
) != 0);
4223 void ALsource::eax_set_efx_wet_gain_hf_auto()
4225 WetGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMHFAUTO
) != 0);
4228 void ALsource::eax1_set(const EaxCall
& call
, Eax1Props
& props
)
4230 switch (call
.get_property_id()) {
4231 case DSPROPERTY_EAXBUFFER_ALL
:
4232 eax_defer
<Eax1SourceAllValidator
>(call
, props
);
4235 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4236 eax_defer
<Eax1SourceReverbMixValidator
>(call
, props
.fMix
);
4240 eax_fail_unknown_property_id();
4244 void ALsource::eax2_set(const EaxCall
& call
, Eax2Props
& props
)
4246 switch (call
.get_property_id()) {
4247 case DSPROPERTY_EAX20BUFFER_NONE
:
4250 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4251 eax_defer
<Eax2SourceAllValidator
>(call
, props
);
4254 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4255 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4258 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4259 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4262 case DSPROPERTY_EAX20BUFFER_ROOM
:
4263 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4266 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4267 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4270 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4271 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4274 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4275 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4278 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4279 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4282 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4283 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4286 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4287 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4290 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4291 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4294 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4295 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4298 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4299 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4302 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4303 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.dwFlags
);
4307 eax_fail_unknown_property_id();
4311 void ALsource::eax3_set(const EaxCall
& call
, Eax3Props
& props
)
4313 switch (call
.get_property_id()) {
4314 case EAXSOURCE_NONE
:
4317 case EAXSOURCE_ALLPARAMETERS
:
4318 eax_defer
<Eax3SourceAllValidator
>(call
, props
);
4321 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4322 eax_defer_sub
<Eax4ObstructionValidator
, EAXOBSTRUCTIONPROPERTIES
>(call
, props
.lObstruction
);
4325 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4326 eax_defer_sub
<Eax4OcclusionValidator
, EAXOCCLUSIONPROPERTIES
>(call
, props
.lOcclusion
);
4329 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4330 eax_defer_sub
<Eax4ExclusionValidator
, EAXEXCLUSIONPROPERTIES
>(call
, props
.lExclusion
);
4333 case EAXSOURCE_DIRECT
:
4334 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4337 case EAXSOURCE_DIRECTHF
:
4338 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4341 case EAXSOURCE_ROOM
:
4342 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4345 case EAXSOURCE_ROOMHF
:
4346 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4349 case EAXSOURCE_OBSTRUCTION
:
4350 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4353 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4354 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4357 case EAXSOURCE_OCCLUSION
:
4358 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4361 case EAXSOURCE_OCCLUSIONLFRATIO
:
4362 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4365 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4366 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4369 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4370 eax_defer
<Eax3SourceOcclusionDirectRatioValidator
>(call
, props
.flOcclusionDirectRatio
);
4373 case EAXSOURCE_EXCLUSION
:
4374 eax_defer
<Eax3SourceExclusionValidator
>(call
, props
.lExclusion
);
4377 case EAXSOURCE_EXCLUSIONLFRATIO
:
4378 eax_defer
<Eax3SourceExclusionLfRatioValidator
>(call
, props
.flExclusionLFRatio
);
4381 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4382 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4385 case EAXSOURCE_DOPPLERFACTOR
:
4386 eax_defer
<Eax3SourceDopplerFactorValidator
>(call
, props
.flDopplerFactor
);
4389 case EAXSOURCE_ROLLOFFFACTOR
:
4390 eax_defer
<Eax3SourceRolloffFactorValidator
>(call
, props
.flRolloffFactor
);
4393 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4394 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4397 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4398 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4401 case EAXSOURCE_FLAGS
:
4402 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.ulFlags
);
4406 eax_fail_unknown_property_id();
4410 void ALsource::eax4_set(const EaxCall
& call
, Eax4Props
& props
)
4412 switch (call
.get_property_id()) {
4413 case EAXSOURCE_NONE
:
4414 case EAXSOURCE_ALLPARAMETERS
:
4415 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4416 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4417 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4418 case EAXSOURCE_DIRECT
:
4419 case EAXSOURCE_DIRECTHF
:
4420 case EAXSOURCE_ROOM
:
4421 case EAXSOURCE_ROOMHF
:
4422 case EAXSOURCE_OBSTRUCTION
:
4423 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4424 case EAXSOURCE_OCCLUSION
:
4425 case EAXSOURCE_OCCLUSIONLFRATIO
:
4426 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4427 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4428 case EAXSOURCE_EXCLUSION
:
4429 case EAXSOURCE_EXCLUSIONLFRATIO
:
4430 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4431 case EAXSOURCE_DOPPLERFACTOR
:
4432 case EAXSOURCE_ROLLOFFFACTOR
:
4433 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4434 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4435 case EAXSOURCE_FLAGS
:
4436 eax3_set(call
, props
.source
);
4439 case EAXSOURCE_SENDPARAMETERS
:
4440 eax4_defer_sends
<Eax4SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4443 case EAXSOURCE_ALLSENDPARAMETERS
:
4444 eax4_defer_sends
<Eax4AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4447 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4448 eax4_defer_sends
<Eax4OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4451 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4452 eax4_defer_sends
<Eax4ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4455 case EAXSOURCE_ACTIVEFXSLOTID
:
4456 eax4_defer_active_fx_slot_id(call
, al::span
{props
.active_fx_slots
.guidActiveFXSlots
});
4460 eax_fail_unknown_property_id();
4464 void ALsource::eax5_defer_all_2d(const EaxCall
& call
, EAX50SOURCEPROPERTIES
& props
)
4466 const auto& src_props
= call
.get_value
<Exception
, const EAXSOURCE2DPROPERTIES
>();
4467 Eax5SourceAll2dValidator
{}(src_props
);
4468 props
.lDirect
= src_props
.lDirect
;
4469 props
.lDirectHF
= src_props
.lDirectHF
;
4470 props
.lRoom
= src_props
.lRoom
;
4471 props
.lRoomHF
= src_props
.lRoomHF
;
4472 props
.ulFlags
= src_props
.ulFlags
;
4475 void ALsource::eax5_defer_speaker_levels(const EaxCall
& call
, EaxSpeakerLevels
& props
)
4477 const auto values
= call
.get_values
<const EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4478 std::for_each(values
.cbegin(), values
.cend(), Eax5SpeakerAllValidator
{});
4480 for (const auto& value
: values
) {
4481 const auto index
= static_cast<size_t>(value
.lSpeakerID
- EAXSPEAKER_FRONT_LEFT
);
4482 props
[index
].lLevel
= value
.lLevel
;
4486 void ALsource::eax5_set(const EaxCall
& call
, Eax5Props
& props
)
4488 switch (call
.get_property_id()) {
4489 case EAXSOURCE_NONE
:
4492 case EAXSOURCE_ALLPARAMETERS
:
4493 eax_defer
<Eax5SourceAllValidator
>(call
, props
.source
);
4496 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4497 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4498 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4499 case EAXSOURCE_DIRECT
:
4500 case EAXSOURCE_DIRECTHF
:
4501 case EAXSOURCE_ROOM
:
4502 case EAXSOURCE_ROOMHF
:
4503 case EAXSOURCE_OBSTRUCTION
:
4504 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4505 case EAXSOURCE_OCCLUSION
:
4506 case EAXSOURCE_OCCLUSIONLFRATIO
:
4507 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4508 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4509 case EAXSOURCE_EXCLUSION
:
4510 case EAXSOURCE_EXCLUSIONLFRATIO
:
4511 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4512 case EAXSOURCE_DOPPLERFACTOR
:
4513 case EAXSOURCE_ROLLOFFFACTOR
:
4514 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4515 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4516 eax3_set(call
, props
.source
);
4519 case EAXSOURCE_FLAGS
:
4520 eax_defer
<Eax5SourceFlagsValidator
>(call
, props
.source
.ulFlags
);
4523 case EAXSOURCE_SENDPARAMETERS
:
4524 eax5_defer_sends
<Eax5SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4527 case EAXSOURCE_ALLSENDPARAMETERS
:
4528 eax5_defer_sends
<Eax5AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4531 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4532 eax5_defer_sends
<Eax5OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4535 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4536 eax5_defer_sends
<Eax5ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4539 case EAXSOURCE_ACTIVEFXSLOTID
:
4540 eax5_defer_active_fx_slot_id(call
, al::span
{props
.active_fx_slots
.guidActiveFXSlots
});
4543 case EAXSOURCE_MACROFXFACTOR
:
4544 eax_defer
<Eax5SourceMacroFXFactorValidator
>(call
, props
.source
.flMacroFXFactor
);
4547 case EAXSOURCE_SPEAKERLEVELS
:
4548 eax5_defer_speaker_levels(call
, props
.speaker_levels
);
4551 case EAXSOURCE_ALL2DPARAMETERS
:
4552 eax5_defer_all_2d(call
, props
.source
);
4556 eax_fail_unknown_property_id();
4560 void ALsource::eax_set(const EaxCall
& call
)
4562 const auto eax_version
= call
.get_version();
4565 case 1: eax1_set(call
, mEax1
.d
); break;
4566 case 2: eax2_set(call
, mEax2
.d
); break;
4567 case 3: eax3_set(call
, mEax3
.d
); break;
4568 case 4: eax4_set(call
, mEax4
.d
); break;
4569 case 5: eax5_set(call
, mEax5
.d
); break;
4570 default: eax_fail_unknown_property_id();
4573 mEaxVersion
= eax_version
;
4576 void ALsource::eax_get_active_fx_slot_id(const EaxCall
& call
, const al::span
<const GUID
> src_ids
)
4578 assert(src_ids
.size()==EAX40_MAX_ACTIVE_FXSLOTS
|| src_ids
.size()==EAX50_MAX_ACTIVE_FXSLOTS
);
4579 const auto dst_ids
= call
.get_values
<GUID
>(src_ids
.size());
4580 std::uninitialized_copy_n(src_ids
.begin(), dst_ids
.size(), dst_ids
.begin());
4583 void ALsource::eax1_get(const EaxCall
& call
, const Eax1Props
& props
)
4585 switch (call
.get_property_id()) {
4586 case DSPROPERTY_EAXBUFFER_ALL
:
4587 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4588 call
.set_value
<Exception
>(props
.fMix
);
4592 eax_fail_unknown_property_id();
4596 void ALsource::eax2_get(const EaxCall
& call
, const Eax2Props
& props
)
4598 switch (call
.get_property_id()) {
4599 case DSPROPERTY_EAX20BUFFER_NONE
:
4602 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4603 call
.set_value
<Exception
>(props
);
4606 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4607 call
.set_value
<Exception
>(props
.lDirect
);
4610 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4611 call
.set_value
<Exception
>(props
.lDirectHF
);
4614 case DSPROPERTY_EAX20BUFFER_ROOM
:
4615 call
.set_value
<Exception
>(props
.lRoom
);
4618 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4619 call
.set_value
<Exception
>(props
.lRoomHF
);
4622 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4623 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4626 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4627 call
.set_value
<Exception
>(props
.lObstruction
);
4630 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4631 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4634 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4635 call
.set_value
<Exception
>(props
.lOcclusion
);
4638 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4639 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4642 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4643 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4646 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4647 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4650 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4651 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4654 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4655 call
.set_value
<Exception
>(props
.dwFlags
);
4659 eax_fail_unknown_property_id();
4663 void ALsource::eax3_get_obstruction(const EaxCall
& call
, const Eax3Props
& props
)
4665 const auto& subprops
= reinterpret_cast<const EAXOBSTRUCTIONPROPERTIES
&>(props
.lObstruction
);
4666 call
.set_value
<Exception
>(subprops
);
4669 void ALsource::eax3_get_occlusion(const EaxCall
& call
, const Eax3Props
& props
)
4671 const auto& subprops
= reinterpret_cast<const EAXOCCLUSIONPROPERTIES
&>(props
.lOcclusion
);
4672 call
.set_value
<Exception
>(subprops
);
4675 void ALsource::eax3_get_exclusion(const EaxCall
& call
, const Eax3Props
& props
)
4677 const auto& subprops
= reinterpret_cast<const EAXEXCLUSIONPROPERTIES
&>(props
.lExclusion
);
4678 call
.set_value
<Exception
>(subprops
);
4681 void ALsource::eax3_get(const EaxCall
& call
, const Eax3Props
& props
)
4683 switch (call
.get_property_id()) {
4684 case EAXSOURCE_NONE
:
4687 case EAXSOURCE_ALLPARAMETERS
:
4688 call
.set_value
<Exception
>(props
);
4691 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4692 eax3_get_obstruction(call
, props
);
4695 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4696 eax3_get_occlusion(call
, props
);
4699 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4700 eax3_get_exclusion(call
, props
);
4703 case EAXSOURCE_DIRECT
:
4704 call
.set_value
<Exception
>(props
.lDirect
);
4707 case EAXSOURCE_DIRECTHF
:
4708 call
.set_value
<Exception
>(props
.lDirectHF
);
4711 case EAXSOURCE_ROOM
:
4712 call
.set_value
<Exception
>(props
.lRoom
);
4715 case EAXSOURCE_ROOMHF
:
4716 call
.set_value
<Exception
>(props
.lRoomHF
);
4719 case EAXSOURCE_OBSTRUCTION
:
4720 call
.set_value
<Exception
>(props
.lObstruction
);
4723 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4724 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4727 case EAXSOURCE_OCCLUSION
:
4728 call
.set_value
<Exception
>(props
.lOcclusion
);
4731 case EAXSOURCE_OCCLUSIONLFRATIO
:
4732 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4735 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4736 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4739 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4740 call
.set_value
<Exception
>(props
.flOcclusionDirectRatio
);
4743 case EAXSOURCE_EXCLUSION
:
4744 call
.set_value
<Exception
>(props
.lExclusion
);
4747 case EAXSOURCE_EXCLUSIONLFRATIO
:
4748 call
.set_value
<Exception
>(props
.flExclusionLFRatio
);
4751 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4752 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4755 case EAXSOURCE_DOPPLERFACTOR
:
4756 call
.set_value
<Exception
>(props
.flDopplerFactor
);
4759 case EAXSOURCE_ROLLOFFFACTOR
:
4760 call
.set_value
<Exception
>(props
.flRolloffFactor
);
4763 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4764 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4767 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4768 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4771 case EAXSOURCE_FLAGS
:
4772 call
.set_value
<Exception
>(props
.ulFlags
);
4776 eax_fail_unknown_property_id();
4780 void ALsource::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
4782 switch (call
.get_property_id()) {
4783 case EAXSOURCE_NONE
:
4786 case EAXSOURCE_ALLPARAMETERS
:
4787 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4788 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4789 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4790 case EAXSOURCE_DIRECT
:
4791 case EAXSOURCE_DIRECTHF
:
4792 case EAXSOURCE_ROOM
:
4793 case EAXSOURCE_ROOMHF
:
4794 case EAXSOURCE_OBSTRUCTION
:
4795 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4796 case EAXSOURCE_OCCLUSION
:
4797 case EAXSOURCE_OCCLUSIONLFRATIO
:
4798 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4799 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4800 case EAXSOURCE_EXCLUSION
:
4801 case EAXSOURCE_EXCLUSIONLFRATIO
:
4802 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4803 case EAXSOURCE_DOPPLERFACTOR
:
4804 case EAXSOURCE_ROLLOFFFACTOR
:
4805 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4806 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4807 case EAXSOURCE_FLAGS
:
4808 eax3_get(call
, props
.source
);
4811 case EAXSOURCE_SENDPARAMETERS
:
4812 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4815 case EAXSOURCE_ALLSENDPARAMETERS
:
4816 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4819 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4820 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4823 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4824 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4827 case EAXSOURCE_ACTIVEFXSLOTID
:
4828 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4832 eax_fail_unknown_property_id();
4836 void ALsource::eax5_get_all_2d(const EaxCall
& call
, const EAX50SOURCEPROPERTIES
& props
)
4838 auto& subprops
= call
.get_value
<Exception
, EAXSOURCE2DPROPERTIES
>();
4839 subprops
.lDirect
= props
.lDirect
;
4840 subprops
.lDirectHF
= props
.lDirectHF
;
4841 subprops
.lRoom
= props
.lRoom
;
4842 subprops
.lRoomHF
= props
.lRoomHF
;
4843 subprops
.ulFlags
= props
.ulFlags
;
4846 void ALsource::eax5_get_speaker_levels(const EaxCall
& call
, const EaxSpeakerLevels
& props
)
4848 const auto subprops
= call
.get_values
<EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4849 std::uninitialized_copy_n(props
.cbegin(), subprops
.size(), subprops
.begin());
4852 void ALsource::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
4854 switch (call
.get_property_id()) {
4855 case EAXSOURCE_NONE
:
4858 case EAXSOURCE_ALLPARAMETERS
:
4859 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4860 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4861 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4862 case EAXSOURCE_DIRECT
:
4863 case EAXSOURCE_DIRECTHF
:
4864 case EAXSOURCE_ROOM
:
4865 case EAXSOURCE_ROOMHF
:
4866 case EAXSOURCE_OBSTRUCTION
:
4867 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4868 case EAXSOURCE_OCCLUSION
:
4869 case EAXSOURCE_OCCLUSIONLFRATIO
:
4870 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4871 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4872 case EAXSOURCE_EXCLUSION
:
4873 case EAXSOURCE_EXCLUSIONLFRATIO
:
4874 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4875 case EAXSOURCE_DOPPLERFACTOR
:
4876 case EAXSOURCE_ROLLOFFFACTOR
:
4877 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4878 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4879 case EAXSOURCE_FLAGS
:
4880 eax3_get(call
, props
.source
);
4883 case EAXSOURCE_SENDPARAMETERS
:
4884 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4887 case EAXSOURCE_ALLSENDPARAMETERS
:
4888 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4891 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4892 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4895 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4896 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4899 case EAXSOURCE_ACTIVEFXSLOTID
:
4900 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4903 case EAXSOURCE_MACROFXFACTOR
:
4904 call
.set_value
<Exception
>(props
.source
.flMacroFXFactor
);
4907 case EAXSOURCE_SPEAKERLEVELS
:
4908 call
.set_value
<Exception
>(props
.speaker_levels
);
4911 case EAXSOURCE_ALL2DPARAMETERS
:
4912 eax5_get_all_2d(call
, props
.source
);
4916 eax_fail_unknown_property_id();
4920 void ALsource::eax_get(const EaxCall
& call
)
4922 switch (call
.get_version()) {
4923 case 1: eax1_get(call
, mEax1
.i
); break;
4924 case 2: eax2_get(call
, mEax2
.i
); break;
4925 case 3: eax3_get(call
, mEax3
.i
); break;
4926 case 4: eax4_get(call
, mEax4
.i
); break;
4927 case 5: eax5_get(call
, mEax5
.i
); break;
4928 default: eax_fail_unknown_version();
4932 void ALsource::eax_set_al_source_send(ALeffectslot
*slot
, size_t sendidx
, const EaxAlLowPassParam
&filter
)
4934 if(sendidx
>= EAX_MAX_FXSLOTS
)
4937 auto &send
= Send
[sendidx
];
4938 send
.Gain
= filter
.gain
;
4939 send
.GainHF
= filter
.gain_hf
;
4940 send
.HFReference
= LowPassFreqRef
;
4942 send
.LFReference
= HighPassFreqRef
;
4945 IncrementRef(slot
->ref
);
4946 if(auto *oldslot
= send
.Slot
)
4947 DecrementRef(oldslot
->ref
);
4953 void ALsource::eax_commit_active_fx_slots()
4955 // Clear all slots to an inactive state.
4956 mEaxActiveFxSlots
.fill(false);
4958 // Mark the set slots as active.
4959 for(const auto& slot_id
: mEax
.active_fx_slots
.guidActiveFXSlots
)
4961 if(slot_id
== EAX_NULL_GUID
)
4964 else if(slot_id
== EAX_PrimaryFXSlotID
)
4966 // Mark primary FX slot as active.
4967 if(mEaxPrimaryFxSlotId
.has_value())
4968 mEaxActiveFxSlots
[*mEaxPrimaryFxSlotId
] = true;
4970 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot0
)
4971 mEaxActiveFxSlots
[0] = true;
4972 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot1
)
4973 mEaxActiveFxSlots
[1] = true;
4974 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot2
)
4975 mEaxActiveFxSlots
[2] = true;
4976 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot3
)
4977 mEaxActiveFxSlots
[3] = true;
4980 // Deactivate EFX auxiliary effect slots for inactive slots. Active slots
4981 // will be updated with the room filters.
4982 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4984 if(!mEaxActiveFxSlots
[i
])
4985 eax_set_al_source_send(nullptr, i
, EaxAlLowPassParam
{1.0f
, 1.0f
});
4989 void ALsource::eax_commit_filters()
4991 eax_update_direct_filter();
4992 eax_update_room_filters();
4995 void ALsource::eaxCommit()
4997 const auto primary_fx_slot_id
= mEaxAlContext
->eaxGetPrimaryFxSlotIndex();
4998 const auto is_primary_fx_slot_id_changed
= (mEaxPrimaryFxSlotId
!= primary_fx_slot_id
);
5000 if(!mEaxChanged
&& !is_primary_fx_slot_id_changed
)
5003 mEaxPrimaryFxSlotId
= primary_fx_slot_id
;
5004 mEaxChanged
= false;
5010 eax1_translate(mEax1
.i
, mEax
);
5014 eax2_translate(mEax2
.i
, mEax
);
5018 eax3_translate(mEax3
.i
, mEax
);
5022 eax4_translate(mEax4
.i
, mEax
);
5030 eax_set_efx_outer_gain_hf();
5031 eax_set_efx_doppler_factor();
5032 eax_set_efx_rolloff_factor();
5033 eax_set_efx_room_rolloff_factor();
5034 eax_set_efx_air_absorption_factor();
5035 eax_set_efx_dry_gain_hf_auto();
5036 eax_set_efx_wet_gain_auto();
5037 eax_set_efx_wet_gain_hf_auto();
5039 eax_commit_active_fx_slots();
5040 eax_commit_filters();
5043 #endif // ALSOFT_EAX