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>;
91 Voice
*GetSourceVoice(ALsource
*source
, ALCcontext
*context
)
93 auto voicelist
= context
->getVoicesSpan();
94 ALuint idx
{source
->VoiceIdx
};
95 if(idx
< voicelist
.size())
97 ALuint sid
{source
->id
};
98 Voice
*voice
= voicelist
[idx
];
99 if(voice
->mSourceID
.load(std::memory_order_acquire
) == sid
)
102 source
->VoiceIdx
= InvalidVoiceIndex
;
107 void UpdateSourceProps(const ALsource
*source
, Voice
*voice
, ALCcontext
*context
)
109 /* Get an unused property container, or allocate a new one as needed. */
110 VoicePropsItem
*props
{context
->mFreeVoiceProps
.load(std::memory_order_acquire
)};
113 context
->allocVoiceProps();
114 props
= context
->mFreeVoiceProps
.load(std::memory_order_acquire
);
116 VoicePropsItem
*next
;
118 next
= props
->next
.load(std::memory_order_relaxed
);
119 } while(context
->mFreeVoiceProps
.compare_exchange_weak(props
, next
,
120 std::memory_order_acq_rel
, std::memory_order_acquire
) == false);
122 props
->Pitch
= source
->Pitch
;
123 props
->Gain
= source
->Gain
;
124 props
->OuterGain
= source
->OuterGain
;
125 props
->MinGain
= source
->MinGain
;
126 props
->MaxGain
= source
->MaxGain
;
127 props
->InnerAngle
= source
->InnerAngle
;
128 props
->OuterAngle
= source
->OuterAngle
;
129 props
->RefDistance
= source
->RefDistance
;
130 props
->MaxDistance
= source
->MaxDistance
;
131 props
->RolloffFactor
= source
->RolloffFactor
133 + source
->RolloffFactor2
136 props
->Position
= source
->Position
;
137 props
->Velocity
= source
->Velocity
;
138 props
->Direction
= source
->Direction
;
139 props
->OrientAt
= source
->OrientAt
;
140 props
->OrientUp
= source
->OrientUp
;
141 props
->HeadRelative
= source
->HeadRelative
;
142 props
->mDistanceModel
= source
->mDistanceModel
;
143 props
->mResampler
= source
->mResampler
;
144 props
->DirectChannels
= source
->DirectChannels
;
145 props
->mSpatializeMode
= source
->mSpatialize
;
147 props
->DryGainHFAuto
= source
->DryGainHFAuto
;
148 props
->WetGainAuto
= source
->WetGainAuto
;
149 props
->WetGainHFAuto
= source
->WetGainHFAuto
;
150 props
->OuterGainHF
= source
->OuterGainHF
;
152 props
->AirAbsorptionFactor
= source
->AirAbsorptionFactor
;
153 props
->RoomRolloffFactor
= source
->RoomRolloffFactor
;
154 props
->DopplerFactor
= source
->DopplerFactor
;
156 props
->StereoPan
= source
->StereoPan
;
158 props
->Radius
= source
->Radius
;
159 props
->EnhWidth
= source
->EnhWidth
;
160 props
->Panning
= source
->mPanningEnabled
? source
->mPan
: 0.0f
;
162 props
->Direct
.Gain
= source
->Direct
.Gain
;
163 props
->Direct
.GainHF
= source
->Direct
.GainHF
;
164 props
->Direct
.HFReference
= source
->Direct
.HFReference
;
165 props
->Direct
.GainLF
= source
->Direct
.GainLF
;
166 props
->Direct
.LFReference
= source
->Direct
.LFReference
;
168 auto copy_send
= [](const ALsource::SendData
&srcsend
) noexcept
-> VoiceProps::SendData
170 VoiceProps::SendData ret
{};
171 ret
.Slot
= srcsend
.Slot
? srcsend
.Slot
->mSlot
: nullptr;
172 ret
.Gain
= srcsend
.Gain
;
173 ret
.GainHF
= srcsend
.GainHF
;
174 ret
.HFReference
= srcsend
.HFReference
;
175 ret
.GainLF
= srcsend
.GainLF
;
176 ret
.LFReference
= srcsend
.LFReference
;
179 std::transform(source
->Send
.cbegin(), source
->Send
.cend(), props
->Send
.begin(), copy_send
);
180 if(!props
->Send
[0].Slot
&& context
->mDefaultSlot
)
181 props
->Send
[0].Slot
= context
->mDefaultSlot
->mSlot
;
183 /* Set the new container for updating internal parameters. */
184 props
= voice
->mUpdate
.exchange(props
, std::memory_order_acq_rel
);
187 /* If there was an unused update container, put it back in the
190 AtomicReplaceHead(context
->mFreeVoiceProps
, props
);
194 /* GetSourceSampleOffset
196 * Gets the current read offset for the given Source, in 32.32 fixed-point
197 * samples. The offset is relative to the start of the queue (not the start of
198 * the current buffer).
200 int64_t GetSourceSampleOffset(ALsource
*Source
, ALCcontext
*context
, nanoseconds
*clocktime
)
202 ALCdevice
*device
{context
->mALDevice
.get()};
203 const VoiceBufferItem
*Current
{};
209 refcount
= device
->waitForMix();
210 *clocktime
= device
->getClockTime();
211 voice
= GetSourceVoice(Source
, context
);
214 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
216 readPos
= int64_t{voice
->mPosition
.load(std::memory_order_relaxed
)} << MixerFracBits
;
217 readPos
+= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
219 std::atomic_thread_fence(std::memory_order_acquire
);
220 } while(refcount
!= device
->mMixCount
.load(std::memory_order_relaxed
));
225 for(auto &item
: Source
->mQueue
)
227 if(&item
== Current
) break;
228 readPos
+= int64_t{item
.mSampleLen
} << MixerFracBits
;
230 if(readPos
> std::numeric_limits
<int64_t>::max() >> (32-MixerFracBits
))
231 return std::numeric_limits
<int64_t>::max();
232 return readPos
<< (32-MixerFracBits
);
235 /* GetSourceSecOffset
237 * Gets the current read offset for the given Source, in seconds. The offset is
238 * relative to the start of the queue (not the start of the current buffer).
240 double GetSourceSecOffset(ALsource
*Source
, ALCcontext
*context
, nanoseconds
*clocktime
)
242 ALCdevice
*device
{context
->mALDevice
.get()};
243 const VoiceBufferItem
*Current
{};
249 refcount
= device
->waitForMix();
250 *clocktime
= device
->getClockTime();
251 voice
= GetSourceVoice(Source
, context
);
254 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
256 readPos
= int64_t{voice
->mPosition
.load(std::memory_order_relaxed
)} << MixerFracBits
;
257 readPos
+= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
259 std::atomic_thread_fence(std::memory_order_acquire
);
260 } while(refcount
!= device
->mMixCount
.load(std::memory_order_relaxed
));
265 const ALbuffer
*BufferFmt
{nullptr};
266 auto BufferList
= Source
->mQueue
.cbegin();
267 while(BufferList
!= Source
->mQueue
.cend() && al::to_address(BufferList
) != Current
)
269 if(!BufferFmt
) BufferFmt
= BufferList
->mBuffer
;
270 readPos
+= int64_t{BufferList
->mSampleLen
} << MixerFracBits
;
273 while(BufferList
!= Source
->mQueue
.cend() && !BufferFmt
)
275 BufferFmt
= BufferList
->mBuffer
;
278 ASSUME(BufferFmt
!= nullptr);
280 return static_cast<double>(readPos
) / double{MixerFracOne
} / BufferFmt
->mSampleRate
;
285 * Gets the current read offset for the given Source, in the appropriate format
286 * (Bytes, Samples or Seconds). The offset is relative to the start of the
287 * queue (not the start of the current buffer).
290 NOINLINE T
GetSourceOffset(ALsource
*Source
, ALenum name
, ALCcontext
*context
)
292 ALCdevice
*device
{context
->mALDevice
.get()};
293 const VoiceBufferItem
*Current
{};
300 refcount
= device
->waitForMix();
301 voice
= GetSourceVoice(Source
, context
);
304 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
306 readPos
= voice
->mPosition
.load(std::memory_order_relaxed
);
307 readPosFrac
= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
309 std::atomic_thread_fence(std::memory_order_acquire
);
310 } while(refcount
!= device
->mMixCount
.load(std::memory_order_relaxed
));
315 const ALbuffer
*BufferFmt
{nullptr};
316 auto BufferList
= Source
->mQueue
.cbegin();
317 while(BufferList
!= Source
->mQueue
.cend() && al::to_address(BufferList
) != Current
)
319 if(!BufferFmt
) BufferFmt
= BufferList
->mBuffer
;
320 readPos
+= BufferList
->mSampleLen
;
323 while(BufferList
!= Source
->mQueue
.cend() && !BufferFmt
)
325 BufferFmt
= BufferList
->mBuffer
;
328 ASSUME(BufferFmt
!= nullptr);
334 if constexpr(std::is_floating_point_v
<T
>)
336 offset
= static_cast<T
>(readPos
) + static_cast<T
>(readPosFrac
)/T
{MixerFracOne
};
337 offset
/= static_cast<T
>(BufferFmt
->mSampleRate
);
341 readPos
/= BufferFmt
->mSampleRate
;
342 offset
= static_cast<T
>(std::clamp
<int64_t>(readPos
, std::numeric_limits
<T
>::min(),
343 std::numeric_limits
<T
>::max()));
347 case AL_SAMPLE_OFFSET
:
348 if constexpr(std::is_floating_point_v
<T
>)
349 offset
= static_cast<T
>(readPos
) + static_cast<T
>(readPosFrac
)/T
{MixerFracOne
};
351 offset
= static_cast<T
>(std::clamp
<int64_t>(readPos
, std::numeric_limits
<T
>::min(),
352 std::numeric_limits
<T
>::max()));
356 const ALuint BlockSamples
{BufferFmt
->mBlockAlign
};
357 const ALuint BlockSize
{BufferFmt
->blockSizeFromFmt()};
358 /* Round down to the block boundary. */
359 readPos
= readPos
/ BlockSamples
* BlockSize
;
361 if constexpr(std::is_floating_point_v
<T
>)
362 offset
= static_cast<T
>(readPos
);
365 if(readPos
> std::numeric_limits
<T
>::max())
366 offset
= RoundDown(std::numeric_limits
<T
>::max(), static_cast<T
>(BlockSize
));
367 else if(readPos
< std::numeric_limits
<T
>::min())
368 offset
= RoundUp(std::numeric_limits
<T
>::min(), static_cast<T
>(BlockSize
));
370 offset
= static_cast<T
>(readPos
);
379 * Gets the length of the given Source's buffer queue, in the appropriate
380 * format (Bytes, Samples or Seconds).
383 NOINLINE T
GetSourceLength(const ALsource
*source
, ALenum name
)
386 const ALbuffer
*BufferFmt
{nullptr};
387 for(auto &listitem
: source
->mQueue
)
390 BufferFmt
= listitem
.mBuffer
;
391 length
+= listitem
.mSampleLen
;
396 ASSUME(BufferFmt
!= nullptr);
399 case AL_SEC_LENGTH_SOFT
:
400 if constexpr(std::is_floating_point_v
<T
>)
401 return static_cast<T
>(length
) / static_cast<T
>(BufferFmt
->mSampleRate
);
403 return static_cast<T
>(std::min
<uint64_t>(length
/BufferFmt
->mSampleRate
,
404 std::numeric_limits
<T
>::max()));
406 case AL_SAMPLE_LENGTH_SOFT
:
407 if constexpr(std::is_floating_point_v
<T
>)
408 return static_cast<T
>(length
);
410 return static_cast<T
>(std::min
<uint64_t>(length
, std::numeric_limits
<T
>::max()));
412 case AL_BYTE_LENGTH_SOFT
:
413 const ALuint BlockSamples
{BufferFmt
->mBlockAlign
};
414 const ALuint BlockSize
{BufferFmt
->blockSizeFromFmt()};
415 /* Round down to the block boundary. */
416 length
= length
/ BlockSamples
* BlockSize
;
418 if constexpr(std::is_floating_point_v
<T
>)
419 return static_cast<T
>(length
);
422 if(length
> std::numeric_limits
<T
>::max())
423 return RoundDown(std::numeric_limits
<T
>::max(), static_cast<T
>(BlockSize
));
424 return static_cast<T
>(length
);
434 ALbufferQueueItem
*bufferitem
;
440 * Retrieves the voice position, fixed-point fraction, and bufferlist item
441 * using the given offset type and offset. If the offset is out of range,
442 * returns an empty optional.
444 std::optional
<VoicePos
> GetSampleOffset(std::deque
<ALbufferQueueItem
> &BufferList
,
445 ALenum OffsetType
, double Offset
)
447 /* Find the first valid Buffer in the Queue */
448 const ALbuffer
*BufferFmt
{nullptr};
449 for(auto &item
: BufferList
)
451 BufferFmt
= item
.mBuffer
;
454 if(!BufferFmt
) UNLIKELY
457 /* Get sample frame offset */
460 double dbloff
, dblfrac
;
464 dblfrac
= std::modf(Offset
*BufferFmt
->mSampleRate
, &dbloff
);
467 /* If there's a negative fraction, reduce the offset to "floor" it,
468 * and convert the fraction to a percentage to the next value (e.g.
469 * -2.75 -> -3 + 0.25).
474 offset
= static_cast<int64_t>(dbloff
);
475 frac
= static_cast<uint
>(std::min(dblfrac
*MixerFracOne
, MixerFracOne
-1.0));
478 case AL_SAMPLE_OFFSET
:
479 dblfrac
= std::modf(Offset
, &dbloff
);
485 offset
= static_cast<int64_t>(dbloff
);
486 frac
= static_cast<uint
>(std::min(dblfrac
*MixerFracOne
, MixerFracOne
-1.0));
490 /* Determine the ByteOffset (and ensure it is block aligned) */
491 Offset
= std::floor(Offset
/ BufferFmt
->blockSizeFromFmt());
492 offset
= static_cast<int64_t>(Offset
) * BufferFmt
->mBlockAlign
;
497 /* Find the bufferlist item this offset belongs to. */
500 if(offset
< std::numeric_limits
<int>::min())
502 return VoicePos
{static_cast<int>(offset
), frac
, &BufferList
.front()};
505 if(BufferFmt
->mCallback
)
508 int64_t totalBufferLen
{0};
509 for(auto &item
: BufferList
)
511 if(totalBufferLen
> offset
)
513 if(item
.mSampleLen
> offset
-totalBufferLen
)
515 /* Offset is in this buffer */
516 return VoicePos
{static_cast<int>(offset
-totalBufferLen
), frac
, &item
};
518 totalBufferLen
+= item
.mSampleLen
;
521 /* Offset is out of range of the queue */
526 void InitVoice(Voice
*voice
, ALsource
*source
, ALbufferQueueItem
*BufferList
, ALCcontext
*context
,
529 voice
->mLoopBuffer
.store(source
->Looping
? &source
->mQueue
.front() : nullptr,
530 std::memory_order_relaxed
);
532 ALbuffer
*buffer
{BufferList
->mBuffer
};
533 voice
->mFrequency
= buffer
->mSampleRate
;
534 if(buffer
->mChannels
== FmtMono
&& source
->mPanningEnabled
)
535 voice
->mFmtChannels
= FmtMonoDup
;
536 else if(buffer
->mChannels
== FmtStereo
&& source
->mStereoMode
== SourceStereo::Enhanced
)
537 voice
->mFmtChannels
= FmtSuperStereo
;
539 voice
->mFmtChannels
= buffer
->mChannels
;
540 voice
->mFmtType
= buffer
->mType
;
541 voice
->mFrameStep
= buffer
->channelsFromFmt();
542 voice
->mBytesPerBlock
= buffer
->blockSizeFromFmt();
543 voice
->mSamplesPerBlock
= buffer
->mBlockAlign
;
544 voice
->mAmbiLayout
= IsUHJ(voice
->mFmtChannels
) ? AmbiLayout::FuMa
: buffer
->mAmbiLayout
;
545 voice
->mAmbiScaling
= IsUHJ(voice
->mFmtChannels
) ? AmbiScaling::UHJ
: buffer
->mAmbiScaling
;
546 voice
->mAmbiOrder
= (voice
->mFmtChannels
== FmtSuperStereo
) ? 1 : buffer
->mAmbiOrder
;
548 if(buffer
->mCallback
) voice
->mFlags
.set(VoiceIsCallback
);
549 else if(source
->SourceType
== AL_STATIC
) voice
->mFlags
.set(VoiceIsStatic
);
550 voice
->mNumCallbackBlocks
= 0;
551 voice
->mCallbackBlockBase
= 0;
553 voice
->prepare(device
);
555 source
->mPropsDirty
= false;
556 UpdateSourceProps(source
, voice
, context
);
558 voice
->mSourceID
.store(source
->id
, std::memory_order_release
);
562 VoiceChange
*GetVoiceChanger(ALCcontext
*ctx
)
564 VoiceChange
*vchg
{ctx
->mVoiceChangeTail
};
565 if(vchg
== ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)) UNLIKELY
567 ctx
->allocVoiceChanges();
568 vchg
= ctx
->mVoiceChangeTail
;
571 ctx
->mVoiceChangeTail
= vchg
->mNext
.exchange(nullptr, std::memory_order_relaxed
);
576 void SendVoiceChanges(ALCcontext
*ctx
, VoiceChange
*tail
)
578 ALCdevice
*device
{ctx
->mALDevice
.get()};
580 VoiceChange
*oldhead
{ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)};
581 while(VoiceChange
*next
{oldhead
->mNext
.load(std::memory_order_relaxed
)})
583 oldhead
->mNext
.store(tail
, std::memory_order_release
);
585 const bool connected
{device
->Connected
.load(std::memory_order_acquire
)};
586 std::ignore
= device
->waitForMix();
587 if(!connected
) UNLIKELY
589 if(ctx
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
591 /* If the device is disconnected and voices are stopped, just
592 * ignore all pending changes.
594 VoiceChange
*cur
{ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)};
595 while(VoiceChange
*next
{cur
->mNext
.load(std::memory_order_acquire
)})
598 if(Voice
*voice
{cur
->mVoice
})
599 voice
->mSourceID
.store(0, std::memory_order_relaxed
);
601 ctx
->mCurrentVoiceChange
.store(cur
, std::memory_order_release
);
607 bool SetVoiceOffset(Voice
*oldvoice
, const VoicePos
&vpos
, ALsource
*source
, ALCcontext
*context
,
610 /* First, get a free voice to start at the new offset. */
611 auto voicelist
= context
->getVoicesSpan();
614 for(Voice
*voice
: voicelist
)
616 if(voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
617 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
618 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false)
625 if(!newvoice
) UNLIKELY
627 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
628 if(allvoices
.size() == voicelist
.size())
629 context
->allocVoices(1);
630 context
->mActiveVoiceCount
.fetch_add(1, std::memory_order_release
);
631 voicelist
= context
->getVoicesSpan();
634 for(Voice
*voice
: voicelist
)
636 if(voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
637 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
638 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false)
645 ASSUME(newvoice
!= nullptr);
648 /* Initialize the new voice and set its starting offset.
649 * TODO: It might be better to have the VoiceChange processing copy the old
650 * voice's mixing parameters (and pending update) insead of initializing it
651 * all here. This would just need to set the minimum properties to link the
652 * voice to the source and its position-dependent properties (including the
655 newvoice
->mPlayState
.store(Voice::Pending
, std::memory_order_relaxed
);
656 newvoice
->mPosition
.store(vpos
.pos
, std::memory_order_relaxed
);
657 newvoice
->mPositionFrac
.store(vpos
.frac
, std::memory_order_relaxed
);
658 newvoice
->mCurrentBuffer
.store(vpos
.bufferitem
, std::memory_order_relaxed
);
659 newvoice
->mStartTime
= oldvoice
->mStartTime
;
660 newvoice
->mFlags
.reset();
661 if(vpos
.pos
> 0 || (vpos
.pos
== 0 && vpos
.frac
> 0)
662 || vpos
.bufferitem
!= &source
->mQueue
.front())
663 newvoice
->mFlags
.set(VoiceIsFading
);
664 InitVoice(newvoice
, source
, vpos
.bufferitem
, context
, device
);
665 source
->VoiceIdx
= vidx
;
667 /* Set the old voice as having a pending change, and send it off with the
668 * new one with a new offset voice change.
670 oldvoice
->mPendingChange
.store(true, std::memory_order_relaxed
);
672 VoiceChange
*vchg
{GetVoiceChanger(context
)};
673 vchg
->mOldVoice
= oldvoice
;
674 vchg
->mVoice
= newvoice
;
675 vchg
->mSourceID
= source
->id
;
676 vchg
->mState
= VChangeState::Restart
;
677 SendVoiceChanges(context
, vchg
);
679 /* If the old voice still has a sourceID, it's still active and the change-
680 * over will work on the next update.
682 if(oldvoice
->mSourceID
.load(std::memory_order_acquire
) != 0u) LIKELY
685 /* Otherwise, if the new voice's state is not pending, the change-over
688 if(newvoice
->mPlayState
.load(std::memory_order_acquire
) != Voice::Pending
)
691 /* Otherwise, wait for any current mix to finish and check one last time. */
692 std::ignore
= device
->waitForMix();
693 if(newvoice
->mPlayState
.load(std::memory_order_acquire
) != Voice::Pending
)
695 /* The change-over failed because the old voice stopped before the new
696 * voice could start at the new offset. Let go of the new voice and have
697 * the caller store the source offset since it's stopped.
699 newvoice
->mCurrentBuffer
.store(nullptr, std::memory_order_relaxed
);
700 newvoice
->mLoopBuffer
.store(nullptr, std::memory_order_relaxed
);
701 newvoice
->mSourceID
.store(0u, std::memory_order_relaxed
);
702 newvoice
->mPlayState
.store(Voice::Stopped
, std::memory_order_relaxed
);
708 * Returns if the last known state for the source was playing or paused. Does
709 * not sync with the mixer voice.
711 inline bool IsPlayingOrPaused(ALsource
*source
)
712 { return source
->state
== AL_PLAYING
|| source
->state
== AL_PAUSED
; }
715 * Returns an updated source state using the matching voice's status (or lack
718 inline ALenum
GetSourceState(ALsource
*source
, Voice
*voice
)
720 if(!voice
&& source
->state
== AL_PLAYING
)
721 source
->state
= AL_STOPPED
;
722 return source
->state
;
726 bool EnsureSources(ALCcontext
*context
, size_t needed
)
728 size_t count
{std::accumulate(context
->mSourceList
.cbegin(), context
->mSourceList
.cend(), 0_uz
,
729 [](size_t cur
, const SourceSubList
&sublist
) noexcept
-> size_t
730 { return cur
+ static_cast<ALuint
>(al::popcount(sublist
.FreeMask
)); })};
733 while(needed
> count
)
735 if(context
->mSourceList
.size() >= 1<<25) UNLIKELY
738 SourceSubList sublist
{};
739 sublist
.FreeMask
= ~0_u64
;
740 sublist
.Sources
= SubListAllocator
{}.allocate(1);
741 context
->mSourceList
.emplace_back(std::move(sublist
));
742 count
+= std::tuple_size_v
<SubListAllocator::value_type
>;
751 ALsource
*AllocSource(ALCcontext
*context
) noexcept
753 auto sublist
= std::find_if(context
->mSourceList
.begin(), context
->mSourceList
.end(),
754 [](const SourceSubList
&entry
) noexcept
-> bool
755 { return entry
.FreeMask
!= 0; });
756 auto lidx
= static_cast<ALuint
>(std::distance(context
->mSourceList
.begin(), sublist
));
757 auto slidx
= static_cast<ALuint
>(al::countr_zero(sublist
->FreeMask
));
760 ALsource
*source
{al::construct_at(al::to_address(sublist
->Sources
->begin() + slidx
))};
762 source
->eaxInitialize(context
);
765 /* Add 1 to avoid source ID 0. */
766 source
->id
= ((lidx
<<6) | slidx
) + 1;
768 context
->mNumSources
+= 1;
769 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
774 void FreeSource(ALCcontext
*context
, ALsource
*source
)
776 context
->mSourceNames
.erase(source
->id
);
778 const ALuint id
{source
->id
- 1};
779 const size_t lidx
{id
>> 6};
780 const ALuint slidx
{id
& 0x3f};
782 if(Voice
*voice
{GetSourceVoice(source
, context
)})
784 VoiceChange
*vchg
{GetVoiceChanger(context
)};
786 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
787 vchg
->mVoice
= voice
;
788 vchg
->mSourceID
= source
->id
;
789 vchg
->mState
= VChangeState::Stop
;
791 SendVoiceChanges(context
, vchg
);
794 std::destroy_at(source
);
796 context
->mSourceList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
797 context
->mNumSources
--;
801 inline ALsource
*LookupSource(ALCcontext
*context
, ALuint id
) noexcept
803 const size_t lidx
{(id
-1) >> 6};
804 const ALuint slidx
{(id
-1) & 0x3f};
806 if(lidx
>= context
->mSourceList
.size()) UNLIKELY
808 SourceSubList
&sublist
{context
->mSourceList
[lidx
]};
809 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
811 return al::to_address(sublist
.Sources
->begin() + slidx
);
814 auto LookupBuffer
= [](ALCdevice
*device
, auto id
) noexcept
-> ALbuffer
*
816 const auto lidx
{(id
-1) >> 6};
817 const auto slidx
{(id
-1) & 0x3f};
819 if(lidx
>= device
->BufferList
.size()) UNLIKELY
821 BufferSubList
&sublist
= device
->BufferList
[static_cast<size_t>(lidx
)];
822 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
824 return al::to_address(sublist
.Buffers
->begin() + static_cast<size_t>(slidx
));
827 auto LookupFilter
= [](ALCdevice
*device
, auto id
) noexcept
-> ALfilter
*
829 const auto lidx
{(id
-1) >> 6};
830 const auto slidx
{(id
-1) & 0x3f};
832 if(lidx
>= device
->FilterList
.size()) UNLIKELY
834 FilterSubList
&sublist
= device
->FilterList
[static_cast<size_t>(lidx
)];
835 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
837 return al::to_address(sublist
.Filters
->begin() + static_cast<size_t>(slidx
));
840 auto LookupEffectSlot
= [](ALCcontext
*context
, auto id
) noexcept
-> ALeffectslot
*
842 const auto lidx
{(id
-1) >> 6};
843 const auto slidx
{(id
-1) & 0x3f};
845 if(lidx
>= context
->mEffectSlotList
.size()) UNLIKELY
847 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[static_cast<size_t>(lidx
)]};
848 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
850 return al::to_address(sublist
.EffectSlots
->begin() + static_cast<size_t>(slidx
));
854 auto StereoModeFromEnum
= [](auto mode
) noexcept
-> std::optional
<SourceStereo
>
858 case AL_NORMAL_SOFT
: return SourceStereo::Normal
;
859 case AL_SUPER_STEREO_SOFT
: return SourceStereo::Enhanced
;
863 ALenum
EnumFromStereoMode(SourceStereo mode
)
867 case SourceStereo::Normal
: return AL_NORMAL_SOFT
;
868 case SourceStereo::Enhanced
: return AL_SUPER_STEREO_SOFT
;
870 throw std::runtime_error
{"Invalid SourceStereo: "+std::to_string(int(mode
))};
873 auto SpatializeModeFromEnum
= [](auto mode
) noexcept
-> std::optional
<SpatializeMode
>
877 case AL_FALSE
: return SpatializeMode::Off
;
878 case AL_TRUE
: return SpatializeMode::On
;
879 case AL_AUTO_SOFT
: return SpatializeMode::Auto
;
883 ALenum
EnumFromSpatializeMode(SpatializeMode mode
)
887 case SpatializeMode::Off
: return AL_FALSE
;
888 case SpatializeMode::On
: return AL_TRUE
;
889 case SpatializeMode::Auto
: return AL_AUTO_SOFT
;
891 throw std::runtime_error
{"Invalid SpatializeMode: "+std::to_string(int(mode
))};
894 auto DirectModeFromEnum
= [](auto mode
) noexcept
-> std::optional
<DirectMode
>
898 case AL_FALSE
: return DirectMode::Off
;
899 case AL_DROP_UNMATCHED_SOFT
: return DirectMode::DropMismatch
;
900 case AL_REMIX_UNMATCHED_SOFT
: return DirectMode::RemixMismatch
;
904 ALenum
EnumFromDirectMode(DirectMode mode
)
908 case DirectMode::Off
: return AL_FALSE
;
909 case DirectMode::DropMismatch
: return AL_DROP_UNMATCHED_SOFT
;
910 case DirectMode::RemixMismatch
: return AL_REMIX_UNMATCHED_SOFT
;
912 throw std::runtime_error
{"Invalid DirectMode: "+std::to_string(int(mode
))};
915 auto DistanceModelFromALenum
= [](auto model
) noexcept
-> std::optional
<DistanceModel
>
919 case AL_NONE
: return DistanceModel::Disable
;
920 case AL_INVERSE_DISTANCE
: return DistanceModel::Inverse
;
921 case AL_INVERSE_DISTANCE_CLAMPED
: return DistanceModel::InverseClamped
;
922 case AL_LINEAR_DISTANCE
: return DistanceModel::Linear
;
923 case AL_LINEAR_DISTANCE_CLAMPED
: return DistanceModel::LinearClamped
;
924 case AL_EXPONENT_DISTANCE
: return DistanceModel::Exponent
;
925 case AL_EXPONENT_DISTANCE_CLAMPED
: return DistanceModel::ExponentClamped
;
929 ALenum
ALenumFromDistanceModel(DistanceModel model
)
933 case DistanceModel::Disable
: return AL_NONE
;
934 case DistanceModel::Inverse
: return AL_INVERSE_DISTANCE
;
935 case DistanceModel::InverseClamped
: return AL_INVERSE_DISTANCE_CLAMPED
;
936 case DistanceModel::Linear
: return AL_LINEAR_DISTANCE
;
937 case DistanceModel::LinearClamped
: return AL_LINEAR_DISTANCE_CLAMPED
;
938 case DistanceModel::Exponent
: return AL_EXPONENT_DISTANCE
;
939 case DistanceModel::ExponentClamped
: return AL_EXPONENT_DISTANCE_CLAMPED
;
941 throw std::runtime_error
{"Unexpected distance model "+std::to_string(static_cast<int>(model
))};
944 enum SourceProp
: ALenum
{
947 srcMinGain
= AL_MIN_GAIN
,
948 srcMaxGain
= AL_MAX_GAIN
,
949 srcMaxDistance
= AL_MAX_DISTANCE
,
950 srcRolloffFactor
= AL_ROLLOFF_FACTOR
,
951 srcDopplerFactor
= AL_DOPPLER_FACTOR
,
952 srcConeOuterGain
= AL_CONE_OUTER_GAIN
,
953 srcSecOffset
= AL_SEC_OFFSET
,
954 srcSampleOffset
= AL_SAMPLE_OFFSET
,
955 srcByteOffset
= AL_BYTE_OFFSET
,
956 srcConeInnerAngle
= AL_CONE_INNER_ANGLE
,
957 srcConeOuterAngle
= AL_CONE_OUTER_ANGLE
,
958 srcRefDistance
= AL_REFERENCE_DISTANCE
,
960 srcPosition
= AL_POSITION
,
961 srcVelocity
= AL_VELOCITY
,
962 srcDirection
= AL_DIRECTION
,
964 srcSourceRelative
= AL_SOURCE_RELATIVE
,
965 srcLooping
= AL_LOOPING
,
966 srcBuffer
= AL_BUFFER
,
967 srcSourceState
= AL_SOURCE_STATE
,
968 srcBuffersQueued
= AL_BUFFERS_QUEUED
,
969 srcBuffersProcessed
= AL_BUFFERS_PROCESSED
,
970 srcSourceType
= AL_SOURCE_TYPE
,
973 srcConeOuterGainHF
= AL_CONE_OUTER_GAINHF
,
974 srcAirAbsorptionFactor
= AL_AIR_ABSORPTION_FACTOR
,
975 srcRoomRolloffFactor
= AL_ROOM_ROLLOFF_FACTOR
,
976 srcDirectFilterGainHFAuto
= AL_DIRECT_FILTER_GAINHF_AUTO
,
977 srcAuxSendFilterGainAuto
= AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
,
978 srcAuxSendFilterGainHFAuto
= AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
,
979 srcDirectFilter
= AL_DIRECT_FILTER
,
980 srcAuxSendFilter
= AL_AUXILIARY_SEND_FILTER
,
982 /* AL_SOFT_direct_channels */
983 srcDirectChannelsSOFT
= AL_DIRECT_CHANNELS_SOFT
,
985 /* AL_EXT_source_distance_model */
986 srcDistanceModel
= AL_DISTANCE_MODEL
,
988 /* AL_SOFT_source_latency */
989 srcSampleOffsetLatencySOFT
= AL_SAMPLE_OFFSET_LATENCY_SOFT
,
990 srcSecOffsetLatencySOFT
= AL_SEC_OFFSET_LATENCY_SOFT
,
992 /* AL_EXT_STEREO_ANGLES */
993 srcAngles
= AL_STEREO_ANGLES
,
995 /* AL_EXT_SOURCE_RADIUS */
996 srcRadius
= AL_SOURCE_RADIUS
,
999 srcOrientation
= AL_ORIENTATION
,
1001 /* AL_SOFT_source_length */
1002 srcByteLength
= AL_BYTE_LENGTH_SOFT
,
1003 srcSampleLength
= AL_SAMPLE_LENGTH_SOFT
,
1004 srcSecLength
= AL_SEC_LENGTH_SOFT
,
1006 /* AL_SOFT_source_resampler */
1007 srcResampler
= AL_SOURCE_RESAMPLER_SOFT
,
1009 /* AL_SOFT_source_spatialize */
1010 srcSpatialize
= AL_SOURCE_SPATIALIZE_SOFT
,
1012 /* ALC_SOFT_device_clock */
1013 srcSampleOffsetClockSOFT
= AL_SAMPLE_OFFSET_CLOCK_SOFT
,
1014 srcSecOffsetClockSOFT
= AL_SEC_OFFSET_CLOCK_SOFT
,
1017 srcStereoMode
= AL_STEREO_MODE_SOFT
,
1018 srcSuperStereoWidth
= AL_SUPER_STEREO_WIDTH_SOFT
,
1020 /* AL_SOFT_buffer_sub_data */
1021 srcByteRWOffsetsSOFT
= AL_BYTE_RW_OFFSETS_SOFT
,
1022 srcSampleRWOffsetsSOFT
= AL_SAMPLE_RW_OFFSETS_SOFT
,
1024 /* AL_SOFT_source_panning */
1025 srcPanningEnabledSOFT
= AL_PANNING_ENABLED_SOFT
,
1026 srcPanSOFT
= AL_PAN_SOFT
,
1030 constexpr ALuint
IntValsByProp(ALenum prop
)
1032 switch(static_cast<SourceProp
>(prop
))
1034 case AL_SOURCE_STATE
:
1035 case AL_SOURCE_TYPE
:
1036 case AL_BUFFERS_QUEUED
:
1037 case AL_BUFFERS_PROCESSED
:
1038 case AL_BYTE_LENGTH_SOFT
:
1039 case AL_SAMPLE_LENGTH_SOFT
:
1040 case AL_SOURCE_RELATIVE
:
1043 case AL_SAMPLE_OFFSET
:
1044 case AL_BYTE_OFFSET
:
1045 case AL_DIRECT_FILTER
:
1046 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1047 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1048 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1049 case AL_DIRECT_CHANNELS_SOFT
:
1050 case AL_DISTANCE_MODEL
:
1051 case AL_SOURCE_RESAMPLER_SOFT
:
1052 case AL_SOURCE_SPATIALIZE_SOFT
:
1053 case AL_STEREO_MODE_SOFT
:
1054 case AL_PANNING_ENABLED_SOFT
:
1058 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1059 if(sBufferSubDataCompat
)
1062 case AL_CONE_INNER_ANGLE
:
1063 case AL_CONE_OUTER_ANGLE
:
1068 case AL_REFERENCE_DISTANCE
:
1069 case AL_ROLLOFF_FACTOR
:
1070 case AL_CONE_OUTER_GAIN
:
1071 case AL_MAX_DISTANCE
:
1073 case AL_DOPPLER_FACTOR
:
1074 case AL_CONE_OUTER_GAINHF
:
1075 case AL_AIR_ABSORPTION_FACTOR
:
1076 case AL_ROOM_ROLLOFF_FACTOR
:
1077 case AL_SEC_LENGTH_SOFT
:
1078 case AL_SUPER_STEREO_WIDTH_SOFT
:
1079 return 1; /* 1x float */
1081 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1082 if(sBufferSubDataCompat
)
1086 case AL_AUXILIARY_SEND_FILTER
:
1092 return 3; /* 3x float */
1094 case AL_ORIENTATION
:
1095 return 6; /* 6x float */
1097 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1098 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1099 case AL_STEREO_ANGLES
:
1100 break; /* i64 only */
1101 case AL_SEC_OFFSET_LATENCY_SOFT
:
1102 case AL_SEC_OFFSET_CLOCK_SOFT
:
1103 break; /* double only */
1109 constexpr ALuint
Int64ValsByProp(ALenum prop
)
1111 switch(static_cast<SourceProp
>(prop
))
1113 case AL_SOURCE_STATE
:
1114 case AL_SOURCE_TYPE
:
1115 case AL_BUFFERS_QUEUED
:
1116 case AL_BUFFERS_PROCESSED
:
1117 case AL_BYTE_LENGTH_SOFT
:
1118 case AL_SAMPLE_LENGTH_SOFT
:
1119 case AL_SOURCE_RELATIVE
:
1122 case AL_SAMPLE_OFFSET
:
1123 case AL_BYTE_OFFSET
:
1124 case AL_DIRECT_FILTER
:
1125 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1126 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1127 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1128 case AL_DIRECT_CHANNELS_SOFT
:
1129 case AL_DISTANCE_MODEL
:
1130 case AL_SOURCE_RESAMPLER_SOFT
:
1131 case AL_SOURCE_SPATIALIZE_SOFT
:
1132 case AL_STEREO_MODE_SOFT
:
1133 case AL_PANNING_ENABLED_SOFT
:
1137 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1138 if(sBufferSubDataCompat
)
1141 case AL_CONE_INNER_ANGLE
:
1142 case AL_CONE_OUTER_ANGLE
:
1147 case AL_REFERENCE_DISTANCE
:
1148 case AL_ROLLOFF_FACTOR
:
1149 case AL_CONE_OUTER_GAIN
:
1150 case AL_MAX_DISTANCE
:
1152 case AL_DOPPLER_FACTOR
:
1153 case AL_CONE_OUTER_GAINHF
:
1154 case AL_AIR_ABSORPTION_FACTOR
:
1155 case AL_ROOM_ROLLOFF_FACTOR
:
1156 case AL_SEC_LENGTH_SOFT
:
1157 case AL_SUPER_STEREO_WIDTH_SOFT
:
1158 return 1; /* 1x float */
1160 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1161 if(sBufferSubDataCompat
)
1165 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1166 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1167 case AL_STEREO_ANGLES
:
1170 case AL_AUXILIARY_SEND_FILTER
:
1176 return 3; /* 3x float */
1178 case AL_ORIENTATION
:
1179 return 6; /* 6x float */
1181 case AL_SEC_OFFSET_LATENCY_SOFT
:
1182 case AL_SEC_OFFSET_CLOCK_SOFT
:
1183 break; /* double only */
1189 constexpr ALuint
FloatValsByProp(ALenum prop
)
1191 switch(static_cast<SourceProp
>(prop
))
1197 case AL_MAX_DISTANCE
:
1198 case AL_ROLLOFF_FACTOR
:
1199 case AL_DOPPLER_FACTOR
:
1200 case AL_CONE_OUTER_GAIN
:
1202 case AL_SAMPLE_OFFSET
:
1203 case AL_BYTE_OFFSET
:
1204 case AL_CONE_INNER_ANGLE
:
1205 case AL_CONE_OUTER_ANGLE
:
1206 case AL_REFERENCE_DISTANCE
:
1207 case AL_CONE_OUTER_GAINHF
:
1208 case AL_AIR_ABSORPTION_FACTOR
:
1209 case AL_ROOM_ROLLOFF_FACTOR
:
1210 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1211 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1212 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1213 case AL_DIRECT_CHANNELS_SOFT
:
1214 case AL_DISTANCE_MODEL
:
1215 case AL_SOURCE_RELATIVE
:
1217 case AL_SOURCE_STATE
:
1218 case AL_BUFFERS_QUEUED
:
1219 case AL_BUFFERS_PROCESSED
:
1220 case AL_SOURCE_TYPE
:
1221 case AL_SOURCE_RESAMPLER_SOFT
:
1222 case AL_SOURCE_SPATIALIZE_SOFT
:
1223 case AL_BYTE_LENGTH_SOFT
:
1224 case AL_SAMPLE_LENGTH_SOFT
:
1225 case AL_SEC_LENGTH_SOFT
:
1226 case AL_STEREO_MODE_SOFT
:
1227 case AL_SUPER_STEREO_WIDTH_SOFT
:
1228 case AL_PANNING_ENABLED_SOFT
:
1232 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1233 if(!sBufferSubDataCompat
)
1236 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1239 case AL_STEREO_ANGLES
:
1247 case AL_ORIENTATION
:
1250 case AL_SEC_OFFSET_LATENCY_SOFT
:
1251 case AL_SEC_OFFSET_CLOCK_SOFT
:
1252 break; /* Double only */
1255 case AL_DIRECT_FILTER
:
1256 case AL_AUXILIARY_SEND_FILTER
:
1257 break; /* i/i64 only */
1258 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1259 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1260 break; /* i64 only */
1264 constexpr ALuint
DoubleValsByProp(ALenum prop
)
1266 switch(static_cast<SourceProp
>(prop
))
1272 case AL_MAX_DISTANCE
:
1273 case AL_ROLLOFF_FACTOR
:
1274 case AL_DOPPLER_FACTOR
:
1275 case AL_CONE_OUTER_GAIN
:
1277 case AL_SAMPLE_OFFSET
:
1278 case AL_BYTE_OFFSET
:
1279 case AL_CONE_INNER_ANGLE
:
1280 case AL_CONE_OUTER_ANGLE
:
1281 case AL_REFERENCE_DISTANCE
:
1282 case AL_CONE_OUTER_GAINHF
:
1283 case AL_AIR_ABSORPTION_FACTOR
:
1284 case AL_ROOM_ROLLOFF_FACTOR
:
1285 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1286 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1287 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1288 case AL_DIRECT_CHANNELS_SOFT
:
1289 case AL_DISTANCE_MODEL
:
1290 case AL_SOURCE_RELATIVE
:
1292 case AL_SOURCE_STATE
:
1293 case AL_BUFFERS_QUEUED
:
1294 case AL_BUFFERS_PROCESSED
:
1295 case AL_SOURCE_TYPE
:
1296 case AL_SOURCE_RESAMPLER_SOFT
:
1297 case AL_SOURCE_SPATIALIZE_SOFT
:
1298 case AL_BYTE_LENGTH_SOFT
:
1299 case AL_SAMPLE_LENGTH_SOFT
:
1300 case AL_SEC_LENGTH_SOFT
:
1301 case AL_STEREO_MODE_SOFT
:
1302 case AL_SUPER_STEREO_WIDTH_SOFT
:
1303 case AL_PANNING_ENABLED_SOFT
:
1307 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1308 if(!sBufferSubDataCompat
)
1311 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1314 case AL_SEC_OFFSET_LATENCY_SOFT
:
1315 case AL_SEC_OFFSET_CLOCK_SOFT
:
1316 case AL_STEREO_ANGLES
:
1324 case AL_ORIENTATION
:
1328 case AL_DIRECT_FILTER
:
1329 case AL_AUXILIARY_SEND_FILTER
:
1330 break; /* i/i64 only */
1331 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1332 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1333 break; /* i64 only */
1339 void UpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1341 if(!context
->mDeferUpdates
)
1343 if(Voice
*voice
{GetSourceVoice(source
, context
)})
1345 UpdateSourceProps(source
, voice
, context
);
1349 source
->mPropsDirty
= true;
1352 void CommitAndUpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1354 if(!context
->mDeferUpdates
)
1356 if(context
->hasEax())
1357 source
->eaxCommit();
1358 if(Voice
*voice
{GetSourceVoice(source
, context
)})
1360 UpdateSourceProps(source
, voice
, context
);
1364 source
->mPropsDirty
= true;
1369 inline void CommitAndUpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1370 { UpdateSourceProps(source
, context
); }
1374 template<typename T
>
1375 struct PropType
{ };
1377 struct PropType
<ALint
> { static const char *Name() { return "integer"; } };
1379 struct PropType
<ALint64SOFT
> { static const char *Name() { return "int64"; } };
1381 struct PropType
<ALfloat
> { static const char *Name() { return "float"; } };
1383 struct PropType
<ALdouble
> { static const char *Name() { return "double"; } };
1386 std::array
<char,32> mStr
{};
1388 template<typename T
>
1391 using ST
= std::make_signed_t
<std::remove_cv_t
<T
>>;
1392 if constexpr(std::is_same_v
<ST
,int>)
1393 std::snprintf(mStr
.data(), mStr
.size(), "0x%x", value
);
1394 else if constexpr(std::is_same_v
<ST
,long>)
1395 std::snprintf(mStr
.data(), mStr
.size(), "0x%lx", value
);
1396 else if constexpr(std::is_same_v
<ST
,long long>)
1397 std::snprintf(mStr
.data(), mStr
.size(), "0x%llx", value
);
1400 [[nodiscard
]] auto c_str() const noexcept
-> const char* { return mStr
.data(); }
1405 * Returns a pair of lambdas to check the following setter.
1407 * The first lambda checks the size of the span is valid for the required size,
1408 * setting the proper context error and throwing a check_size_exception if it
1411 * The second lambda tests the validity of the value check, setting the proper
1412 * context error and throwing a check_value_exception if it failed.
1414 template<typename T
, size_t N
>
1415 auto GetCheckers(const SourceProp prop
, const al::span
<T
,N
> values
)
1417 return std::make_pair(
1418 [=](size_t expect
) -> void
1420 if(values
.size() == expect
) return;
1421 throw al::context_error
{AL_INVALID_ENUM
,
1422 "Property 0x%04x expects %zu value(s), got %zu", prop
, expect
, values
.size()};
1424 [](bool passed
) -> void
1427 throw al::context_error
{AL_INVALID_VALUE
, "Value out of range"};
1432 template<typename T
>
1433 NOINLINE
void SetProperty(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
1434 const al::span
<const T
> values
)
1436 auto [CheckSize
, CheckValue
] = GetCheckers(prop
, values
);
1437 ALCdevice
*device
{Context
->mALDevice
.get()};
1441 case AL_SOURCE_STATE
:
1442 case AL_SOURCE_TYPE
:
1443 case AL_BUFFERS_QUEUED
:
1444 case AL_BUFFERS_PROCESSED
:
1445 if constexpr(std::is_integral_v
<T
>)
1448 throw al::context_error
{AL_INVALID_OPERATION
,
1449 "Setting read-only source property 0x%04x", prop
};
1453 case AL_BYTE_LENGTH_SOFT
:
1454 case AL_SAMPLE_LENGTH_SOFT
:
1455 case AL_SEC_LENGTH_SOFT
:
1456 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1457 case AL_SEC_OFFSET_LATENCY_SOFT
:
1458 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1459 case AL_SEC_OFFSET_CLOCK_SOFT
:
1461 throw al::context_error
{AL_INVALID_OPERATION
, "Setting read-only source property 0x%04x",
1466 CheckValue(values
[0] >= T
{0});
1468 Source
->Pitch
= static_cast<float>(values
[0]);
1469 return UpdateSourceProps(Source
, Context
);
1471 case AL_CONE_INNER_ANGLE
:
1473 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{360});
1475 Source
->InnerAngle
= static_cast<float>(values
[0]);
1476 return CommitAndUpdateSourceProps(Source
, Context
);
1478 case AL_CONE_OUTER_ANGLE
:
1480 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{360});
1482 Source
->OuterAngle
= static_cast<float>(values
[0]);
1483 return CommitAndUpdateSourceProps(Source
, Context
);
1487 CheckValue(values
[0] >= T
{0});
1489 Source
->Gain
= static_cast<float>(values
[0]);
1490 return UpdateSourceProps(Source
, Context
);
1492 case AL_MAX_DISTANCE
:
1494 CheckValue(values
[0] >= T
{0});
1496 Source
->MaxDistance
= static_cast<float>(values
[0]);
1497 return CommitAndUpdateSourceProps(Source
, Context
);
1499 case AL_ROLLOFF_FACTOR
:
1501 CheckValue(values
[0] >= T
{0});
1503 Source
->RolloffFactor
= static_cast<float>(values
[0]);
1504 return CommitAndUpdateSourceProps(Source
, Context
);
1506 case AL_REFERENCE_DISTANCE
:
1508 CheckValue(values
[0] >= T
{0});
1510 Source
->RefDistance
= static_cast<float>(values
[0]);
1511 return CommitAndUpdateSourceProps(Source
, Context
);
1515 CheckValue(values
[0] >= T
{0});
1517 Source
->MinGain
= static_cast<float>(values
[0]);
1518 return UpdateSourceProps(Source
, Context
);
1522 CheckValue(values
[0] >= T
{0});
1524 Source
->MaxGain
= static_cast<float>(values
[0]);
1525 return UpdateSourceProps(Source
, Context
);
1527 case AL_CONE_OUTER_GAIN
:
1529 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1531 Source
->OuterGain
= static_cast<float>(values
[0]);
1532 return UpdateSourceProps(Source
, Context
);
1534 case AL_CONE_OUTER_GAINHF
:
1536 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1538 Source
->OuterGainHF
= static_cast<float>(values
[0]);
1539 return UpdateSourceProps(Source
, Context
);
1541 case AL_AIR_ABSORPTION_FACTOR
:
1543 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{10});
1545 Source
->AirAbsorptionFactor
= static_cast<float>(values
[0]);
1546 return UpdateSourceProps(Source
, Context
);
1548 case AL_ROOM_ROLLOFF_FACTOR
:
1550 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1552 Source
->RoomRolloffFactor
= static_cast<float>(values
[0]);
1553 return UpdateSourceProps(Source
, Context
);
1555 case AL_DOPPLER_FACTOR
:
1557 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1559 Source
->DopplerFactor
= static_cast<float>(values
[0]);
1560 return UpdateSourceProps(Source
, Context
);
1563 case AL_SOURCE_RELATIVE
:
1564 if constexpr(std::is_integral_v
<T
>)
1567 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1569 Source
->HeadRelative
= values
[0] != AL_FALSE
;
1570 return CommitAndUpdateSourceProps(Source
, Context
);
1575 if constexpr(std::is_integral_v
<T
>)
1578 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1580 Source
->Looping
= values
[0] != AL_FALSE
;
1581 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1584 voice
->mLoopBuffer
.store(&Source
->mQueue
.front(), std::memory_order_release
);
1586 voice
->mLoopBuffer
.store(nullptr, std::memory_order_release
);
1588 /* If the source is playing, wait for the current mix to finish
1589 * to ensure it isn't currently looping back or reaching the
1592 std::ignore
= device
->waitForMix();
1599 if constexpr(std::is_integral_v
<T
>)
1602 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1603 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1604 throw al::context_error
{AL_INVALID_OPERATION
,
1605 "Setting buffer on playing or paused source %u", Source
->id
};
1607 std::deque
<ALbufferQueueItem
> oldlist
;
1610 using UT
= std::make_unsigned_t
<T
>;
1611 std::lock_guard
<std::mutex
> buflock
{device
->BufferLock
};
1612 ALbuffer
*buffer
{LookupBuffer(device
, static_cast<UT
>(values
[0]))};
1614 throw al::context_error
{AL_INVALID_VALUE
, "Invalid buffer ID %s",
1615 std::to_string(values
[0]).c_str()};
1616 if(buffer
->MappedAccess
&& !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
1617 throw al::context_error
{AL_INVALID_OPERATION
,
1618 "Setting non-persistently mapped buffer %u", buffer
->id
};
1619 if(buffer
->mCallback
&& buffer
->ref
.load(std::memory_order_relaxed
) != 0)
1620 throw al::context_error
{AL_INVALID_OPERATION
,
1621 "Setting already-set callback buffer %u", buffer
->id
};
1623 /* Add the selected buffer to a one-item queue */
1624 std::deque
<ALbufferQueueItem
> newlist
;
1625 newlist
.emplace_back();
1626 newlist
.back().mCallback
= buffer
->mCallback
;
1627 newlist
.back().mUserData
= buffer
->mUserData
;
1628 newlist
.back().mBlockAlign
= buffer
->mBlockAlign
;
1629 newlist
.back().mSampleLen
= buffer
->mSampleLen
;
1630 newlist
.back().mLoopStart
= buffer
->mLoopStart
;
1631 newlist
.back().mLoopEnd
= buffer
->mLoopEnd
;
1632 newlist
.back().mSamples
= buffer
->mData
;
1633 newlist
.back().mBuffer
= buffer
;
1634 IncrementRef(buffer
->ref
);
1636 /* Source is now Static */
1637 Source
->SourceType
= AL_STATIC
;
1638 Source
->mQueue
.swap(oldlist
);
1639 Source
->mQueue
.swap(newlist
);
1643 /* Source is now Undetermined */
1644 Source
->SourceType
= AL_UNDETERMINED
;
1645 Source
->mQueue
.swap(oldlist
);
1648 /* Delete all elements in the previous queue */
1649 for(auto &item
: oldlist
)
1651 if(ALbuffer
*buffer
{item
.mBuffer
})
1652 DecrementRef(buffer
->ref
);
1660 case AL_SAMPLE_OFFSET
:
1661 case AL_BYTE_OFFSET
:
1663 if constexpr(std::is_floating_point_v
<T
>)
1664 CheckValue(std::isfinite(values
[0]));
1666 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1668 auto vpos
= GetSampleOffset(Source
->mQueue
, prop
, static_cast<double>(values
[0]));
1669 if(!vpos
) throw al::context_error
{AL_INVALID_VALUE
, "Invalid offset"};
1671 if(SetVoiceOffset(voice
, *vpos
, Source
, Context
, Context
->mALDevice
.get()))
1674 Source
->OffsetType
= prop
;
1675 Source
->Offset
= static_cast<double>(values
[0]);
1678 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1679 if(sBufferSubDataCompat
)
1681 if constexpr(std::is_integral_v
<T
>)
1684 throw al::context_error
{AL_INVALID_OPERATION
,
1685 "Setting read-only source property 0x%04x", prop
};
1690 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1691 if(sBufferSubDataCompat
)
1693 if constexpr(std::is_integral_v
<T
>)
1696 throw al::context_error
{AL_INVALID_OPERATION
,
1697 "Setting read-only source property 0x%04x", prop
};
1702 if constexpr(std::is_floating_point_v
<T
>)
1703 CheckValue(values
[0] >= T
{0} && std::isfinite(static_cast<float>(values
[0])));
1705 CheckValue(values
[0] >= T
{0});
1707 Source
->Radius
= static_cast<float>(values
[0]);
1708 return UpdateSourceProps(Source
, Context
);
1710 case AL_SUPER_STEREO_WIDTH_SOFT
:
1712 CheckValue(values
[0] >= T
{0} && values
[0] <= T
{1});
1714 Source
->EnhWidth
= static_cast<float>(values
[0]);
1715 return UpdateSourceProps(Source
, Context
);
1717 case AL_PANNING_ENABLED_SOFT
:
1719 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1720 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1721 throw al::context_error
{AL_INVALID_OPERATION
,
1722 "Modifying panning enabled on playing or paused source %u", Source
->id
};
1724 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1726 Source
->mPanningEnabled
= values
[0] != AL_FALSE
;
1727 return UpdateSourceProps(Source
, Context
);
1731 CheckValue(values
[0] >= T
{-1} && values
[0] <= T
{1});
1733 Source
->mPan
= static_cast<float>(values
[0]);
1734 return UpdateSourceProps(Source
, Context
);
1736 case AL_STEREO_ANGLES
:
1738 if constexpr(std::is_floating_point_v
<T
>)
1739 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1740 && std::isfinite(static_cast<float>(values
[1])));
1742 Source
->StereoPan
[0] = static_cast<float>(values
[0]);
1743 Source
->StereoPan
[1] = static_cast<float>(values
[1]);
1744 return UpdateSourceProps(Source
, Context
);
1749 if constexpr(std::is_floating_point_v
<T
>)
1750 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1751 && std::isfinite(static_cast<float>(values
[1]))
1752 && std::isfinite(static_cast<float>(values
[2])));
1754 Source
->Position
[0] = static_cast<float>(values
[0]);
1755 Source
->Position
[1] = static_cast<float>(values
[1]);
1756 Source
->Position
[2] = static_cast<float>(values
[2]);
1757 return CommitAndUpdateSourceProps(Source
, Context
);
1761 if constexpr(std::is_floating_point_v
<T
>)
1762 CheckValue(std::isfinite(static_cast<float>(values
[0]))
1763 && std::isfinite(static_cast<float>(values
[1]))
1764 && std::isfinite(static_cast<float>(values
[2])));
1766 Source
->Velocity
[0] = static_cast<float>(values
[0]);
1767 Source
->Velocity
[1] = static_cast<float>(values
[1]);
1768 Source
->Velocity
[2] = static_cast<float>(values
[2]);
1769 return CommitAndUpdateSourceProps(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
->Direction
[0] = static_cast<float>(values
[0]);
1779 Source
->Direction
[1] = static_cast<float>(values
[1]);
1780 Source
->Direction
[2] = static_cast<float>(values
[2]);
1781 return CommitAndUpdateSourceProps(Source
, Context
);
1783 case AL_ORIENTATION
:
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]))
1789 && std::isfinite(static_cast<float>(values
[3]))
1790 && std::isfinite(static_cast<float>(values
[4]))
1791 && std::isfinite(static_cast<float>(values
[5])));
1793 Source
->OrientAt
[0] = static_cast<float>(values
[0]);
1794 Source
->OrientAt
[1] = static_cast<float>(values
[1]);
1795 Source
->OrientAt
[2] = static_cast<float>(values
[2]);
1796 Source
->OrientUp
[0] = static_cast<float>(values
[3]);
1797 Source
->OrientUp
[1] = static_cast<float>(values
[4]);
1798 Source
->OrientUp
[2] = static_cast<float>(values
[5]);
1799 return UpdateSourceProps(Source
, Context
);
1802 case AL_DIRECT_FILTER
:
1803 if constexpr(std::is_integral_v
<T
>)
1806 const auto filterid
= static_cast<std::make_unsigned_t
<T
>>(values
[0]);
1809 std::lock_guard
<std::mutex
> filterlock
{device
->FilterLock
};
1810 ALfilter
*filter
{LookupFilter(device
, filterid
)};
1812 throw al::context_error
{AL_INVALID_VALUE
, "Invalid filter ID %s",
1813 std::to_string(filterid
).c_str()};
1814 Source
->Direct
.Gain
= filter
->Gain
;
1815 Source
->Direct
.GainHF
= filter
->GainHF
;
1816 Source
->Direct
.HFReference
= filter
->HFReference
;
1817 Source
->Direct
.GainLF
= filter
->GainLF
;
1818 Source
->Direct
.LFReference
= filter
->LFReference
;
1822 Source
->Direct
.Gain
= 1.0f
;
1823 Source
->Direct
.GainHF
= 1.0f
;
1824 Source
->Direct
.HFReference
= LowPassFreqRef
;
1825 Source
->Direct
.GainLF
= 1.0f
;
1826 Source
->Direct
.LFReference
= HighPassFreqRef
;
1828 return UpdateSourceProps(Source
, Context
);
1832 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1833 if constexpr(std::is_integral_v
<T
>)
1836 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1838 Source
->DryGainHFAuto
= values
[0] != AL_FALSE
;
1839 return UpdateSourceProps(Source
, Context
);
1843 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1844 if constexpr(std::is_integral_v
<T
>)
1847 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1849 Source
->WetGainAuto
= values
[0] != AL_FALSE
;
1850 return UpdateSourceProps(Source
, Context
);
1854 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1855 if constexpr(std::is_integral_v
<T
>)
1858 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1860 Source
->WetGainHFAuto
= values
[0] != AL_FALSE
;
1861 return UpdateSourceProps(Source
, Context
);
1865 case AL_DIRECT_CHANNELS_SOFT
:
1866 if constexpr(std::is_integral_v
<T
>)
1869 if(auto mode
= DirectModeFromEnum(values
[0]))
1871 Source
->DirectChannels
= *mode
;
1872 return UpdateSourceProps(Source
, Context
);
1874 throw al::context_error
{AL_INVALID_VALUE
, "Invalid direct channels mode: %s\n",
1875 HexPrinter
{values
[0]}.c_str()};
1879 case AL_DISTANCE_MODEL
:
1880 if constexpr(std::is_integral_v
<T
>)
1883 if(auto model
= DistanceModelFromALenum(values
[0]))
1885 Source
->mDistanceModel
= *model
;
1886 if(Context
->mSourceDistanceModel
)
1887 UpdateSourceProps(Source
, Context
);
1890 throw al::context_error
{AL_INVALID_VALUE
, "Invalid distance model: %s\n",
1891 HexPrinter
{values
[0]}.c_str()};
1895 case AL_SOURCE_RESAMPLER_SOFT
:
1896 if constexpr(std::is_integral_v
<T
>)
1899 CheckValue(values
[0] >= 0 && values
[0] <= static_cast<int>(Resampler::Max
));
1901 Source
->mResampler
= static_cast<Resampler
>(values
[0]);
1902 return UpdateSourceProps(Source
, Context
);
1906 case AL_SOURCE_SPATIALIZE_SOFT
:
1907 if constexpr(std::is_integral_v
<T
>)
1910 if(auto mode
= SpatializeModeFromEnum(values
[0]))
1912 Source
->mSpatialize
= *mode
;
1913 return UpdateSourceProps(Source
, Context
);
1915 throw al::context_error
{AL_INVALID_VALUE
, "Invalid source spatialize mode: %s\n",
1916 HexPrinter
{values
[0]}.c_str()};
1920 case AL_STEREO_MODE_SOFT
:
1921 if constexpr(std::is_integral_v
<T
>)
1924 if(const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1925 state
== AL_PLAYING
|| state
== AL_PAUSED
)
1926 throw al::context_error
{AL_INVALID_OPERATION
,
1927 "Modifying stereo mode on playing or paused source %u", Source
->id
};
1929 if(auto mode
= StereoModeFromEnum(values
[0]))
1931 Source
->mStereoMode
= *mode
;
1934 throw al::context_error
{AL_INVALID_VALUE
, "Invalid stereo mode: %s\n",
1935 HexPrinter
{values
[0]}.c_str()};
1939 case AL_AUXILIARY_SEND_FILTER
:
1940 if constexpr(std::is_integral_v
<T
>)
1943 const auto slotid
= static_cast<std::make_unsigned_t
<T
>>(values
[0]);
1944 const auto sendidx
= static_cast<std::make_unsigned_t
<T
>>(values
[1]);
1945 const auto filterid
= static_cast<std::make_unsigned_t
<T
>>(values
[2]);
1947 std::unique_lock slotlock
{Context
->mEffectSlotLock
};
1948 ALeffectslot
*slot
{};
1951 slot
= LookupEffectSlot(Context
, slotid
);
1953 throw al::context_error
{AL_INVALID_VALUE
, "Invalid effect ID %s",
1954 std::to_string(slotid
).c_str()};
1957 if(sendidx
>= device
->NumAuxSends
)
1958 throw al::context_error
{AL_INVALID_VALUE
, "Invalid send %s",
1959 std::to_string(sendidx
).c_str()};
1960 auto &send
= Source
->Send
[static_cast<size_t>(sendidx
)];
1964 std::lock_guard
<std::mutex
> filterlock
{device
->FilterLock
};
1965 ALfilter
*filter
{LookupFilter(device
, filterid
)};
1967 throw al::context_error
{AL_INVALID_VALUE
, "Invalid filter ID %s",
1968 std::to_string(filterid
).c_str()};
1970 send
.Gain
= filter
->Gain
;
1971 send
.GainHF
= filter
->GainHF
;
1972 send
.HFReference
= filter
->HFReference
;
1973 send
.GainLF
= filter
->GainLF
;
1974 send
.LFReference
= filter
->LFReference
;
1978 /* Disable filter */
1981 send
.HFReference
= LowPassFreqRef
;
1983 send
.LFReference
= HighPassFreqRef
;
1986 /* We must force an update if the current auxiliary slot is valid
1987 * and about to be changed on an active source, in case the old
1988 * slot is about to be deleted.
1990 if(send
.Slot
&& slot
!= send
.Slot
&& IsPlayingOrPaused(Source
))
1992 /* Add refcount on the new slot, and release the previous slot */
1993 if(slot
) IncrementRef(slot
->ref
);
1994 if(auto *oldslot
= send
.Slot
)
1995 DecrementRef(oldslot
->ref
);
1998 Voice
*voice
{GetSourceVoice(Source
, Context
)};
1999 if(voice
) UpdateSourceProps(Source
, voice
, Context
);
2000 else Source
->mPropsDirty
= true;
2004 if(slot
) IncrementRef(slot
->ref
);
2005 if(auto *oldslot
= send
.Slot
)
2006 DecrementRef(oldslot
->ref
);
2008 UpdateSourceProps(Source
, Context
);
2015 ERR("Unexpected %s property: 0x%04x\n", PropType
<T
>::Name(), prop
);
2016 throw al::context_error
{AL_INVALID_ENUM
, "Invalid source %s property 0x%04x",
2017 PropType
<T
>::Name(), prop
};
2021 template<typename T
, size_t N
>
2022 auto GetSizeChecker(const SourceProp prop
, const al::span
<T
,N
> values
)
2024 return [=](size_t expect
) -> void
2026 if(values
.size() == expect
) LIKELY
return;
2027 throw al::context_error
{AL_INVALID_ENUM
, "Property 0x%04x expects %zu value(s), got %zu",
2028 prop
, expect
, values
.size()};
2032 template<typename T
>
2033 NOINLINE
void GetProperty(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
2034 const al::span
<T
> values
)
2036 using std::chrono::duration_cast
;
2037 auto CheckSize
= GetSizeChecker(prop
, values
);
2038 ALCdevice
*device
{Context
->mALDevice
.get()};
2044 values
[0] = static_cast<T
>(Source
->Gain
);
2049 values
[0] = static_cast<T
>(Source
->Pitch
);
2052 case AL_MAX_DISTANCE
:
2054 values
[0] = static_cast<T
>(Source
->MaxDistance
);
2057 case AL_ROLLOFF_FACTOR
:
2059 values
[0] = static_cast<T
>(Source
->RolloffFactor
);
2062 case AL_REFERENCE_DISTANCE
:
2064 values
[0] = static_cast<T
>(Source
->RefDistance
);
2067 case AL_CONE_INNER_ANGLE
:
2069 values
[0] = static_cast<T
>(Source
->InnerAngle
);
2072 case AL_CONE_OUTER_ANGLE
:
2074 values
[0] = static_cast<T
>(Source
->OuterAngle
);
2079 values
[0] = static_cast<T
>(Source
->MinGain
);
2084 values
[0] = static_cast<T
>(Source
->MaxGain
);
2087 case AL_CONE_OUTER_GAIN
:
2089 values
[0] = static_cast<T
>(Source
->OuterGain
);
2093 case AL_SAMPLE_OFFSET
:
2094 case AL_BYTE_OFFSET
:
2096 values
[0] = GetSourceOffset
<T
>(Source
, prop
, Context
);
2099 case AL_CONE_OUTER_GAINHF
:
2101 values
[0] = static_cast<T
>(Source
->OuterGainHF
);
2104 case AL_AIR_ABSORPTION_FACTOR
:
2106 values
[0] = static_cast<T
>(Source
->AirAbsorptionFactor
);
2109 case AL_ROOM_ROLLOFF_FACTOR
:
2111 values
[0] = static_cast<T
>(Source
->RoomRolloffFactor
);
2114 case AL_DOPPLER_FACTOR
:
2116 values
[0] = static_cast<T
>(Source
->DopplerFactor
);
2119 case AL_SAMPLE_RW_OFFSETS_SOFT
:
2120 if constexpr(std::is_integral_v
<T
>)
2122 if(sBufferSubDataCompat
)
2125 values
[0] = GetSourceOffset
<T
>(Source
, AL_SAMPLE_OFFSET
, Context
);
2126 /* FIXME: values[1] should be ahead of values[0] by the device
2127 * update time. It needs to clamp or wrap the length of the
2130 values
[1] = values
[0];
2135 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
2136 if constexpr(std::is_floating_point_v
<T
>)
2138 if(sBufferSubDataCompat
)
2142 values
[0] = static_cast<T
>(Source
->Radius
);
2147 if(sBufferSubDataCompat
)
2150 values
[0] = GetSourceOffset
<T
>(Source
, AL_BYTE_OFFSET
, Context
);
2151 /* FIXME: values[1] should be ahead of values[0] by the device
2152 * update time. It needs to clamp or wrap the length of the
2155 values
[1] = values
[0];
2161 case AL_SUPER_STEREO_WIDTH_SOFT
:
2163 values
[0] = static_cast<T
>(Source
->EnhWidth
);
2166 case AL_BYTE_LENGTH_SOFT
:
2167 case AL_SAMPLE_LENGTH_SOFT
:
2168 case AL_SEC_LENGTH_SOFT
:
2170 values
[0] = GetSourceLength
<T
>(Source
, prop
);
2173 case AL_PANNING_ENABLED_SOFT
:
2175 values
[0] = Source
->mPanningEnabled
;
2180 values
[0] = static_cast<T
>(Source
->mPan
);
2183 case AL_STEREO_ANGLES
:
2184 if constexpr(std::is_floating_point_v
<T
>)
2187 values
[0] = static_cast<T
>(Source
->StereoPan
[0]);
2188 values
[1] = static_cast<T
>(Source
->StereoPan
[1]);
2193 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
2194 if constexpr(std::is_same_v
<T
,int64_t>)
2197 /* Get the source offset with the clock time first. Then get the
2198 * clock time with the device latency. Order is important.
2200 ClockLatency clocktime
{};
2201 nanoseconds srcclock
{};
2202 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2204 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2205 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2207 if(srcclock
== clocktime
.ClockTime
)
2208 values
[1] = nanoseconds
{clocktime
.Latency
}.count();
2211 /* If the clock time incremented, reduce the latency by that
2212 * much since it's that much closer to the source offset it got
2215 const auto diff
= std::min(clocktime
.Latency
, clocktime
.ClockTime
-srcclock
);
2216 values
[1] = nanoseconds
{clocktime
.Latency
- diff
}.count();
2222 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
2223 if constexpr(std::is_same_v
<T
,int64_t>)
2226 nanoseconds srcclock
{};
2227 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2228 values
[1] = srcclock
.count();
2233 case AL_SEC_OFFSET_LATENCY_SOFT
:
2234 if constexpr(std::is_same_v
<T
,double>)
2237 /* Get the source offset with the clock time first. Then get the
2238 * clock time with the device latency. Order is important.
2240 ClockLatency clocktime
{};
2241 nanoseconds srcclock
{};
2242 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2244 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2245 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2247 if(srcclock
== clocktime
.ClockTime
)
2248 values
[1] = duration_cast
<seconds_d
>(clocktime
.Latency
).count();
2251 /* If the clock time incremented, reduce the latency by that
2252 * much since it's that much closer to the source offset it got
2255 const auto diff
= std::min(clocktime
.Latency
, clocktime
.ClockTime
-srcclock
);
2256 values
[1] = duration_cast
<seconds_d
>(clocktime
.Latency
- diff
).count();
2262 case AL_SEC_OFFSET_CLOCK_SOFT
:
2263 if constexpr(std::is_same_v
<T
,double>)
2266 nanoseconds srcclock
{};
2267 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2268 values
[1] = duration_cast
<seconds_d
>(srcclock
).count();
2275 values
[0] = static_cast<T
>(Source
->Position
[0]);
2276 values
[1] = static_cast<T
>(Source
->Position
[1]);
2277 values
[2] = static_cast<T
>(Source
->Position
[2]);
2282 values
[0] = static_cast<T
>(Source
->Velocity
[0]);
2283 values
[1] = static_cast<T
>(Source
->Velocity
[1]);
2284 values
[2] = static_cast<T
>(Source
->Velocity
[2]);
2289 values
[0] = static_cast<T
>(Source
->Direction
[0]);
2290 values
[1] = static_cast<T
>(Source
->Direction
[1]);
2291 values
[2] = static_cast<T
>(Source
->Direction
[2]);
2294 case AL_ORIENTATION
:
2296 values
[0] = static_cast<T
>(Source
->OrientAt
[0]);
2297 values
[1] = static_cast<T
>(Source
->OrientAt
[1]);
2298 values
[2] = static_cast<T
>(Source
->OrientAt
[2]);
2299 values
[3] = static_cast<T
>(Source
->OrientUp
[0]);
2300 values
[4] = static_cast<T
>(Source
->OrientUp
[1]);
2301 values
[5] = static_cast<T
>(Source
->OrientUp
[2]);
2305 case AL_SOURCE_RELATIVE
:
2306 if constexpr(std::is_integral_v
<T
>)
2309 values
[0] = Source
->HeadRelative
;
2315 if constexpr(std::is_integral_v
<T
>)
2318 values
[0] = Source
->Looping
;
2324 if constexpr(std::is_integral_v
<T
>)
2327 ALbufferQueueItem
*BufferList
{};
2328 /* HACK: This query should technically only return the buffer set
2329 * on a static source. However, some apps had used it to detect
2330 * when a streaming source changed buffers, so report the current
2331 * buffer's ID when playing.
2333 if(Source
->SourceType
== AL_STATIC
|| Source
->state
== AL_INITIAL
)
2335 if(!Source
->mQueue
.empty())
2336 BufferList
= &Source
->mQueue
.front();
2338 else if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2340 VoiceBufferItem
*Current
{voice
->mCurrentBuffer
.load(std::memory_order_relaxed
)};
2341 BufferList
= static_cast<ALbufferQueueItem
*>(Current
);
2343 ALbuffer
*buffer
{BufferList
? BufferList
->mBuffer
: nullptr};
2344 values
[0] = buffer
? static_cast<T
>(buffer
->id
) : T
{0};
2349 case AL_SOURCE_STATE
:
2350 if constexpr(std::is_integral_v
<T
>)
2353 values
[0] = GetSourceState(Source
, GetSourceVoice(Source
, Context
));
2358 case AL_BUFFERS_QUEUED
:
2359 if constexpr(std::is_integral_v
<T
>)
2362 values
[0] = static_cast<T
>(Source
->mQueue
.size());
2367 case AL_BUFFERS_PROCESSED
:
2368 if constexpr(std::is_integral_v
<T
>)
2371 if(Source
->Looping
|| Source
->SourceType
!= AL_STREAMING
)
2373 /* Buffers on a looping source are in a perpetual state of
2374 * PENDING, so don't report any as PROCESSED
2381 if(Source
->state
!= AL_INITIAL
)
2383 const VoiceBufferItem
*Current
{nullptr};
2384 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2385 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
2386 for(auto &item
: Source
->mQueue
)
2388 if(&item
== Current
)
2399 case AL_SOURCE_TYPE
:
2400 if constexpr(std::is_integral_v
<T
>)
2403 values
[0] = Source
->SourceType
;
2408 case AL_DIRECT_FILTER_GAINHF_AUTO
:
2409 if constexpr(std::is_integral_v
<T
>)
2412 values
[0] = Source
->DryGainHFAuto
;
2417 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
2418 if constexpr(std::is_integral_v
<T
>)
2421 values
[0] = Source
->WetGainAuto
;
2426 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
2427 if constexpr(std::is_integral_v
<T
>)
2430 values
[0] = Source
->WetGainHFAuto
;
2435 case AL_DIRECT_CHANNELS_SOFT
:
2436 if constexpr(std::is_integral_v
<T
>)
2439 values
[0] = EnumFromDirectMode(Source
->DirectChannels
);
2444 case AL_DISTANCE_MODEL
:
2445 if constexpr(std::is_integral_v
<T
>)
2448 values
[0] = ALenumFromDistanceModel(Source
->mDistanceModel
);
2453 case AL_SOURCE_RESAMPLER_SOFT
:
2454 if constexpr(std::is_integral_v
<T
>)
2457 values
[0] = static_cast<T
>(Source
->mResampler
);
2462 case AL_SOURCE_SPATIALIZE_SOFT
:
2463 if constexpr(std::is_integral_v
<T
>)
2466 values
[0] = EnumFromSpatializeMode(Source
->mSpatialize
);
2471 case AL_STEREO_MODE_SOFT
:
2472 if constexpr(std::is_integral_v
<T
>)
2475 values
[0] = EnumFromStereoMode(Source
->mStereoMode
);
2480 case AL_DIRECT_FILTER
:
2481 case AL_AUXILIARY_SEND_FILTER
:
2485 ERR("Unexpected %s query property: 0x%04x\n", PropType
<T
>::Name(), prop
);
2486 throw al::context_error
{AL_INVALID_ENUM
, "Invalid source %s query property 0x%04x",
2487 PropType
<T
>::Name(), prop
};
2491 void StartSources(ALCcontext
*const context
, const al::span
<ALsource
*> srchandles
,
2492 const nanoseconds start_time
=nanoseconds::min())
2494 ALCdevice
*device
{context
->mALDevice
.get()};
2495 /* If the device is disconnected, and voices stop on disconnect, go right
2498 if(!device
->Connected
.load(std::memory_order_acquire
)) UNLIKELY
2500 if(context
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
2502 for(ALsource
*source
: srchandles
)
2504 /* TODO: Send state change event? */
2505 source
->Offset
= 0.0;
2506 source
->OffsetType
= AL_NONE
;
2507 source
->state
= AL_STOPPED
;
2513 /* Count the number of reusable voices. */
2514 auto voicelist
= context
->getVoicesSpan();
2515 size_t free_voices
{0};
2516 for(const Voice
*voice
: voicelist
)
2518 free_voices
+= (voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2519 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2520 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false);
2521 if(free_voices
== srchandles
.size())
2524 if(srchandles
.size() != free_voices
) UNLIKELY
2526 const size_t inc_amount
{srchandles
.size() - free_voices
};
2527 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
2528 if(inc_amount
> allvoices
.size() - voicelist
.size())
2530 /* Increase the number of voices to handle the request. */
2531 context
->allocVoices(inc_amount
- (allvoices
.size() - voicelist
.size()));
2533 context
->mActiveVoiceCount
.fetch_add(inc_amount
, std::memory_order_release
);
2534 voicelist
= context
->getVoicesSpan();
2537 auto voiceiter
= voicelist
.begin();
2539 VoiceChange
*tail
{}, *cur
{};
2540 for(ALsource
*source
: srchandles
)
2542 /* Check that there is a queue containing at least one valid, non zero
2545 auto find_buffer
= [](ALbufferQueueItem
&entry
) noexcept
2546 { return entry
.mSampleLen
!= 0 || entry
.mCallback
!= nullptr; };
2547 auto BufferList
= std::find_if(source
->mQueue
.begin(), source
->mQueue
.end(), find_buffer
);
2549 /* If there's nothing to play, go right to stopped. */
2550 if(BufferList
== source
->mQueue
.end()) UNLIKELY
2552 /* NOTE: A source without any playable buffers should not have a
2553 * Voice since it shouldn't be in a playing or paused state. So
2554 * there's no need to look up its voice and clear the source.
2556 source
->Offset
= 0.0;
2557 source
->OffsetType
= AL_NONE
;
2558 source
->state
= AL_STOPPED
;
2563 cur
= tail
= GetVoiceChanger(context
);
2566 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
2567 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
2570 Voice
*voice
{GetSourceVoice(source
, context
)};
2571 switch(GetSourceState(source
, voice
))
2574 /* A source that's paused simply resumes. If there's no voice, it
2575 * was lost from a disconnect, so just start over with a new one.
2577 cur
->mOldVoice
= nullptr;
2579 cur
->mVoice
= voice
;
2580 cur
->mSourceID
= source
->id
;
2581 cur
->mState
= VChangeState::Play
;
2582 source
->state
= AL_PLAYING
;
2584 if(context
->hasEax())
2585 source
->eaxCommit();
2586 #endif // ALSOFT_EAX
2590 /* A source that's already playing is restarted from the beginning.
2591 * Stop the current voice and start a new one so it properly cross-
2592 * fades back to the beginning.
2595 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
2596 cur
->mOldVoice
= voice
;
2601 assert(voice
== nullptr);
2602 cur
->mOldVoice
= nullptr;
2604 if(context
->hasEax())
2605 source
->eaxCommit();
2606 #endif // ALSOFT_EAX
2610 /* Find the next unused voice to play this source with. */
2611 for(;voiceiter
!= voicelist
.end();++voiceiter
,++vidx
)
2613 Voice
*v
{*voiceiter
};
2614 if(v
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2615 && v
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2616 && v
->mPendingChange
.load(std::memory_order_relaxed
) == false)
2622 ASSUME(voice
!= nullptr);
2624 voice
->mPosition
.store(0, std::memory_order_relaxed
);
2625 voice
->mPositionFrac
.store(0, std::memory_order_relaxed
);
2626 voice
->mCurrentBuffer
.store(&source
->mQueue
.front(), std::memory_order_relaxed
);
2627 voice
->mStartTime
= start_time
;
2628 voice
->mFlags
.reset();
2629 /* A source that's not playing or paused has any offset applied when it
2632 if(const ALenum offsettype
{source
->OffsetType
})
2634 const double offset
{source
->Offset
};
2635 source
->OffsetType
= AL_NONE
;
2636 source
->Offset
= 0.0;
2637 if(auto vpos
= GetSampleOffset(source
->mQueue
, offsettype
, offset
))
2639 voice
->mPosition
.store(vpos
->pos
, std::memory_order_relaxed
);
2640 voice
->mPositionFrac
.store(vpos
->frac
, std::memory_order_relaxed
);
2641 voice
->mCurrentBuffer
.store(vpos
->bufferitem
, std::memory_order_relaxed
);
2642 if(vpos
->pos
> 0 || (vpos
->pos
== 0 && vpos
->frac
> 0)
2643 || vpos
->bufferitem
!= &source
->mQueue
.front())
2644 voice
->mFlags
.set(VoiceIsFading
);
2647 InitVoice(voice
, source
, al::to_address(BufferList
), context
, device
);
2649 source
->VoiceIdx
= vidx
;
2650 source
->state
= AL_PLAYING
;
2652 cur
->mVoice
= voice
;
2653 cur
->mSourceID
= source
->id
;
2654 cur
->mState
= VChangeState::Play
;
2657 SendVoiceChanges(context
, tail
);
2662 AL_API
DECL_FUNC2(void, alGenSources
, ALsizei
,n
, ALuint
*,sources
)
2663 FORCE_ALIGN
void AL_APIENTRY
alGenSourcesDirect(ALCcontext
*context
, ALsizei n
, ALuint
*sources
) noexcept
2666 throw al::context_error
{AL_INVALID_VALUE
, "Generating %d sources", n
};
2667 if(n
<= 0) UNLIKELY
return;
2669 std::unique_lock
<std::mutex
> srclock
{context
->mSourceLock
};
2670 ALCdevice
*device
{context
->mALDevice
.get()};
2672 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2673 if(sids
.size() > device
->SourcesMax
-context
->mNumSources
)
2674 throw al::context_error
{AL_OUT_OF_MEMORY
, "Exceeding %u source limit (%u + %d)",
2675 device
->SourcesMax
, context
->mNumSources
, n
};
2676 if(!EnsureSources(context
, sids
.size()))
2677 throw al::context_error
{AL_OUT_OF_MEMORY
, "Failed to allocate %d source%s", n
,
2678 (n
== 1) ? "" : "s"};
2680 std::generate(sids
.begin(), sids
.end(), [context
]{ return AllocSource(context
)->id
; });
2682 catch(al::context_error
& e
) {
2683 context
->setError(e
.errorCode(), "%s", e
.what());
2686 AL_API
DECL_FUNC2(void, alDeleteSources
, ALsizei
,n
, const ALuint
*,sources
)
2687 FORCE_ALIGN
void AL_APIENTRY
alDeleteSourcesDirect(ALCcontext
*context
, ALsizei n
,
2688 const ALuint
*sources
) noexcept
2691 throw al::context_error
{AL_INVALID_VALUE
, "Deleting %d sources", n
};
2692 if(n
<= 0) UNLIKELY
return;
2694 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2696 /* Check that all Sources are valid */
2697 auto validate_source
= [context
](const ALuint sid
) -> bool
2698 { return LookupSource(context
, sid
) != nullptr; };
2700 const al::span sids
{sources
, static_cast<ALuint
>(n
)};
2701 auto invsrc
= std::find_if_not(sids
.begin(), sids
.end(), validate_source
);
2702 if(invsrc
!= sids
.end())
2703 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", *invsrc
};
2705 /* All good. Delete source IDs. */
2706 auto delete_source
= [&context
](const ALuint sid
) -> void
2708 if(ALsource
*src
{LookupSource(context
, sid
)})
2709 FreeSource(context
, src
);
2711 std::for_each(sids
.begin(), sids
.end(), delete_source
);
2713 catch(al::context_error
& e
) {
2714 context
->setError(e
.errorCode(), "%s", e
.what());
2717 AL_API
DECL_FUNC1(ALboolean
, alIsSource
, ALuint
,source
)
2718 FORCE_ALIGN ALboolean AL_APIENTRY
alIsSourceDirect(ALCcontext
*context
, ALuint source
) noexcept
2720 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
2721 if(LookupSource(context
, source
) != nullptr)
2727 AL_API
DECL_FUNC3(void, alSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
,value
)
2728 FORCE_ALIGN
void AL_APIENTRY
alSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2729 ALfloat value
) noexcept
2731 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2732 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2733 ALsource
*Source
{LookupSource(context
, source
)};
2735 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2737 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2739 catch(al::context_error
& e
) {
2740 context
->setError(e
.errorCode(), "%s", e
.what());
2743 AL_API
DECL_FUNC5(void, alSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
,value1
, ALfloat
,value2
, ALfloat
,value3
)
2744 FORCE_ALIGN
void AL_APIENTRY
alSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2745 ALfloat value1
, ALfloat value2
, ALfloat value3
) noexcept
2747 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2748 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2749 ALsource
*Source
{LookupSource(context
, source
)};
2751 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2753 const std::array fvals
{value1
, value2
, value3
};
2754 SetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
2756 catch(al::context_error
& e
) {
2757 context
->setError(e
.errorCode(), "%s", e
.what());
2760 AL_API
DECL_FUNC3(void, alSourcefv
, ALuint
,source
, ALenum
,param
, const ALfloat
*,values
)
2761 FORCE_ALIGN
void AL_APIENTRY
alSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2762 const ALfloat
*values
) noexcept
2764 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2765 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2766 ALsource
*Source
{LookupSource(context
, source
)};
2768 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2770 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2772 const ALuint count
{FloatValsByProp(param
)};
2773 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2775 catch(al::context_error
& e
) {
2776 context
->setError(e
.errorCode(), "%s", e
.what());
2780 AL_API
DECL_FUNCEXT3(void, alSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value
)
2781 FORCE_ALIGN
void AL_APIENTRY
alSourcedDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2782 ALdouble value
) noexcept
2784 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2785 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2786 ALsource
*Source
{LookupSource(context
, source
)};
2788 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2790 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1});
2792 catch(al::context_error
& e
) {
2793 context
->setError(e
.errorCode(), "%s", e
.what());
2796 AL_API
DECL_FUNCEXT5(void, alSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
,value1
, ALdouble
,value2
, ALdouble
,value3
)
2797 FORCE_ALIGN
void AL_APIENTRY
alSource3dDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2798 ALdouble value1
, ALdouble value2
, ALdouble value3
) noexcept
2800 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2801 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2802 ALsource
*Source
{LookupSource(context
, source
)};
2804 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2806 const std::array dvals
{value1
, value2
, value3
};
2807 SetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
2809 catch(al::context_error
& e
) {
2810 context
->setError(e
.errorCode(), "%s", e
.what());
2813 AL_API
DECL_FUNCEXT3(void, alSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, const ALdouble
*,values
)
2814 FORCE_ALIGN
void AL_APIENTRY
alSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
,
2815 const ALdouble
*values
) noexcept
2817 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2818 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2819 ALsource
*Source
{LookupSource(context
, source
)};
2821 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2823 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2825 const ALuint count
{DoubleValsByProp(param
)};
2826 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2828 catch(al::context_error
& e
) {
2829 context
->setError(e
.errorCode(), "%s", e
.what());
2833 AL_API
DECL_FUNC3(void, alSourcei
, ALuint
,source
, ALenum
,param
, ALint
,value
)
2834 FORCE_ALIGN
void AL_APIENTRY
alSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2835 ALint value
) noexcept
2837 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2838 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2839 ALsource
*Source
{LookupSource(context
, source
)};
2841 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2843 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2845 catch(al::context_error
& e
) {
2846 context
->setError(e
.errorCode(), "%s", e
.what());
2849 AL_API
DECL_FUNC5(void, alSource3i
, ALuint
,buffer
, ALenum
,param
, ALint
,value1
, ALint
,value2
, ALint
,value3
)
2850 FORCE_ALIGN
void AL_APIENTRY
alSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2851 ALint value1
, ALint value2
, ALint value3
) noexcept
2853 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2854 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2855 ALsource
*Source
{LookupSource(context
, source
)};
2857 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2859 const std::array ivals
{value1
, value2
, value3
};
2860 SetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
2862 catch(al::context_error
& e
) {
2863 context
->setError(e
.errorCode(), "%s", e
.what());
2866 AL_API
DECL_FUNC3(void, alSourceiv
, ALuint
,source
, ALenum
,param
, const ALint
*,values
)
2867 FORCE_ALIGN
void AL_APIENTRY
alSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2868 const ALint
*values
) noexcept
2870 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2871 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2872 ALsource
*Source
{LookupSource(context
, source
)};
2874 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2876 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2878 const ALuint count
{IntValsByProp(param
)};
2879 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2881 catch(al::context_error
& e
) {
2882 context
->setError(e
.errorCode(), "%s", e
.what());
2886 AL_API
DECL_FUNCEXT3(void, alSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value
)
2887 FORCE_ALIGN
void AL_APIENTRY
alSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
,
2888 ALenum param
, ALint64SOFT value
) noexcept
2890 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2891 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2892 ALsource
*Source
{LookupSource(context
, source
)};
2894 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2896 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), {&value
, 1u});
2898 catch(al::context_error
& e
) {
2899 context
->setError(e
.errorCode(), "%s", e
.what());
2902 AL_API
DECL_FUNCEXT5(void, alSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
,value1
, ALint64SOFT
,value2
, ALint64SOFT
,value3
)
2903 FORCE_ALIGN
void AL_APIENTRY
alSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
2904 ALenum param
, ALint64SOFT value1
, ALint64SOFT value2
, ALint64SOFT value3
) noexcept
2906 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2907 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2908 ALsource
*Source
{LookupSource(context
, source
)};
2910 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2912 const std::array i64vals
{value1
, value2
, value3
};
2913 SetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
2915 catch(al::context_error
& e
) {
2916 context
->setError(e
.errorCode(), "%s", e
.what());
2919 AL_API
DECL_FUNCEXT3(void, alSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, const ALint64SOFT
*,values
)
2920 FORCE_ALIGN
void AL_APIENTRY
alSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
2921 ALenum param
, const ALint64SOFT
*values
) noexcept
2923 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
2924 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2925 ALsource
*Source
{LookupSource(context
, source
)};
2927 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2929 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2931 const ALuint count
{Int64ValsByProp(param
)};
2932 SetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2934 catch(al::context_error
& e
) {
2935 context
->setError(e
.errorCode(), "%s", e
.what());
2939 AL_API
DECL_FUNC3(void, alGetSourcef
, ALuint
,source
, ALenum
,param
, ALfloat
*,value
)
2940 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2941 ALfloat
*value
) noexcept
2943 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2944 ALsource
*Source
{LookupSource(context
, source
)};
2946 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2948 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2950 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
2952 catch(al::context_error
& e
) {
2953 context
->setError(e
.errorCode(), "%s", e
.what());
2956 AL_API
DECL_FUNC5(void, alGetSource3f
, ALuint
,source
, ALenum
,param
, ALfloat
*,value1
, ALfloat
*,value2
, ALfloat
*,value3
)
2957 FORCE_ALIGN
void AL_APIENTRY
alGetSource3fDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2958 ALfloat
*value1
, ALfloat
*value2
, ALfloat
*value3
) noexcept
2960 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2961 ALsource
*Source
{LookupSource(context
, source
)};
2963 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2964 if(!(value1
&& value2
&& value3
))
2965 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2967 std::array
<float,3> fvals
{};
2968 GetProperty
<float>(Source
, context
, static_cast<SourceProp
>(param
), fvals
);
2973 catch(al::context_error
& e
) {
2974 context
->setError(e
.errorCode(), "%s", e
.what());
2977 AL_API
DECL_FUNC3(void, alGetSourcefv
, ALuint
,source
, ALenum
,param
, ALfloat
*,values
)
2978 FORCE_ALIGN
void AL_APIENTRY
alGetSourcefvDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
2979 ALfloat
*values
) noexcept
2981 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
2982 ALsource
*Source
{LookupSource(context
, source
)};
2984 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
2986 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
2988 const ALuint count
{FloatValsByProp(param
)};
2989 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
2991 catch(al::context_error
& e
) {
2992 context
->setError(e
.errorCode(), "%s", e
.what());
2996 AL_API
DECL_FUNCEXT3(void, alGetSourced
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value
)
2997 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedDirectSOFT(ALCcontext
*context
, ALuint source
,
2998 ALenum param
, ALdouble
*value
) noexcept
3000 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3001 ALsource
*Source
{LookupSource(context
, source
)};
3003 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3005 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3007 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3009 catch(al::context_error
& e
) {
3010 context
->setError(e
.errorCode(), "%s", e
.what());
3013 AL_API
DECL_FUNCEXT5(void, alGetSource3d
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,value1
, ALdouble
*,value2
, ALdouble
*,value3
)
3014 FORCE_ALIGN
void AL_APIENTRY
alGetSource3dDirectSOFT(ALCcontext
*context
, ALuint source
,
3015 ALenum param
, ALdouble
*value1
, ALdouble
*value2
, ALdouble
*value3
) noexcept
3017 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3018 ALsource
*Source
{LookupSource(context
, source
)};
3020 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3021 if(!(value1
&& value2
&& value3
))
3022 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3024 std::array
<double,3> dvals
{};
3025 GetProperty
<double>(Source
, context
, static_cast<SourceProp
>(param
), dvals
);
3030 catch(al::context_error
& e
) {
3031 context
->setError(e
.errorCode(), "%s", e
.what());
3034 AL_API
DECL_FUNCEXT3(void, alGetSourcedv
,SOFT
, ALuint
,source
, ALenum
,param
, ALdouble
*,values
)
3035 FORCE_ALIGN
void AL_APIENTRY
alGetSourcedvDirectSOFT(ALCcontext
*context
, ALuint source
,
3036 ALenum param
, ALdouble
*values
) noexcept
3038 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3039 ALsource
*Source
{LookupSource(context
, source
)};
3041 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3043 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3045 const ALuint count
{DoubleValsByProp(param
)};
3046 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3048 catch(al::context_error
& e
) {
3049 context
->setError(e
.errorCode(), "%s", e
.what());
3053 AL_API
DECL_FUNC3(void, alGetSourcei
, ALuint
,source
, ALenum
,param
, ALint
*,value
)
3054 FORCE_ALIGN
void AL_APIENTRY
alGetSourceiDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3055 ALint
*value
) noexcept
3057 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3058 ALsource
*Source
{LookupSource(context
, source
)};
3060 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3062 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3064 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3066 catch(al::context_error
& e
) {
3067 context
->setError(e
.errorCode(), "%s", e
.what());
3070 AL_API
DECL_FUNC5(void, alGetSource3i
, ALuint
,source
, ALenum
,param
, ALint
*,value1
, ALint
*,value2
, ALint
*,value3
)
3071 FORCE_ALIGN
void AL_APIENTRY
alGetSource3iDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3072 ALint
*value1
, ALint
*value2
, ALint
*value3
) noexcept
3074 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3075 ALsource
*Source
{LookupSource(context
, source
)};
3077 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3078 if(!(value1
&& value2
&& value3
))
3079 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3081 std::array
<int,3> ivals
{};
3082 GetProperty
<int>(Source
, context
, static_cast<SourceProp
>(param
), ivals
);
3087 catch(al::context_error
& e
) {
3088 context
->setError(e
.errorCode(), "%s", e
.what());
3091 AL_API
DECL_FUNC3(void, alGetSourceiv
, ALuint
,source
, ALenum
,param
, ALint
*,values
)
3092 FORCE_ALIGN
void AL_APIENTRY
alGetSourceivDirect(ALCcontext
*context
, ALuint source
, ALenum param
,
3093 ALint
*values
) noexcept
3095 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3096 ALsource
*Source
{LookupSource(context
, source
)};
3098 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3100 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3102 const ALuint count
{IntValsByProp(param
)};
3103 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3105 catch(al::context_error
& e
) {
3106 context
->setError(e
.errorCode(), "%s", e
.what());
3110 AL_API
DECL_FUNCEXT3(void, alGetSourcei64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value
)
3111 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64DirectSOFT(ALCcontext
*context
, ALuint source
, ALenum param
, ALint64SOFT
*value
) noexcept
3113 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3114 ALsource
*Source
{LookupSource(context
, source
)};
3116 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3118 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3120 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{value
, 1u});
3122 catch(al::context_error
& e
) {
3123 context
->setError(e
.errorCode(), "%s", e
.what());
3126 AL_API
DECL_FUNCEXT5(void, alGetSource3i64
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,value1
, ALint64SOFT
*,value2
, ALint64SOFT
*,value3
)
3127 FORCE_ALIGN
void AL_APIENTRY
alGetSource3i64DirectSOFT(ALCcontext
*context
, ALuint source
,
3128 ALenum param
, ALint64SOFT
*value1
, ALint64SOFT
*value2
, ALint64SOFT
*value3
) noexcept
3130 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3131 ALsource
*Source
{LookupSource(context
, source
)};
3133 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3134 if(!(value1
&& value2
&& value3
))
3135 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3137 std::array
<int64_t,3> i64vals
{};
3138 GetProperty
<int64_t>(Source
, context
, static_cast<SourceProp
>(param
), i64vals
);
3139 *value1
= i64vals
[0];
3140 *value2
= i64vals
[1];
3141 *value3
= i64vals
[2];
3143 catch(al::context_error
& e
) {
3144 context
->setError(e
.errorCode(), "%s", e
.what());
3147 AL_API
DECL_FUNCEXT3(void, alGetSourcei64v
,SOFT
, ALuint
,source
, ALenum
,param
, ALint64SOFT
*,values
)
3148 FORCE_ALIGN
void AL_APIENTRY
alGetSourcei64vDirectSOFT(ALCcontext
*context
, ALuint source
,
3149 ALenum param
, ALint64SOFT
*values
) noexcept
3151 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3152 ALsource
*Source
{LookupSource(context
, source
)};
3154 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3156 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
3158 const ALuint count
{Int64ValsByProp(param
)};
3159 GetProperty(Source
, context
, static_cast<SourceProp
>(param
), al::span
{values
, count
});
3161 catch(al::context_error
& e
) {
3162 context
->setError(e
.errorCode(), "%s", e
.what());
3166 AL_API
DECL_FUNC1(void, alSourcePlay
, ALuint
,source
)
3167 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayDirect(ALCcontext
*context
, ALuint source
) noexcept
3169 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3170 ALsource
*Source
{LookupSource(context
, source
)};
3172 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3174 StartSources(context
, {&Source
, 1});
3176 catch(al::context_error
& e
) {
3177 context
->setError(e
.errorCode(), "%s", e
.what());
3180 FORCE_ALIGN
DECL_FUNCEXT2(void, alSourcePlayAtTime
,SOFT
, ALuint
,source
, ALint64SOFT
,start_time
)
3181 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimeDirectSOFT(ALCcontext
*context
, ALuint source
,
3182 ALint64SOFT start_time
) noexcept
3185 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3187 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3188 ALsource
*Source
{LookupSource(context
, source
)};
3190 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", source
};
3192 StartSources(context
, {&Source
, 1}, nanoseconds
{start_time
});
3194 catch(al::context_error
& e
) {
3195 context
->setError(e
.errorCode(), "%s", e
.what());
3198 AL_API
DECL_FUNC2(void, alSourcePlayv
, ALsizei
,n
, const ALuint
*,sources
)
3199 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayvDirect(ALCcontext
*context
, ALsizei n
,
3200 const ALuint
*sources
) noexcept
3203 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3204 if(n
<= 0) UNLIKELY
return;
3206 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3207 std::vector
<ALsource
*> extra_sources
;
3208 std::array
<ALsource
*,8> source_storage
;
3209 al::span
<ALsource
*> srchandles
;
3210 if(sids
.size() <= source_storage
.size()) LIKELY
3211 srchandles
= al::span
{source_storage
}.first(sids
.size());
3214 extra_sources
.resize(sids
.size());
3215 srchandles
= extra_sources
;
3218 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3219 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(),
3220 [context
](const ALuint sid
) -> ALsource
*
3222 if(ALsource
*src
{LookupSource(context
, sid
)})
3224 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3227 StartSources(context
, srchandles
);
3229 catch(al::context_error
& e
) {
3230 context
->setError(e
.errorCode(), "%s", e
.what());
3233 FORCE_ALIGN
DECL_FUNCEXT3(void, alSourcePlayAtTimev
,SOFT
, ALsizei
,n
, const ALuint
*,sources
, ALint64SOFT
,start_time
)
3234 FORCE_ALIGN
void AL_APIENTRY
alSourcePlayAtTimevDirectSOFT(ALCcontext
*context
, ALsizei n
,
3235 const ALuint
*sources
, ALint64SOFT start_time
) noexcept
3238 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d sources", n
};
3239 if(n
<= 0) UNLIKELY
return;
3242 throw al::context_error
{AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
};
3244 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3245 std::vector
<ALsource
*> extra_sources
;
3246 std::array
<ALsource
*,8> source_storage
;
3247 al::span
<ALsource
*> srchandles
;
3248 if(sids
.size() <= source_storage
.size()) LIKELY
3249 srchandles
= al::span
{source_storage
}.first(sids
.size());
3252 extra_sources
.resize(sids
.size());
3253 srchandles
= extra_sources
;
3256 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3257 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(),
3258 [context
](const ALuint sid
) -> ALsource
*
3260 if(ALsource
*src
{LookupSource(context
, sid
)})
3262 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3265 StartSources(context
, srchandles
, nanoseconds
{start_time
});
3267 catch(al::context_error
& e
) {
3268 context
->setError(e
.errorCode(), "%s", e
.what());
3272 AL_API
DECL_FUNC1(void, alSourcePause
, ALuint
,source
)
3273 FORCE_ALIGN
void AL_APIENTRY
alSourcePauseDirect(ALCcontext
*context
, ALuint source
) noexcept
3274 { alSourcePausevDirect(context
, 1, &source
); }
3276 AL_API
DECL_FUNC2(void, alSourcePausev
, ALsizei
,n
, const ALuint
*,sources
)
3277 FORCE_ALIGN
void AL_APIENTRY
alSourcePausevDirect(ALCcontext
*context
, ALsizei n
,
3278 const ALuint
*sources
) noexcept
3281 throw al::context_error
{AL_INVALID_VALUE
, "Pausing %d sources", n
};
3282 if(n
<= 0) UNLIKELY
return;
3284 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3285 std::vector
<ALsource
*> extra_sources
;
3286 std::array
<ALsource
*,8> source_storage
;
3287 al::span
<ALsource
*> srchandles
;
3288 if(sids
.size() <= source_storage
.size()) LIKELY
3289 srchandles
= al::span
{source_storage
}.first(sids
.size());
3292 extra_sources
.resize(sids
.size());
3293 srchandles
= extra_sources
;
3296 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3297 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(),
3298 [context
](const ALuint sid
) -> ALsource
*
3300 if(ALsource
*src
{LookupSource(context
, sid
)})
3302 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3305 /* Pausing has to be done in two steps. First, for each source that's
3306 * detected to be playing, chamge the voice (asynchronously) to
3309 VoiceChange
*tail
{}, *cur
{};
3310 for(ALsource
*source
: srchandles
)
3312 Voice
*voice
{GetSourceVoice(source
, context
)};
3313 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3316 cur
= tail
= GetVoiceChanger(context
);
3319 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3320 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3322 cur
->mVoice
= voice
;
3323 cur
->mSourceID
= source
->id
;
3324 cur
->mState
= VChangeState::Pause
;
3329 SendVoiceChanges(context
, tail
);
3330 /* Second, now that the voice changes have been sent, because it's
3331 * possible that the voice stopped after it was detected playing and
3332 * before the voice got paused, recheck that the source is still
3333 * considered playing and set it to paused if so.
3335 for(ALsource
*source
: srchandles
)
3337 Voice
*voice
{GetSourceVoice(source
, context
)};
3338 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3339 source
->state
= AL_PAUSED
;
3343 catch(al::context_error
& e
) {
3344 context
->setError(e
.errorCode(), "%s", e
.what());
3348 AL_API
DECL_FUNC1(void, alSourceStop
, ALuint
,source
)
3349 FORCE_ALIGN
void AL_APIENTRY
alSourceStopDirect(ALCcontext
*context
, ALuint source
) noexcept
3350 { alSourceStopvDirect(context
, 1, &source
); }
3352 AL_API
DECL_FUNC2(void, alSourceStopv
, ALsizei
,n
, const ALuint
*,sources
)
3353 FORCE_ALIGN
void AL_APIENTRY
alSourceStopvDirect(ALCcontext
*context
, ALsizei n
,
3354 const ALuint
*sources
) noexcept
3357 throw al::context_error
{AL_INVALID_VALUE
, "Stopping %d sources", n
};
3358 if(n
<= 0) UNLIKELY
return;
3360 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3361 std::vector
<ALsource
*> extra_sources
;
3362 std::array
<ALsource
*,8> source_storage
;
3363 al::span
<ALsource
*> srchandles
;
3364 if(sids
.size() <= source_storage
.size()) LIKELY
3365 srchandles
= al::span
{source_storage
}.first(sids
.size());
3368 extra_sources
.resize(sids
.size());
3369 srchandles
= extra_sources
;
3372 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3373 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(),
3374 [context
](const ALuint sid
) -> ALsource
*
3376 if(ALsource
*src
{LookupSource(context
, sid
)})
3378 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3381 VoiceChange
*tail
{}, *cur
{};
3382 for(ALsource
*source
: srchandles
)
3384 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3387 cur
= tail
= GetVoiceChanger(context
);
3390 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3391 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3393 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3394 cur
->mVoice
= voice
;
3395 cur
->mSourceID
= source
->id
;
3396 cur
->mState
= VChangeState::Stop
;
3397 source
->state
= AL_STOPPED
;
3399 source
->Offset
= 0.0;
3400 source
->OffsetType
= AL_NONE
;
3401 source
->VoiceIdx
= InvalidVoiceIndex
;
3404 SendVoiceChanges(context
, tail
);
3406 catch(al::context_error
& e
) {
3407 context
->setError(e
.errorCode(), "%s", e
.what());
3411 AL_API
DECL_FUNC1(void, alSourceRewind
, ALuint
,source
)
3412 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindDirect(ALCcontext
*context
, ALuint source
) noexcept
3413 { alSourceRewindvDirect(context
, 1, &source
); }
3415 AL_API
DECL_FUNC2(void, alSourceRewindv
, ALsizei
,n
, const ALuint
*,sources
)
3416 FORCE_ALIGN
void AL_APIENTRY
alSourceRewindvDirect(ALCcontext
*context
, ALsizei n
,
3417 const ALuint
*sources
) noexcept
3420 throw al::context_error
{AL_INVALID_VALUE
, "Rewinding %d sources", n
};
3421 if(n
<= 0) UNLIKELY
return;
3423 al::span sids
{sources
, static_cast<ALuint
>(n
)};
3424 std::vector
<ALsource
*> extra_sources
;
3425 std::array
<ALsource
*,8> source_storage
;
3426 al::span
<ALsource
*> srchandles
;
3427 if(sids
.size() <= source_storage
.size()) LIKELY
3428 srchandles
= al::span
{source_storage
}.first(sids
.size());
3431 extra_sources
.resize(sids
.size());
3432 srchandles
= extra_sources
;
3435 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3436 std::transform(sids
.cbegin(), sids
.cend(), srchandles
.begin(),
3437 [context
](const ALuint sid
) -> ALsource
*
3439 if(ALsource
*src
{LookupSource(context
, sid
)})
3441 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", sid
};
3444 VoiceChange
*tail
{}, *cur
{};
3445 for(ALsource
*source
: srchandles
)
3447 Voice
*voice
{GetSourceVoice(source
, context
)};
3448 if(source
->state
!= AL_INITIAL
)
3451 cur
= tail
= GetVoiceChanger(context
);
3454 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
3455 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3458 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3459 cur
->mVoice
= voice
;
3460 cur
->mSourceID
= source
->id
;
3461 cur
->mState
= VChangeState::Reset
;
3462 source
->state
= AL_INITIAL
;
3464 source
->Offset
= 0.0;
3465 source
->OffsetType
= AL_NONE
;
3466 source
->VoiceIdx
= InvalidVoiceIndex
;
3469 SendVoiceChanges(context
, tail
);
3471 catch(al::context_error
& e
) {
3472 context
->setError(e
.errorCode(), "%s", e
.what());
3476 AL_API
DECL_FUNC3(void, alSourceQueueBuffers
, ALuint
,source
, ALsizei
,nb
, const ALuint
*,buffers
)
3477 FORCE_ALIGN
void AL_APIENTRY
alSourceQueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3478 ALsizei nb
, const ALuint
*buffers
) noexcept
3481 throw al::context_error
{AL_INVALID_VALUE
, "Queueing %d buffers", nb
};
3482 if(nb
<= 0) UNLIKELY
return;
3484 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3485 ALsource
*source
{LookupSource(context
,src
)};
3487 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3489 /* Can't queue on a Static Source */
3490 if(source
->SourceType
== AL_STATIC
)
3491 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing onto static source %u", src
};
3493 /* Check for a valid Buffer, for its frequency and format */
3494 ALCdevice
*device
{context
->mALDevice
.get()};
3495 ALbuffer
*BufferFmt
{nullptr};
3496 for(auto &item
: source
->mQueue
)
3498 BufferFmt
= item
.mBuffer
;
3499 if(BufferFmt
) break;
3502 std::unique_lock
<std::mutex
> buflock
{device
->BufferLock
};
3503 const auto bids
= al::span
{buffers
, static_cast<ALuint
>(nb
)};
3504 const size_t NewListStart
{source
->mQueue
.size()};
3506 ALbufferQueueItem
*BufferList
{nullptr};
3507 std::for_each(bids
.cbegin(), bids
.cend(),
3508 [source
,device
,&BufferFmt
,&BufferList
](const ALuint bid
)
3510 ALbuffer
*buffer
{bid
? LookupBuffer(device
, bid
) : nullptr};
3512 throw al::context_error
{AL_INVALID_NAME
, "Queueing invalid buffer ID %u", bid
};
3516 if(buffer
->mSampleRate
< 1)
3517 throw al::context_error
{AL_INVALID_OPERATION
,
3518 "Queueing buffer %u with no format", buffer
->id
};
3520 if(buffer
->mCallback
)
3521 throw al::context_error
{AL_INVALID_OPERATION
, "Queueing callback buffer %u",
3524 if(buffer
->MappedAccess
!= 0 && !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
3525 throw al::context_error
{AL_INVALID_OPERATION
,
3526 "Queueing non-persistently mapped buffer %u", buffer
->id
};
3529 source
->mQueue
.emplace_back();
3531 BufferList
= &source
->mQueue
.back();
3534 auto &item
= source
->mQueue
.back();
3535 BufferList
->mNext
.store(&item
, std::memory_order_relaxed
);
3539 BufferList
->mBlockAlign
= buffer
->mBlockAlign
;
3540 BufferList
->mSampleLen
= buffer
->mSampleLen
;
3541 BufferList
->mLoopEnd
= buffer
->mSampleLen
;
3542 BufferList
->mSamples
= buffer
->mData
;
3543 BufferList
->mBuffer
= buffer
;
3544 IncrementRef(buffer
->ref
);
3546 bool fmt_mismatch
{false};
3547 if(BufferFmt
== nullptr)
3551 fmt_mismatch
|= BufferFmt
->mSampleRate
!= buffer
->mSampleRate
;
3552 fmt_mismatch
|= BufferFmt
->mChannels
!= buffer
->mChannels
;
3553 fmt_mismatch
|= BufferFmt
->mType
!= buffer
->mType
;
3554 if(BufferFmt
->isBFormat())
3556 fmt_mismatch
|= BufferFmt
->mAmbiLayout
!= buffer
->mAmbiLayout
;
3557 fmt_mismatch
|= BufferFmt
->mAmbiScaling
!= buffer
->mAmbiScaling
;
3559 fmt_mismatch
|= BufferFmt
->mAmbiOrder
!= buffer
->mAmbiOrder
;
3562 throw al::context_error
{AL_INVALID_OPERATION
,
3563 "Queueing buffer with mismatched format\n"
3564 " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt
->mSampleRate
,
3565 NameFromFormat(BufferFmt
->mType
), NameFromFormat(BufferFmt
->mChannels
),
3566 buffer
->mSampleRate
, NameFromFormat(buffer
->mType
),
3567 NameFromFormat(buffer
->mChannels
)};
3571 /* A buffer failed (invalid ID or format), or there was some other
3572 * unexpected error, so unlock and release each buffer we had.
3574 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3575 for(;iter
!= source
->mQueue
.end();++iter
)
3577 if(ALbuffer
*buf
{iter
->mBuffer
})
3578 DecrementRef(buf
->ref
);
3580 source
->mQueue
.resize(NewListStart
);
3583 /* All buffers good. */
3586 /* Source is now streaming */
3587 source
->SourceType
= AL_STREAMING
;
3589 if(NewListStart
!= 0)
3591 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3592 (iter
-1)->mNext
.store(al::to_address(iter
), std::memory_order_release
);
3595 catch(al::context_error
& e
) {
3596 context
->setError(e
.errorCode(), "%s", e
.what());
3599 AL_API
DECL_FUNC3(void, alSourceUnqueueBuffers
, ALuint
,source
, ALsizei
,nb
, ALuint
*,buffers
)
3600 FORCE_ALIGN
void AL_APIENTRY
alSourceUnqueueBuffersDirect(ALCcontext
*context
, ALuint src
,
3601 ALsizei nb
, ALuint
*buffers
) noexcept
3604 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffers", nb
};
3605 if(nb
<= 0) UNLIKELY
return;
3607 std::lock_guard
<std::mutex
> sourcelock
{context
->mSourceLock
};
3608 ALsource
*source
{LookupSource(context
,src
)};
3610 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", src
};
3612 if(source
->SourceType
!= AL_STREAMING
)
3613 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from a non-streaming source %u",src
};
3615 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing from looping source %u", src
};
3617 /* Make sure enough buffers have been processed to unqueue. */
3618 const al::span bids
{buffers
, static_cast<ALuint
>(nb
)};
3619 size_t processed
{0};
3620 if(source
->state
!= AL_INITIAL
) LIKELY
3622 VoiceBufferItem
*Current
{nullptr};
3623 if(Voice
*voice
{GetSourceVoice(source
, context
)})
3624 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
3625 for(auto &item
: source
->mQueue
)
3627 if(&item
== Current
)
3632 if(processed
< bids
.size())
3633 throw al::context_error
{AL_INVALID_VALUE
, "Unqueueing %d buffer%s (only %zu processed)",
3634 nb
, (nb
==1)?"":"s", processed
};
3636 std::generate(bids
.begin(), bids
.end(), [source
]() noexcept
-> ALuint
3638 auto &head
= source
->mQueue
.front();
3640 if(ALbuffer
*buffer
{head
.mBuffer
})
3643 DecrementRef(buffer
->ref
);
3645 source
->mQueue
.pop_front();
3649 catch(al::context_error
& e
) {
3650 context
->setError(e
.errorCode(), "%s", e
.what());
3654 AL_API
void AL_APIENTRY
alSourceQueueBufferLayersSOFT(ALuint
, ALsizei
, const ALuint
*) noexcept
3656 ContextRef context
{GetContextRef()};
3657 if(!context
) UNLIKELY
return;
3659 context
->setError(AL_INVALID_OPERATION
, "alSourceQueueBufferLayersSOFT not supported");
3663 ALsource::ALsource() noexcept
3666 Direct
.GainHF
= 1.0f
;
3667 Direct
.HFReference
= LowPassFreqRef
;
3668 Direct
.GainLF
= 1.0f
;
3669 Direct
.LFReference
= HighPassFreqRef
;
3670 for(auto &send
: Send
)
3672 send
.Slot
= nullptr;
3675 send
.HFReference
= LowPassFreqRef
;
3677 send
.LFReference
= HighPassFreqRef
;
3681 ALsource::~ALsource()
3683 for(auto &item
: mQueue
)
3685 if(ALbuffer
*buffer
{item
.mBuffer
})
3686 DecrementRef(buffer
->ref
);
3689 auto clear_send
= [](ALsource::SendData
&send
) -> void
3690 { if(send
.Slot
) DecrementRef(send
.Slot
->ref
); };
3691 std::for_each(Send
.begin(), Send
.end(), clear_send
);
3694 void UpdateAllSourceProps(ALCcontext
*context
)
3696 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3697 auto voicelist
= context
->getVoicesSpan();
3699 for(Voice
*voice
: voicelist
)
3701 ALuint sid
{voice
->mSourceID
.load(std::memory_order_acquire
)};
3702 ALsource
*source
{sid
? LookupSource(context
, sid
) : nullptr};
3703 if(source
&& source
->VoiceIdx
== vidx
)
3705 if(std::exchange(source
->mPropsDirty
, false))
3706 UpdateSourceProps(source
, voice
, context
);
3712 void ALsource::SetName(ALCcontext
*context
, ALuint id
, std::string_view name
)
3714 std::lock_guard
<std::mutex
> srclock
{context
->mSourceLock
};
3716 auto source
= LookupSource(context
, id
);
3718 throw al::context_error
{AL_INVALID_NAME
, "Invalid source ID %u", id
};
3720 context
->mSourceNames
.insert_or_assign(id
, name
);
3724 SourceSubList::~SourceSubList()
3729 uint64_t usemask
{~FreeMask
};
3732 const int idx
{al::countr_zero(usemask
)};
3733 usemask
&= ~(1_u64
<< idx
);
3734 std::destroy_at(al::to_address(Sources
->begin() + idx
));
3736 FreeMask
= ~usemask
;
3737 SubListAllocator
{}.deallocate(Sources
, 1);
3743 void ALsource::eaxInitialize(ALCcontext
*context
) noexcept
3745 assert(context
!= nullptr);
3746 mEaxAlContext
= context
;
3748 mEaxPrimaryFxSlotId
= context
->eaxGetPrimaryFxSlotIndex();
3751 eax1_translate(mEax1
.i
, mEax
);
3756 void ALsource::eaxDispatch(const EaxCall
& call
)
3758 call
.is_get() ? eax_get(call
) : eax_set(call
);
3761 ALsource
* ALsource::EaxLookupSource(ALCcontext
& al_context
, ALuint source_id
) noexcept
3763 return LookupSource(&al_context
, source_id
);
3766 [[noreturn
]] void ALsource::eax_fail(const char* message
)
3768 throw Exception
{message
};
3771 [[noreturn
]] void ALsource::eax_fail_unknown_property_id()
3773 eax_fail("Unknown property id.");
3776 [[noreturn
]] void ALsource::eax_fail_unknown_version()
3778 eax_fail("Unknown version.");
3781 [[noreturn
]] void ALsource::eax_fail_unknown_active_fx_slot_id()
3783 eax_fail("Unknown active FX slot ID.");
3786 [[noreturn
]] void ALsource::eax_fail_unknown_receiving_fx_slot_id()
3788 eax_fail("Unknown receiving FX slot ID.");
3791 void ALsource::eax_set_sends_defaults(EaxSends
& sends
, const EaxFxSlotIds
& ids
) noexcept
3793 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
3795 auto& send
= sends
[i
];
3796 send
.guidReceivingFXSlotID
= *(ids
[i
]);
3797 send
.lSend
= EAXSOURCE_DEFAULTSEND
;
3798 send
.lSendHF
= EAXSOURCE_DEFAULTSENDHF
;
3799 send
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3800 send
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3801 send
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3802 send
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3803 send
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3804 send
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3808 void ALsource::eax1_set_defaults(Eax1Props
& props
) noexcept
3810 props
.fMix
= EAX_REVERBMIX_USEDISTANCE
;
3813 void ALsource::eax1_set_defaults() noexcept
3815 eax1_set_defaults(mEax1
.i
);
3819 void ALsource::eax2_set_defaults(Eax2Props
& props
) noexcept
3821 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3822 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3823 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3824 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
3825 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3826 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
3827 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
3828 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3829 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3830 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3831 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3832 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3833 props
.dwFlags
= EAXSOURCE_DEFAULTFLAGS
;
3836 void ALsource::eax2_set_defaults() noexcept
3838 eax2_set_defaults(mEax2
.i
);
3842 void ALsource::eax3_set_defaults(Eax3Props
& props
) noexcept
3844 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
3845 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
3846 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
3847 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
3848 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
3849 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
3850 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
3851 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
3852 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
3853 props
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3854 props
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3855 props
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3856 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
3857 props
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3858 props
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3859 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
3860 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
3861 props
.ulFlags
= EAXSOURCE_DEFAULTFLAGS
;
3864 void ALsource::eax3_set_defaults() noexcept
3866 eax3_set_defaults(mEax3
.i
);
3870 void ALsource::eax4_set_sends_defaults(EaxSends
& sends
) noexcept
3872 eax_set_sends_defaults(sends
, eax4_fx_slot_ids
);
3875 void ALsource::eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS
& slots
) noexcept
3877 slots
= EAX40SOURCE_DEFAULTACTIVEFXSLOTID
;
3880 void ALsource::eax4_set_defaults() noexcept
3882 eax3_set_defaults(mEax4
.i
.source
);
3883 eax4_set_sends_defaults(mEax4
.i
.sends
);
3884 eax4_set_active_fx_slots_defaults(mEax4
.i
.active_fx_slots
);
3888 void ALsource::eax5_set_source_defaults(EAX50SOURCEPROPERTIES
& props
) noexcept
3890 eax3_set_defaults(static_cast<Eax3Props
&>(props
));
3891 props
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3894 void ALsource::eax5_set_sends_defaults(EaxSends
& sends
) noexcept
3896 eax_set_sends_defaults(sends
, eax5_fx_slot_ids
);
3899 void ALsource::eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS
& slots
) noexcept
3901 slots
= EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID
;
3904 void ALsource::eax5_set_speaker_levels_defaults(EaxSpeakerLevels
& speaker_levels
) noexcept
3906 for(size_t i
{0};i
< eax_max_speakers
;++i
)
3908 auto& speaker_level
= speaker_levels
[i
];
3909 speaker_level
.lSpeakerID
= static_cast<long>(EAXSPEAKER_FRONT_LEFT
+ i
);
3910 speaker_level
.lLevel
= EAXSOURCE_DEFAULTSPEAKERLEVEL
;
3914 void ALsource::eax5_set_defaults(Eax5Props
& props
) noexcept
3916 eax5_set_source_defaults(props
.source
);
3917 eax5_set_sends_defaults(props
.sends
);
3918 eax5_set_active_fx_slots_defaults(props
.active_fx_slots
);
3919 eax5_set_speaker_levels_defaults(props
.speaker_levels
);
3922 void ALsource::eax5_set_defaults() noexcept
3924 eax5_set_defaults(mEax5
.i
);
3928 void ALsource::eax_set_defaults() noexcept
3930 eax1_set_defaults();
3931 eax2_set_defaults();
3932 eax3_set_defaults();
3933 eax4_set_defaults();
3934 eax5_set_defaults();
3937 void ALsource::eax1_translate(const Eax1Props
& src
, Eax5Props
& dst
) noexcept
3939 eax5_set_defaults(dst
);
3941 if (src
.fMix
== EAX_REVERBMIX_USEDISTANCE
)
3943 dst
.source
.ulFlags
|= EAXSOURCEFLAGS_ROOMAUTO
;
3944 dst
.sends
[0].lSend
= 0;
3948 dst
.source
.ulFlags
&= ~EAXSOURCEFLAGS_ROOMAUTO
;
3949 dst
.sends
[0].lSend
= std::clamp(static_cast<long>(gain_to_level_mb(src
.fMix
)),
3950 EAXSOURCE_MINSEND
, EAXSOURCE_MAXSEND
);
3954 void ALsource::eax2_translate(const Eax2Props
& src
, Eax5Props
& dst
) noexcept
3958 dst
.source
.lDirect
= src
.lDirect
;
3959 dst
.source
.lDirectHF
= src
.lDirectHF
;
3960 dst
.source
.lRoom
= src
.lRoom
;
3961 dst
.source
.lRoomHF
= src
.lRoomHF
;
3962 dst
.source
.lObstruction
= src
.lObstruction
;
3963 dst
.source
.flObstructionLFRatio
= src
.flObstructionLFRatio
;
3964 dst
.source
.lOcclusion
= src
.lOcclusion
;
3965 dst
.source
.flOcclusionLFRatio
= src
.flOcclusionLFRatio
;
3966 dst
.source
.flOcclusionRoomRatio
= src
.flOcclusionRoomRatio
;
3967 dst
.source
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
3968 dst
.source
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
3969 dst
.source
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
3970 dst
.source
.lOutsideVolumeHF
= src
.lOutsideVolumeHF
;
3971 dst
.source
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
3972 dst
.source
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
3973 dst
.source
.flRoomRolloffFactor
= src
.flRoomRolloffFactor
;
3974 dst
.source
.flAirAbsorptionFactor
= src
.flAirAbsorptionFactor
;
3975 dst
.source
.ulFlags
= src
.dwFlags
;
3976 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3978 // Set everything else to defaults.
3980 eax5_set_sends_defaults(dst
.sends
);
3981 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
3982 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
3985 void ALsource::eax3_translate(const Eax3Props
& src
, Eax5Props
& dst
) noexcept
3989 static_cast<Eax3Props
&>(dst
.source
) = src
;
3990 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
3992 // Set everything else to defaults.
3994 eax5_set_sends_defaults(dst
.sends
);
3995 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
3996 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
3999 void ALsource::eax4_translate(const Eax4Props
& src
, Eax5Props
& dst
) noexcept
4003 static_cast<Eax3Props
&>(dst
.source
) = src
.source
;
4004 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4008 dst
.sends
= src
.sends
;
4010 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4011 dst
.sends
[i
].guidReceivingFXSlotID
= *(eax5_fx_slot_ids
[i
]);
4015 for(size_t i
{0};i
< EAX50_MAX_ACTIVE_FXSLOTS
;++i
)
4017 auto& dst_id
= dst
.active_fx_slots
.guidActiveFXSlots
[i
];
4019 if(i
< EAX40_MAX_ACTIVE_FXSLOTS
)
4021 const auto& src_id
= src
.active_fx_slots
.guidActiveFXSlots
[i
];
4023 if(src_id
== EAX_NULL_GUID
)
4024 dst_id
= EAX_NULL_GUID
;
4025 else if(src_id
== EAX_PrimaryFXSlotID
)
4026 dst_id
= EAX_PrimaryFXSlotID
;
4027 else if(src_id
== EAXPROPERTYID_EAX40_FXSlot0
)
4028 dst_id
= EAXPROPERTYID_EAX50_FXSlot0
;
4029 else if(src_id
== EAXPROPERTYID_EAX40_FXSlot1
)
4030 dst_id
= EAXPROPERTYID_EAX50_FXSlot1
;
4031 else if(src_id
== EAXPROPERTYID_EAX40_FXSlot2
)
4032 dst_id
= EAXPROPERTYID_EAX50_FXSlot2
;
4033 else if(src_id
== EAXPROPERTYID_EAX40_FXSlot3
)
4034 dst_id
= EAXPROPERTYID_EAX50_FXSlot3
;
4036 assert(false && "Unknown active FX slot ID.");
4039 dst_id
= EAX_NULL_GUID
;
4044 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4047 float ALsource::eax_calculate_dst_occlusion_mb(
4048 long src_occlusion_mb
,
4050 float lf_ratio
) noexcept
4052 const auto ratio_1
= path_ratio
+ lf_ratio
- 1.0F
;
4053 const auto ratio_2
= path_ratio
* lf_ratio
;
4054 const auto ratio
= (ratio_2
> ratio_1
) ? ratio_2
: ratio_1
;
4055 const auto dst_occlustion_mb
= static_cast<float>(src_occlusion_mb
) * ratio
;
4056 return dst_occlustion_mb
;
4059 EaxAlLowPassParam
ALsource::eax_create_direct_filter_param() const noexcept
4062 static_cast<float>(mEax
.source
.lDirect
) +
4063 (static_cast<float>(mEax
.source
.lObstruction
) * mEax
.source
.flObstructionLFRatio
) +
4064 eax_calculate_dst_occlusion_mb(
4065 mEax
.source
.lOcclusion
,
4066 mEax
.source
.flOcclusionDirectRatio
,
4067 mEax
.source
.flOcclusionLFRatio
);
4069 const auto has_source_occlusion
= (mEax
.source
.lOcclusion
!= 0);
4072 static_cast<float>(mEax
.source
.lDirectHF
) +
4073 static_cast<float>(mEax
.source
.lObstruction
);
4075 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4077 if(!mEaxActiveFxSlots
[i
])
4080 if(has_source_occlusion
)
4082 const auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4083 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4084 const auto is_environmental_fx
= ((fx_slot_eax
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0);
4085 const auto is_primary
= (mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index());
4086 const auto is_listener_environment
= (is_environmental_fx
&& is_primary
);
4088 if(is_listener_environment
)
4090 gain_mb
+= eax_calculate_dst_occlusion_mb(
4091 mEax
.source
.lOcclusion
,
4092 mEax
.source
.flOcclusionDirectRatio
,
4093 mEax
.source
.flOcclusionLFRatio
);
4095 gain_hf_mb
+= static_cast<float>(mEax
.source
.lOcclusion
) * mEax
.source
.flOcclusionDirectRatio
;
4099 const auto& send
= mEax
.sends
[i
];
4101 if(send
.lOcclusion
!= 0)
4103 gain_mb
+= eax_calculate_dst_occlusion_mb(
4105 send
.flOcclusionDirectRatio
,
4106 send
.flOcclusionLFRatio
);
4108 gain_hf_mb
+= static_cast<float>(send
.lOcclusion
) * send
.flOcclusionDirectRatio
;
4112 return EaxAlLowPassParam
{
4113 level_mb_to_gain(gain_mb
),
4114 std::min(level_mb_to_gain(gain_hf_mb
), 1.0f
)};
4117 EaxAlLowPassParam
ALsource::eax_create_room_filter_param(
4118 const ALeffectslot
& fx_slot
,
4119 const EAXSOURCEALLSENDPROPERTIES
& send
) const noexcept
4121 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4122 const auto is_environmental_fx
= ((fx_slot_eax
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0);
4123 const auto is_primary
= (mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index());
4124 const auto is_listener_environment
= (is_environmental_fx
&& is_primary
);
4126 const auto gain_mb
=
4127 (static_cast<float>(fx_slot_eax
.lOcclusion
) * fx_slot_eax
.flOcclusionLFRatio
) +
4128 static_cast<float>((is_environmental_fx
? mEax
.source
.lRoom
: 0) + send
.lSend
) +
4129 (is_listener_environment
?
4130 eax_calculate_dst_occlusion_mb(
4131 mEax
.source
.lOcclusion
,
4132 mEax
.source
.flOcclusionRoomRatio
,
4133 mEax
.source
.flOcclusionLFRatio
) :
4135 eax_calculate_dst_occlusion_mb(
4137 send
.flOcclusionRoomRatio
,
4138 send
.flOcclusionLFRatio
) +
4139 (is_listener_environment
?
4140 (static_cast<float>(mEax
.source
.lExclusion
) * mEax
.source
.flExclusionLFRatio
) :
4142 (static_cast<float>(send
.lExclusion
) * send
.flExclusionLFRatio
);
4144 const auto gain_hf_mb
=
4145 static_cast<float>(fx_slot_eax
.lOcclusion
) +
4146 static_cast<float>((is_environmental_fx
? mEax
.source
.lRoomHF
: 0) + send
.lSendHF
) +
4147 (is_listener_environment
?
4148 ((static_cast<float>(mEax
.source
.lOcclusion
) * mEax
.source
.flOcclusionRoomRatio
)) :
4150 (static_cast<float>(send
.lOcclusion
) * send
.flOcclusionRoomRatio
) +
4151 (is_listener_environment
?
4152 static_cast<float>(mEax
.source
.lExclusion
+ send
.lExclusion
) :
4155 return EaxAlLowPassParam
{
4156 level_mb_to_gain(gain_mb
),
4157 std::min(level_mb_to_gain(gain_hf_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 case EAXSOURCE_FLAGS
:
4517 eax3_set(call
, props
.source
);
4520 case EAXSOURCE_SENDPARAMETERS
:
4521 eax5_defer_sends
<Eax5SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4524 case EAXSOURCE_ALLSENDPARAMETERS
:
4525 eax5_defer_sends
<Eax5AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4528 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4529 eax5_defer_sends
<Eax5OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4532 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4533 eax5_defer_sends
<Eax5ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4536 case EAXSOURCE_ACTIVEFXSLOTID
:
4537 eax5_defer_active_fx_slot_id(call
, al::span
{props
.active_fx_slots
.guidActiveFXSlots
});
4540 case EAXSOURCE_MACROFXFACTOR
:
4541 eax_defer
<Eax5SourceMacroFXFactorValidator
>(call
, props
.source
.flMacroFXFactor
);
4544 case EAXSOURCE_SPEAKERLEVELS
:
4545 eax5_defer_speaker_levels(call
, props
.speaker_levels
);
4548 case EAXSOURCE_ALL2DPARAMETERS
:
4549 eax5_defer_all_2d(call
, props
.source
);
4553 eax_fail_unknown_property_id();
4557 void ALsource::eax_set(const EaxCall
& call
)
4559 const auto eax_version
= call
.get_version();
4562 case 1: eax1_set(call
, mEax1
.d
); break;
4563 case 2: eax2_set(call
, mEax2
.d
); break;
4564 case 3: eax3_set(call
, mEax3
.d
); break;
4565 case 4: eax4_set(call
, mEax4
.d
); break;
4566 case 5: eax5_set(call
, mEax5
.d
); break;
4567 default: eax_fail_unknown_property_id();
4570 mEaxVersion
= eax_version
;
4573 void ALsource::eax_get_active_fx_slot_id(const EaxCall
& call
, const al::span
<const GUID
> src_ids
)
4575 assert(src_ids
.size()==EAX40_MAX_ACTIVE_FXSLOTS
|| src_ids
.size()==EAX50_MAX_ACTIVE_FXSLOTS
);
4576 const auto dst_ids
= call
.get_values
<GUID
>(src_ids
.size());
4577 std::uninitialized_copy_n(src_ids
.begin(), dst_ids
.size(), dst_ids
.begin());
4580 void ALsource::eax1_get(const EaxCall
& call
, const Eax1Props
& props
)
4582 switch (call
.get_property_id()) {
4583 case DSPROPERTY_EAXBUFFER_ALL
:
4584 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4585 call
.set_value
<Exception
>(props
.fMix
);
4589 eax_fail_unknown_property_id();
4593 void ALsource::eax2_get(const EaxCall
& call
, const Eax2Props
& props
)
4595 switch (call
.get_property_id()) {
4596 case DSPROPERTY_EAX20BUFFER_NONE
:
4599 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4600 call
.set_value
<Exception
>(props
);
4603 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4604 call
.set_value
<Exception
>(props
.lDirect
);
4607 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4608 call
.set_value
<Exception
>(props
.lDirectHF
);
4611 case DSPROPERTY_EAX20BUFFER_ROOM
:
4612 call
.set_value
<Exception
>(props
.lRoom
);
4615 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4616 call
.set_value
<Exception
>(props
.lRoomHF
);
4619 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4620 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4623 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4624 call
.set_value
<Exception
>(props
.lObstruction
);
4627 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4628 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4631 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4632 call
.set_value
<Exception
>(props
.lOcclusion
);
4635 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4636 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4639 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4640 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4643 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4644 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4647 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4648 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4651 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4652 call
.set_value
<Exception
>(props
.dwFlags
);
4656 eax_fail_unknown_property_id();
4660 void ALsource::eax3_get_obstruction(const EaxCall
& call
, const Eax3Props
& props
)
4662 const auto& subprops
= reinterpret_cast<const EAXOBSTRUCTIONPROPERTIES
&>(props
.lObstruction
);
4663 call
.set_value
<Exception
>(subprops
);
4666 void ALsource::eax3_get_occlusion(const EaxCall
& call
, const Eax3Props
& props
)
4668 const auto& subprops
= reinterpret_cast<const EAXOCCLUSIONPROPERTIES
&>(props
.lOcclusion
);
4669 call
.set_value
<Exception
>(subprops
);
4672 void ALsource::eax3_get_exclusion(const EaxCall
& call
, const Eax3Props
& props
)
4674 const auto& subprops
= reinterpret_cast<const EAXEXCLUSIONPROPERTIES
&>(props
.lExclusion
);
4675 call
.set_value
<Exception
>(subprops
);
4678 void ALsource::eax3_get(const EaxCall
& call
, const Eax3Props
& props
)
4680 switch (call
.get_property_id()) {
4681 case EAXSOURCE_NONE
:
4684 case EAXSOURCE_ALLPARAMETERS
:
4685 call
.set_value
<Exception
>(props
);
4688 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4689 eax3_get_obstruction(call
, props
);
4692 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4693 eax3_get_occlusion(call
, props
);
4696 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4697 eax3_get_exclusion(call
, props
);
4700 case EAXSOURCE_DIRECT
:
4701 call
.set_value
<Exception
>(props
.lDirect
);
4704 case EAXSOURCE_DIRECTHF
:
4705 call
.set_value
<Exception
>(props
.lDirectHF
);
4708 case EAXSOURCE_ROOM
:
4709 call
.set_value
<Exception
>(props
.lRoom
);
4712 case EAXSOURCE_ROOMHF
:
4713 call
.set_value
<Exception
>(props
.lRoomHF
);
4716 case EAXSOURCE_OBSTRUCTION
:
4717 call
.set_value
<Exception
>(props
.lObstruction
);
4720 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4721 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4724 case EAXSOURCE_OCCLUSION
:
4725 call
.set_value
<Exception
>(props
.lOcclusion
);
4728 case EAXSOURCE_OCCLUSIONLFRATIO
:
4729 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4732 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4733 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4736 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4737 call
.set_value
<Exception
>(props
.flOcclusionDirectRatio
);
4740 case EAXSOURCE_EXCLUSION
:
4741 call
.set_value
<Exception
>(props
.lExclusion
);
4744 case EAXSOURCE_EXCLUSIONLFRATIO
:
4745 call
.set_value
<Exception
>(props
.flExclusionLFRatio
);
4748 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4749 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4752 case EAXSOURCE_DOPPLERFACTOR
:
4753 call
.set_value
<Exception
>(props
.flDopplerFactor
);
4756 case EAXSOURCE_ROLLOFFFACTOR
:
4757 call
.set_value
<Exception
>(props
.flRolloffFactor
);
4760 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4761 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4764 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4765 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4768 case EAXSOURCE_FLAGS
:
4769 call
.set_value
<Exception
>(props
.ulFlags
);
4773 eax_fail_unknown_property_id();
4777 void ALsource::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
4779 switch (call
.get_property_id()) {
4780 case EAXSOURCE_NONE
:
4783 case EAXSOURCE_ALLPARAMETERS
:
4784 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4785 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4786 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4787 case EAXSOURCE_DIRECT
:
4788 case EAXSOURCE_DIRECTHF
:
4789 case EAXSOURCE_ROOM
:
4790 case EAXSOURCE_ROOMHF
:
4791 case EAXSOURCE_OBSTRUCTION
:
4792 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4793 case EAXSOURCE_OCCLUSION
:
4794 case EAXSOURCE_OCCLUSIONLFRATIO
:
4795 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4796 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4797 case EAXSOURCE_EXCLUSION
:
4798 case EAXSOURCE_EXCLUSIONLFRATIO
:
4799 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4800 case EAXSOURCE_DOPPLERFACTOR
:
4801 case EAXSOURCE_ROLLOFFFACTOR
:
4802 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4803 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4804 case EAXSOURCE_FLAGS
:
4805 eax3_get(call
, props
.source
);
4808 case EAXSOURCE_SENDPARAMETERS
:
4809 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4812 case EAXSOURCE_ALLSENDPARAMETERS
:
4813 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4816 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4817 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4820 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4821 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4824 case EAXSOURCE_ACTIVEFXSLOTID
:
4825 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4829 eax_fail_unknown_property_id();
4833 void ALsource::eax5_get_all_2d(const EaxCall
& call
, const EAX50SOURCEPROPERTIES
& props
)
4835 auto& subprops
= call
.get_value
<Exception
, EAXSOURCE2DPROPERTIES
>();
4836 subprops
.lDirect
= props
.lDirect
;
4837 subprops
.lDirectHF
= props
.lDirectHF
;
4838 subprops
.lRoom
= props
.lRoom
;
4839 subprops
.lRoomHF
= props
.lRoomHF
;
4840 subprops
.ulFlags
= props
.ulFlags
;
4843 void ALsource::eax5_get_speaker_levels(const EaxCall
& call
, const EaxSpeakerLevels
& props
)
4845 const auto subprops
= call
.get_values
<EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4846 std::uninitialized_copy_n(props
.cbegin(), subprops
.size(), subprops
.begin());
4849 void ALsource::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
4851 switch (call
.get_property_id()) {
4852 case EAXSOURCE_NONE
:
4855 case EAXSOURCE_ALLPARAMETERS
:
4856 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4857 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4858 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4859 case EAXSOURCE_DIRECT
:
4860 case EAXSOURCE_DIRECTHF
:
4861 case EAXSOURCE_ROOM
:
4862 case EAXSOURCE_ROOMHF
:
4863 case EAXSOURCE_OBSTRUCTION
:
4864 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4865 case EAXSOURCE_OCCLUSION
:
4866 case EAXSOURCE_OCCLUSIONLFRATIO
:
4867 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4868 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4869 case EAXSOURCE_EXCLUSION
:
4870 case EAXSOURCE_EXCLUSIONLFRATIO
:
4871 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4872 case EAXSOURCE_DOPPLERFACTOR
:
4873 case EAXSOURCE_ROLLOFFFACTOR
:
4874 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4875 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4876 case EAXSOURCE_FLAGS
:
4877 eax3_get(call
, props
.source
);
4880 case EAXSOURCE_SENDPARAMETERS
:
4881 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4884 case EAXSOURCE_ALLSENDPARAMETERS
:
4885 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4888 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4889 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4892 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4893 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4896 case EAXSOURCE_ACTIVEFXSLOTID
:
4897 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4900 case EAXSOURCE_MACROFXFACTOR
:
4901 call
.set_value
<Exception
>(props
.source
.flMacroFXFactor
);
4904 case EAXSOURCE_SPEAKERLEVELS
:
4905 call
.set_value
<Exception
>(props
.speaker_levels
);
4908 case EAXSOURCE_ALL2DPARAMETERS
:
4909 eax5_get_all_2d(call
, props
.source
);
4913 eax_fail_unknown_property_id();
4917 void ALsource::eax_get(const EaxCall
& call
)
4919 switch (call
.get_version()) {
4920 case 1: eax1_get(call
, mEax1
.i
); break;
4921 case 2: eax2_get(call
, mEax2
.i
); break;
4922 case 3: eax3_get(call
, mEax3
.i
); break;
4923 case 4: eax4_get(call
, mEax4
.i
); break;
4924 case 5: eax5_get(call
, mEax5
.i
); break;
4925 default: eax_fail_unknown_version();
4929 void ALsource::eax_set_al_source_send(ALeffectslot
*slot
, size_t sendidx
, const EaxAlLowPassParam
&filter
)
4931 if(sendidx
>= EAX_MAX_FXSLOTS
)
4934 auto &send
= Send
[sendidx
];
4935 send
.Gain
= filter
.gain
;
4936 send
.GainHF
= filter
.gain_hf
;
4937 send
.HFReference
= LowPassFreqRef
;
4939 send
.LFReference
= HighPassFreqRef
;
4942 IncrementRef(slot
->ref
);
4943 if(auto *oldslot
= send
.Slot
)
4944 DecrementRef(oldslot
->ref
);
4950 void ALsource::eax_commit_active_fx_slots()
4952 // Clear all slots to an inactive state.
4953 mEaxActiveFxSlots
.fill(false);
4955 // Mark the set slots as active.
4956 for(const auto& slot_id
: mEax
.active_fx_slots
.guidActiveFXSlots
)
4958 if(slot_id
== EAX_NULL_GUID
)
4961 else if(slot_id
== EAX_PrimaryFXSlotID
)
4963 // Mark primary FX slot as active.
4964 if(mEaxPrimaryFxSlotId
.has_value())
4965 mEaxActiveFxSlots
[*mEaxPrimaryFxSlotId
] = true;
4967 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot0
)
4968 mEaxActiveFxSlots
[0] = true;
4969 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot1
)
4970 mEaxActiveFxSlots
[1] = true;
4971 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot2
)
4972 mEaxActiveFxSlots
[2] = true;
4973 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot3
)
4974 mEaxActiveFxSlots
[3] = true;
4977 // Deactivate EFX auxiliary effect slots for inactive slots. Active slots
4978 // will be updated with the room filters.
4979 for(size_t i
{0};i
< EAX_MAX_FXSLOTS
;++i
)
4981 if(!mEaxActiveFxSlots
[i
])
4982 eax_set_al_source_send(nullptr, i
, EaxAlLowPassParam
{1.0f
, 1.0f
});
4986 void ALsource::eax_commit_filters()
4988 eax_update_direct_filter();
4989 eax_update_room_filters();
4992 void ALsource::eaxCommit()
4994 const auto primary_fx_slot_id
= mEaxAlContext
->eaxGetPrimaryFxSlotIndex();
4995 const auto is_primary_fx_slot_id_changed
= (mEaxPrimaryFxSlotId
!= primary_fx_slot_id
);
4997 if(!mEaxChanged
&& !is_primary_fx_slot_id_changed
)
5000 mEaxPrimaryFxSlotId
= primary_fx_slot_id
;
5001 mEaxChanged
= false;
5007 eax1_translate(mEax1
.i
, mEax
);
5011 eax2_translate(mEax2
.i
, mEax
);
5015 eax3_translate(mEax3
.i
, mEax
);
5019 eax4_translate(mEax4
.i
, mEax
);
5027 eax_set_efx_outer_gain_hf();
5028 eax_set_efx_doppler_factor();
5029 eax_set_efx_rolloff_factor();
5030 eax_set_efx_room_rolloff_factor();
5031 eax_set_efx_air_absorption_factor();
5032 eax_set_efx_dry_gain_hf_auto();
5033 eax_set_efx_wet_gain_auto();
5034 eax_set_efx_wet_gain_hf_auto();
5036 eax_commit_active_fx_slots();
5037 eax_commit_filters();
5040 #endif // ALSOFT_EAX