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
52 #include "alc/backends/base.h"
53 #include "alc/context.h"
54 #include "alc/device.h"
55 #include "alc/inprogext.h"
57 #include "alnumeric.h"
58 #include "aloptional.h"
61 #include "auxeffectslot.h"
63 #include "core/ambidefs.h"
64 #include "core/bformatdec.h"
65 #include "core/except.h"
66 #include "core/filters/nfc.h"
67 #include "core/filters/splitter.h"
68 #include "core/logging.h"
69 #include "core/voice_change.h"
72 #include "opthelpers.h"
73 #include "ringbuffer.h"
80 bool sBufferSubDataCompat
{false};
84 using namespace std::placeholders
;
85 using std::chrono::nanoseconds
;
87 Voice
*GetSourceVoice(ALsource
*source
, ALCcontext
*context
)
89 auto voicelist
= context
->getVoicesSpan();
90 ALuint idx
{source
->VoiceIdx
};
91 if(idx
< voicelist
.size())
93 ALuint sid
{source
->id
};
94 Voice
*voice
= voicelist
[idx
];
95 if(voice
->mSourceID
.load(std::memory_order_acquire
) == sid
)
98 source
->VoiceIdx
= INVALID_VOICE_IDX
;
103 void UpdateSourceProps(const ALsource
*source
, Voice
*voice
, ALCcontext
*context
)
105 /* Get an unused property container, or allocate a new one as needed. */
106 VoicePropsItem
*props
{context
->mFreeVoiceProps
.load(std::memory_order_acquire
)};
109 context
->allocVoiceProps();
110 props
= context
->mFreeVoiceProps
.load(std::memory_order_acquire
);
112 VoicePropsItem
*next
;
114 next
= props
->next
.load(std::memory_order_relaxed
);
115 } while(context
->mFreeVoiceProps
.compare_exchange_weak(props
, next
,
116 std::memory_order_acq_rel
, std::memory_order_acquire
) == false);
118 props
->Pitch
= source
->Pitch
;
119 props
->Gain
= source
->Gain
;
120 props
->OuterGain
= source
->OuterGain
;
121 props
->MinGain
= source
->MinGain
;
122 props
->MaxGain
= source
->MaxGain
;
123 props
->InnerAngle
= source
->InnerAngle
;
124 props
->OuterAngle
= source
->OuterAngle
;
125 props
->RefDistance
= source
->RefDistance
;
126 props
->MaxDistance
= source
->MaxDistance
;
127 props
->RolloffFactor
= source
->RolloffFactor
129 + source
->RolloffFactor2
132 props
->Position
= source
->Position
;
133 props
->Velocity
= source
->Velocity
;
134 props
->Direction
= source
->Direction
;
135 props
->OrientAt
= source
->OrientAt
;
136 props
->OrientUp
= source
->OrientUp
;
137 props
->HeadRelative
= source
->HeadRelative
;
138 props
->mDistanceModel
= source
->mDistanceModel
;
139 props
->mResampler
= source
->mResampler
;
140 props
->DirectChannels
= source
->DirectChannels
;
141 props
->mSpatializeMode
= source
->mSpatialize
;
143 props
->DryGainHFAuto
= source
->DryGainHFAuto
;
144 props
->WetGainAuto
= source
->WetGainAuto
;
145 props
->WetGainHFAuto
= source
->WetGainHFAuto
;
146 props
->OuterGainHF
= source
->OuterGainHF
;
148 props
->AirAbsorptionFactor
= source
->AirAbsorptionFactor
;
149 props
->RoomRolloffFactor
= source
->RoomRolloffFactor
;
150 props
->DopplerFactor
= source
->DopplerFactor
;
152 props
->StereoPan
= source
->StereoPan
;
154 props
->Radius
= source
->Radius
;
155 props
->EnhWidth
= source
->EnhWidth
;
157 props
->Direct
.Gain
= source
->Direct
.Gain
;
158 props
->Direct
.GainHF
= source
->Direct
.GainHF
;
159 props
->Direct
.HFReference
= source
->Direct
.HFReference
;
160 props
->Direct
.GainLF
= source
->Direct
.GainLF
;
161 props
->Direct
.LFReference
= source
->Direct
.LFReference
;
163 auto copy_send
= [](const ALsource::SendData
&srcsend
) noexcept
-> VoiceProps::SendData
165 VoiceProps::SendData ret
{};
166 ret
.Slot
= srcsend
.Slot
? srcsend
.Slot
->mSlot
: nullptr;
167 ret
.Gain
= srcsend
.Gain
;
168 ret
.GainHF
= srcsend
.GainHF
;
169 ret
.HFReference
= srcsend
.HFReference
;
170 ret
.GainLF
= srcsend
.GainLF
;
171 ret
.LFReference
= srcsend
.LFReference
;
174 std::transform(source
->Send
.cbegin(), source
->Send
.cend(), props
->Send
, copy_send
);
175 if(!props
->Send
[0].Slot
&& context
->mDefaultSlot
)
176 props
->Send
[0].Slot
= context
->mDefaultSlot
->mSlot
;
178 /* Set the new container for updating internal parameters. */
179 props
= voice
->mUpdate
.exchange(props
, std::memory_order_acq_rel
);
182 /* If there was an unused update container, put it back in the
185 AtomicReplaceHead(context
->mFreeVoiceProps
, props
);
189 /* GetSourceSampleOffset
191 * Gets the current read offset for the given Source, in 32.32 fixed-point
192 * samples. The offset is relative to the start of the queue (not the start of
193 * the current buffer).
195 int64_t GetSourceSampleOffset(ALsource
*Source
, ALCcontext
*context
, nanoseconds
*clocktime
)
197 ALCdevice
*device
{context
->mALDevice
.get()};
198 const VoiceBufferItem
*Current
{};
204 refcount
= device
->waitForMix();
205 *clocktime
= GetDeviceClockTime(device
);
206 voice
= GetSourceVoice(Source
, context
);
209 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
211 readPos
= int64_t{voice
->mPosition
.load(std::memory_order_relaxed
)} << MixerFracBits
;
212 readPos
+= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
214 std::atomic_thread_fence(std::memory_order_acquire
);
215 } while(refcount
!= device
->MixCount
.load(std::memory_order_relaxed
));
220 for(auto &item
: Source
->mQueue
)
222 if(&item
== Current
) break;
223 readPos
+= int64_t{item
.mSampleLen
} << MixerFracBits
;
225 if(readPos
> std::numeric_limits
<int64_t>::max() >> (32-MixerFracBits
))
226 return std::numeric_limits
<int64_t>::max();
227 return readPos
<< (32-MixerFracBits
);
230 /* GetSourceSecOffset
232 * Gets the current read offset for the given Source, in seconds. The offset is
233 * relative to the start of the queue (not the start of the current buffer).
235 double GetSourceSecOffset(ALsource
*Source
, ALCcontext
*context
, nanoseconds
*clocktime
)
237 ALCdevice
*device
{context
->mALDevice
.get()};
238 const VoiceBufferItem
*Current
{};
244 refcount
= device
->waitForMix();
245 *clocktime
= GetDeviceClockTime(device
);
246 voice
= GetSourceVoice(Source
, context
);
249 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
251 readPos
= int64_t{voice
->mPosition
.load(std::memory_order_relaxed
)} << MixerFracBits
;
252 readPos
+= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
254 std::atomic_thread_fence(std::memory_order_acquire
);
255 } while(refcount
!= device
->MixCount
.load(std::memory_order_relaxed
));
260 const ALbuffer
*BufferFmt
{nullptr};
261 auto BufferList
= Source
->mQueue
.cbegin();
262 while(BufferList
!= Source
->mQueue
.cend() && al::to_address(BufferList
) != Current
)
264 if(!BufferFmt
) BufferFmt
= BufferList
->mBuffer
;
265 readPos
+= int64_t{BufferList
->mSampleLen
} << MixerFracBits
;
268 while(BufferList
!= Source
->mQueue
.cend() && !BufferFmt
)
270 BufferFmt
= BufferList
->mBuffer
;
273 ASSUME(BufferFmt
!= nullptr);
275 return static_cast<double>(readPos
) / double{MixerFracOne
} / BufferFmt
->mSampleRate
;
280 * Gets the current read offset for the given Source, in the appropriate format
281 * (Bytes, Samples or Seconds). The offset is relative to the start of the
282 * queue (not the start of the current buffer).
284 double GetSourceOffset(ALsource
*Source
, ALenum name
, ALCcontext
*context
)
286 ALCdevice
*device
{context
->mALDevice
.get()};
287 const VoiceBufferItem
*Current
{};
294 refcount
= device
->waitForMix();
295 voice
= GetSourceVoice(Source
, context
);
298 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
300 readPos
= voice
->mPosition
.load(std::memory_order_relaxed
);
301 readPosFrac
= voice
->mPositionFrac
.load(std::memory_order_relaxed
);
303 std::atomic_thread_fence(std::memory_order_acquire
);
304 } while(refcount
!= device
->MixCount
.load(std::memory_order_relaxed
));
309 const ALbuffer
*BufferFmt
{nullptr};
310 auto BufferList
= Source
->mQueue
.cbegin();
311 while(BufferList
!= Source
->mQueue
.cend() && al::to_address(BufferList
) != Current
)
313 if(!BufferFmt
) BufferFmt
= BufferList
->mBuffer
;
314 readPos
+= BufferList
->mSampleLen
;
317 while(BufferList
!= Source
->mQueue
.cend() && !BufferFmt
)
319 BufferFmt
= BufferList
->mBuffer
;
322 ASSUME(BufferFmt
!= nullptr);
328 offset
= static_cast<double>(readPos
) + readPosFrac
/double{MixerFracOne
};
329 offset
/= BufferFmt
->mSampleRate
;
332 case AL_SAMPLE_OFFSET
:
333 offset
= static_cast<double>(readPos
) + readPosFrac
/double{MixerFracOne
};
337 const ALuint BlockSamples
{BufferFmt
->mBlockAlign
};
338 const ALuint BlockSize
{BufferFmt
->blockSizeFromFmt()};
340 /* Round down to the block boundary. */
341 offset
= static_cast<double>(readPos
/ BlockSamples
) * BlockSize
;
349 * Gets the length of the given Source's buffer queue, in the appropriate
350 * format (Bytes, Samples or Seconds).
352 double GetSourceLength(const ALsource
*source
, ALenum name
)
355 const ALbuffer
*BufferFmt
{nullptr};
356 for(auto &listitem
: source
->mQueue
)
359 BufferFmt
= listitem
.mBuffer
;
360 length
+= listitem
.mSampleLen
;
365 ASSUME(BufferFmt
!= nullptr);
368 case AL_SEC_LENGTH_SOFT
:
369 return static_cast<double>(length
) / BufferFmt
->mSampleRate
;
371 case AL_SAMPLE_LENGTH_SOFT
:
372 return static_cast<double>(length
);
374 case AL_BYTE_LENGTH_SOFT
:
375 const ALuint BlockSamples
{BufferFmt
->mBlockAlign
};
376 const ALuint BlockSize
{BufferFmt
->blockSizeFromFmt()};
378 /* Round down to the block boundary. */
379 return static_cast<double>(length
/ BlockSamples
) * BlockSize
;
388 ALbufferQueueItem
*bufferitem
;
394 * Retrieves the voice position, fixed-point fraction, and bufferlist item
395 * using the givem offset type and offset. If the offset is out of range,
396 * returns an empty optional.
398 al::optional
<VoicePos
> GetSampleOffset(al::deque
<ALbufferQueueItem
> &BufferList
, ALenum OffsetType
,
401 /* Find the first valid Buffer in the Queue */
402 const ALbuffer
*BufferFmt
{nullptr};
403 for(auto &item
: BufferList
)
405 BufferFmt
= item
.mBuffer
;
408 if(!BufferFmt
) UNLIKELY
411 /* Get sample frame offset */
414 double dbloff
, dblfrac
;
418 dblfrac
= std::modf(Offset
*BufferFmt
->mSampleRate
, &dbloff
);
421 /* If there's a negative fraction, reduce the offset to "floor" it,
422 * and convert the fraction to a percentage to the next value (e.g.
423 * -2.75 -> -3 + 0.25).
428 offset
= static_cast<int64_t>(dbloff
);
429 frac
= static_cast<uint
>(mind(dblfrac
*MixerFracOne
, MixerFracOne
-1.0));
432 case AL_SAMPLE_OFFSET
:
433 dblfrac
= std::modf(Offset
, &dbloff
);
439 offset
= static_cast<int64_t>(dbloff
);
440 frac
= static_cast<uint
>(mind(dblfrac
*MixerFracOne
, MixerFracOne
-1.0));
444 /* Determine the ByteOffset (and ensure it is block aligned) */
445 Offset
= std::floor(Offset
/ BufferFmt
->blockSizeFromFmt());
446 offset
= static_cast<int64_t>(Offset
) * BufferFmt
->mBlockAlign
;
451 /* Find the bufferlist item this offset belongs to. */
454 if(offset
< std::numeric_limits
<int>::min())
456 return VoicePos
{static_cast<int>(offset
), frac
, &BufferList
.front()};
459 if(BufferFmt
->mCallback
)
462 int64_t totalBufferLen
{0};
463 for(auto &item
: BufferList
)
465 if(totalBufferLen
> offset
)
467 if(item
.mSampleLen
> offset
-totalBufferLen
)
469 /* Offset is in this buffer */
470 return VoicePos
{static_cast<int>(offset
-totalBufferLen
), frac
, &item
};
472 totalBufferLen
+= item
.mSampleLen
;
475 /* Offset is out of range of the queue */
480 void InitVoice(Voice
*voice
, ALsource
*source
, ALbufferQueueItem
*BufferList
, ALCcontext
*context
,
483 voice
->mLoopBuffer
.store(source
->Looping
? &source
->mQueue
.front() : nullptr,
484 std::memory_order_relaxed
);
486 ALbuffer
*buffer
{BufferList
->mBuffer
};
487 voice
->mFrequency
= buffer
->mSampleRate
;
488 voice
->mFmtChannels
=
489 (buffer
->mChannels
== FmtStereo
&& source
->mStereoMode
== SourceStereo::Enhanced
) ?
490 FmtSuperStereo
: buffer
->mChannels
;
491 voice
->mFmtType
= buffer
->mType
;
492 voice
->mFrameStep
= buffer
->channelsFromFmt();
493 voice
->mBytesPerBlock
= buffer
->blockSizeFromFmt();
494 voice
->mSamplesPerBlock
= buffer
->mBlockAlign
;
495 voice
->mAmbiLayout
= IsUHJ(voice
->mFmtChannels
) ? AmbiLayout::FuMa
: buffer
->mAmbiLayout
;
496 voice
->mAmbiScaling
= IsUHJ(voice
->mFmtChannels
) ? AmbiScaling::UHJ
: buffer
->mAmbiScaling
;
497 voice
->mAmbiOrder
= (voice
->mFmtChannels
== FmtSuperStereo
) ? 1 : buffer
->mAmbiOrder
;
499 if(buffer
->mCallback
) voice
->mFlags
.set(VoiceIsCallback
);
500 else if(source
->SourceType
== AL_STATIC
) voice
->mFlags
.set(VoiceIsStatic
);
501 voice
->mNumCallbackBlocks
= 0;
502 voice
->mCallbackBlockBase
= 0;
504 voice
->prepare(device
);
506 source
->mPropsDirty
= false;
507 UpdateSourceProps(source
, voice
, context
);
509 voice
->mSourceID
.store(source
->id
, std::memory_order_release
);
513 VoiceChange
*GetVoiceChanger(ALCcontext
*ctx
)
515 VoiceChange
*vchg
{ctx
->mVoiceChangeTail
};
516 if(vchg
== ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)) UNLIKELY
518 ctx
->allocVoiceChanges();
519 vchg
= ctx
->mVoiceChangeTail
;
522 ctx
->mVoiceChangeTail
= vchg
->mNext
.exchange(nullptr, std::memory_order_relaxed
);
527 void SendVoiceChanges(ALCcontext
*ctx
, VoiceChange
*tail
)
529 ALCdevice
*device
{ctx
->mALDevice
.get()};
531 VoiceChange
*oldhead
{ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)};
532 while(VoiceChange
*next
{oldhead
->mNext
.load(std::memory_order_relaxed
)})
534 oldhead
->mNext
.store(tail
, std::memory_order_release
);
536 const bool connected
{device
->Connected
.load(std::memory_order_acquire
)};
537 device
->waitForMix();
538 if(!connected
) UNLIKELY
540 if(ctx
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
542 /* If the device is disconnected and voices are stopped, just
543 * ignore all pending changes.
545 VoiceChange
*cur
{ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
)};
546 while(VoiceChange
*next
{cur
->mNext
.load(std::memory_order_acquire
)})
549 if(Voice
*voice
{cur
->mVoice
})
550 voice
->mSourceID
.store(0, std::memory_order_relaxed
);
552 ctx
->mCurrentVoiceChange
.store(cur
, std::memory_order_release
);
558 bool SetVoiceOffset(Voice
*oldvoice
, const VoicePos
&vpos
, ALsource
*source
, ALCcontext
*context
,
561 /* First, get a free voice to start at the new offset. */
562 auto voicelist
= context
->getVoicesSpan();
565 for(Voice
*voice
: voicelist
)
567 if(voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
568 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
569 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false)
576 if(!newvoice
) UNLIKELY
578 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
579 if(allvoices
.size() == voicelist
.size())
580 context
->allocVoices(1);
581 context
->mActiveVoiceCount
.fetch_add(1, std::memory_order_release
);
582 voicelist
= context
->getVoicesSpan();
585 for(Voice
*voice
: voicelist
)
587 if(voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
588 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
589 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false)
596 ASSUME(newvoice
!= nullptr);
599 /* Initialize the new voice and set its starting offset.
600 * TODO: It might be better to have the VoiceChange processing copy the old
601 * voice's mixing parameters (and pending update) insead of initializing it
602 * all here. This would just need to set the minimum properties to link the
603 * voice to the source and its position-dependent properties (including the
606 newvoice
->mPlayState
.store(Voice::Pending
, std::memory_order_relaxed
);
607 newvoice
->mPosition
.store(vpos
.pos
, std::memory_order_relaxed
);
608 newvoice
->mPositionFrac
.store(vpos
.frac
, std::memory_order_relaxed
);
609 newvoice
->mCurrentBuffer
.store(vpos
.bufferitem
, std::memory_order_relaxed
);
610 newvoice
->mStartTime
= oldvoice
->mStartTime
;
611 newvoice
->mFlags
.reset();
612 if(vpos
.pos
> 0 || (vpos
.pos
== 0 && vpos
.frac
> 0)
613 || vpos
.bufferitem
!= &source
->mQueue
.front())
614 newvoice
->mFlags
.set(VoiceIsFading
);
615 InitVoice(newvoice
, source
, vpos
.bufferitem
, context
, device
);
616 source
->VoiceIdx
= vidx
;
618 /* Set the old voice as having a pending change, and send it off with the
619 * new one with a new offset voice change.
621 oldvoice
->mPendingChange
.store(true, std::memory_order_relaxed
);
623 VoiceChange
*vchg
{GetVoiceChanger(context
)};
624 vchg
->mOldVoice
= oldvoice
;
625 vchg
->mVoice
= newvoice
;
626 vchg
->mSourceID
= source
->id
;
627 vchg
->mState
= VChangeState::Restart
;
628 SendVoiceChanges(context
, vchg
);
630 /* If the old voice still has a sourceID, it's still active and the change-
631 * over will work on the next update.
633 if(oldvoice
->mSourceID
.load(std::memory_order_acquire
) != 0u) LIKELY
636 /* Otherwise, if the new voice's state is not pending, the change-over
639 if(newvoice
->mPlayState
.load(std::memory_order_acquire
) != Voice::Pending
)
642 /* Otherwise, wait for any current mix to finish and check one last time. */
643 device
->waitForMix();
644 if(newvoice
->mPlayState
.load(std::memory_order_acquire
) != Voice::Pending
)
646 /* The change-over failed because the old voice stopped before the new
647 * voice could start at the new offset. Let go of the new voice and have
648 * the caller store the source offset since it's stopped.
650 newvoice
->mCurrentBuffer
.store(nullptr, std::memory_order_relaxed
);
651 newvoice
->mLoopBuffer
.store(nullptr, std::memory_order_relaxed
);
652 newvoice
->mSourceID
.store(0u, std::memory_order_relaxed
);
653 newvoice
->mPlayState
.store(Voice::Stopped
, std::memory_order_relaxed
);
659 * Returns if the last known state for the source was playing or paused. Does
660 * not sync with the mixer voice.
662 inline bool IsPlayingOrPaused(ALsource
*source
)
663 { return source
->state
== AL_PLAYING
|| source
->state
== AL_PAUSED
; }
666 * Returns an updated source state using the matching voice's status (or lack
669 inline ALenum
GetSourceState(ALsource
*source
, Voice
*voice
)
671 if(!voice
&& source
->state
== AL_PLAYING
)
672 source
->state
= AL_STOPPED
;
673 return source
->state
;
677 bool EnsureSources(ALCcontext
*context
, size_t needed
)
679 size_t count
{std::accumulate(context
->mSourceList
.cbegin(), context
->mSourceList
.cend(),
681 [](size_t cur
, const SourceSubList
&sublist
) noexcept
-> size_t
682 { return cur
+ static_cast<ALuint
>(al::popcount(sublist
.FreeMask
)); })};
684 while(needed
> count
)
686 if(context
->mSourceList
.size() >= 1<<25) UNLIKELY
689 context
->mSourceList
.emplace_back();
690 auto sublist
= context
->mSourceList
.end() - 1;
691 sublist
->FreeMask
= ~0_u64
;
692 sublist
->Sources
= static_cast<ALsource
*>(al_calloc(alignof(ALsource
), sizeof(ALsource
)*64));
693 if(!sublist
->Sources
) UNLIKELY
695 context
->mSourceList
.pop_back();
703 ALsource
*AllocSource(ALCcontext
*context
)
705 auto sublist
= std::find_if(context
->mSourceList
.begin(), context
->mSourceList
.end(),
706 [](const SourceSubList
&entry
) noexcept
-> bool
707 { return entry
.FreeMask
!= 0; });
708 auto lidx
= static_cast<ALuint
>(std::distance(context
->mSourceList
.begin(), sublist
));
709 auto slidx
= static_cast<ALuint
>(al::countr_zero(sublist
->FreeMask
));
712 ALsource
*source
{al::construct_at(sublist
->Sources
+ slidx
)};
714 /* Add 1 to avoid source ID 0. */
715 source
->id
= ((lidx
<<6) | slidx
) + 1;
717 context
->mNumSources
+= 1;
718 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
723 void FreeSource(ALCcontext
*context
, ALsource
*source
)
725 const ALuint id
{source
->id
- 1};
726 const size_t lidx
{id
>> 6};
727 const ALuint slidx
{id
& 0x3f};
729 if(Voice
*voice
{GetSourceVoice(source
, context
)})
731 VoiceChange
*vchg
{GetVoiceChanger(context
)};
733 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
734 vchg
->mVoice
= voice
;
735 vchg
->mSourceID
= source
->id
;
736 vchg
->mState
= VChangeState::Stop
;
738 SendVoiceChanges(context
, vchg
);
741 al::destroy_at(source
);
743 context
->mSourceList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
744 context
->mNumSources
--;
748 inline ALsource
*LookupSource(ALCcontext
*context
, ALuint id
) noexcept
750 const size_t lidx
{(id
-1) >> 6};
751 const ALuint slidx
{(id
-1) & 0x3f};
753 if(lidx
>= context
->mSourceList
.size()) UNLIKELY
755 SourceSubList
&sublist
{context
->mSourceList
[lidx
]};
756 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
758 return sublist
.Sources
+ slidx
;
761 inline ALbuffer
*LookupBuffer(ALCdevice
*device
, ALuint id
) noexcept
763 const size_t lidx
{(id
-1) >> 6};
764 const ALuint slidx
{(id
-1) & 0x3f};
766 if(lidx
>= device
->BufferList
.size()) UNLIKELY
768 BufferSubList
&sublist
= device
->BufferList
[lidx
];
769 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
771 return sublist
.Buffers
+ slidx
;
774 inline ALfilter
*LookupFilter(ALCdevice
*device
, ALuint id
) noexcept
776 const size_t lidx
{(id
-1) >> 6};
777 const ALuint slidx
{(id
-1) & 0x3f};
779 if(lidx
>= device
->FilterList
.size()) UNLIKELY
781 FilterSubList
&sublist
= device
->FilterList
[lidx
];
782 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
784 return sublist
.Filters
+ slidx
;
787 inline ALeffectslot
*LookupEffectSlot(ALCcontext
*context
, ALuint id
) noexcept
789 const size_t lidx
{(id
-1) >> 6};
790 const ALuint slidx
{(id
-1) & 0x3f};
792 if(lidx
>= context
->mEffectSlotList
.size()) UNLIKELY
794 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[lidx
]};
795 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
797 return sublist
.EffectSlots
+ slidx
;
801 al::optional
<SourceStereo
> StereoModeFromEnum(ALenum mode
)
805 case AL_NORMAL_SOFT
: return SourceStereo::Normal
;
806 case AL_SUPER_STEREO_SOFT
: return SourceStereo::Enhanced
;
808 WARN("Unsupported stereo mode: 0x%04x\n", mode
);
811 ALenum
EnumFromStereoMode(SourceStereo mode
)
815 case SourceStereo::Normal
: return AL_NORMAL_SOFT
;
816 case SourceStereo::Enhanced
: return AL_SUPER_STEREO_SOFT
;
818 throw std::runtime_error
{"Invalid SourceStereo: "+std::to_string(int(mode
))};
821 al::optional
<SpatializeMode
> SpatializeModeFromEnum(ALenum mode
)
825 case AL_FALSE
: return SpatializeMode::Off
;
826 case AL_TRUE
: return SpatializeMode::On
;
827 case AL_AUTO_SOFT
: return SpatializeMode::Auto
;
829 WARN("Unsupported spatialize mode: 0x%04x\n", mode
);
832 ALenum
EnumFromSpatializeMode(SpatializeMode mode
)
836 case SpatializeMode::Off
: return AL_FALSE
;
837 case SpatializeMode::On
: return AL_TRUE
;
838 case SpatializeMode::Auto
: return AL_AUTO_SOFT
;
840 throw std::runtime_error
{"Invalid SpatializeMode: "+std::to_string(int(mode
))};
843 al::optional
<DirectMode
> DirectModeFromEnum(ALenum mode
)
847 case AL_FALSE
: return DirectMode::Off
;
848 case AL_DROP_UNMATCHED_SOFT
: return DirectMode::DropMismatch
;
849 case AL_REMIX_UNMATCHED_SOFT
: return DirectMode::RemixMismatch
;
851 WARN("Unsupported direct mode: 0x%04x\n", mode
);
854 ALenum
EnumFromDirectMode(DirectMode mode
)
858 case DirectMode::Off
: return AL_FALSE
;
859 case DirectMode::DropMismatch
: return AL_DROP_UNMATCHED_SOFT
;
860 case DirectMode::RemixMismatch
: return AL_REMIX_UNMATCHED_SOFT
;
862 throw std::runtime_error
{"Invalid DirectMode: "+std::to_string(int(mode
))};
865 al::optional
<DistanceModel
> DistanceModelFromALenum(ALenum model
)
869 case AL_NONE
: return DistanceModel::Disable
;
870 case AL_INVERSE_DISTANCE
: return DistanceModel::Inverse
;
871 case AL_INVERSE_DISTANCE_CLAMPED
: return DistanceModel::InverseClamped
;
872 case AL_LINEAR_DISTANCE
: return DistanceModel::Linear
;
873 case AL_LINEAR_DISTANCE_CLAMPED
: return DistanceModel::LinearClamped
;
874 case AL_EXPONENT_DISTANCE
: return DistanceModel::Exponent
;
875 case AL_EXPONENT_DISTANCE_CLAMPED
: return DistanceModel::ExponentClamped
;
879 ALenum
ALenumFromDistanceModel(DistanceModel model
)
883 case DistanceModel::Disable
: return AL_NONE
;
884 case DistanceModel::Inverse
: return AL_INVERSE_DISTANCE
;
885 case DistanceModel::InverseClamped
: return AL_INVERSE_DISTANCE_CLAMPED
;
886 case DistanceModel::Linear
: return AL_LINEAR_DISTANCE
;
887 case DistanceModel::LinearClamped
: return AL_LINEAR_DISTANCE_CLAMPED
;
888 case DistanceModel::Exponent
: return AL_EXPONENT_DISTANCE
;
889 case DistanceModel::ExponentClamped
: return AL_EXPONENT_DISTANCE_CLAMPED
;
891 throw std::runtime_error
{"Unexpected distance model "+std::to_string(static_cast<int>(model
))};
894 enum SourceProp
: ALenum
{
897 srcMinGain
= AL_MIN_GAIN
,
898 srcMaxGain
= AL_MAX_GAIN
,
899 srcMaxDistance
= AL_MAX_DISTANCE
,
900 srcRolloffFactor
= AL_ROLLOFF_FACTOR
,
901 srcDopplerFactor
= AL_DOPPLER_FACTOR
,
902 srcConeOuterGain
= AL_CONE_OUTER_GAIN
,
903 srcSecOffset
= AL_SEC_OFFSET
,
904 srcSampleOffset
= AL_SAMPLE_OFFSET
,
905 srcByteOffset
= AL_BYTE_OFFSET
,
906 srcConeInnerAngle
= AL_CONE_INNER_ANGLE
,
907 srcConeOuterAngle
= AL_CONE_OUTER_ANGLE
,
908 srcRefDistance
= AL_REFERENCE_DISTANCE
,
910 srcPosition
= AL_POSITION
,
911 srcVelocity
= AL_VELOCITY
,
912 srcDirection
= AL_DIRECTION
,
914 srcSourceRelative
= AL_SOURCE_RELATIVE
,
915 srcLooping
= AL_LOOPING
,
916 srcBuffer
= AL_BUFFER
,
917 srcSourceState
= AL_SOURCE_STATE
,
918 srcBuffersQueued
= AL_BUFFERS_QUEUED
,
919 srcBuffersProcessed
= AL_BUFFERS_PROCESSED
,
920 srcSourceType
= AL_SOURCE_TYPE
,
923 srcConeOuterGainHF
= AL_CONE_OUTER_GAINHF
,
924 srcAirAbsorptionFactor
= AL_AIR_ABSORPTION_FACTOR
,
925 srcRoomRolloffFactor
= AL_ROOM_ROLLOFF_FACTOR
,
926 srcDirectFilterGainHFAuto
= AL_DIRECT_FILTER_GAINHF_AUTO
,
927 srcAuxSendFilterGainAuto
= AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
,
928 srcAuxSendFilterGainHFAuto
= AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
,
929 srcDirectFilter
= AL_DIRECT_FILTER
,
930 srcAuxSendFilter
= AL_AUXILIARY_SEND_FILTER
,
932 /* AL_SOFT_direct_channels */
933 srcDirectChannelsSOFT
= AL_DIRECT_CHANNELS_SOFT
,
935 /* AL_EXT_source_distance_model */
936 srcDistanceModel
= AL_DISTANCE_MODEL
,
938 /* AL_SOFT_source_latency */
939 srcSampleOffsetLatencySOFT
= AL_SAMPLE_OFFSET_LATENCY_SOFT
,
940 srcSecOffsetLatencySOFT
= AL_SEC_OFFSET_LATENCY_SOFT
,
942 /* AL_EXT_STEREO_ANGLES */
943 srcAngles
= AL_STEREO_ANGLES
,
945 /* AL_EXT_SOURCE_RADIUS */
946 srcRadius
= AL_SOURCE_RADIUS
,
949 srcOrientation
= AL_ORIENTATION
,
951 /* AL_SOFT_source_length */
952 srcByteLength
= AL_BYTE_LENGTH_SOFT
,
953 srcSampleLength
= AL_SAMPLE_LENGTH_SOFT
,
954 srcSecLength
= AL_SEC_LENGTH_SOFT
,
956 /* AL_SOFT_source_resampler */
957 srcResampler
= AL_SOURCE_RESAMPLER_SOFT
,
959 /* AL_SOFT_source_spatialize */
960 srcSpatialize
= AL_SOURCE_SPATIALIZE_SOFT
,
962 /* ALC_SOFT_device_clock */
963 srcSampleOffsetClockSOFT
= AL_SAMPLE_OFFSET_CLOCK_SOFT
,
964 srcSecOffsetClockSOFT
= AL_SEC_OFFSET_CLOCK_SOFT
,
967 srcStereoMode
= AL_STEREO_MODE_SOFT
,
968 srcSuperStereoWidth
= AL_SUPER_STEREO_WIDTH_SOFT
,
970 /* AL_SOFT_buffer_sub_data */
971 srcByteRWOffsetsSOFT
= AL_BYTE_RW_OFFSETS_SOFT
,
972 srcSampleRWOffsetsSOFT
= AL_SAMPLE_RW_OFFSETS_SOFT
,
976 constexpr size_t MaxValues
{6u};
978 constexpr ALuint
IntValsByProp(ALenum prop
)
980 switch(static_cast<SourceProp
>(prop
))
982 case AL_SOURCE_STATE
:
984 case AL_BUFFERS_QUEUED
:
985 case AL_BUFFERS_PROCESSED
:
986 case AL_BYTE_LENGTH_SOFT
:
987 case AL_SAMPLE_LENGTH_SOFT
:
988 case AL_SOURCE_RELATIVE
:
991 case AL_SAMPLE_OFFSET
:
993 case AL_DIRECT_FILTER
:
994 case AL_DIRECT_FILTER_GAINHF_AUTO
:
995 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
996 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
997 case AL_DIRECT_CHANNELS_SOFT
:
998 case AL_DISTANCE_MODEL
:
999 case AL_SOURCE_RESAMPLER_SOFT
:
1000 case AL_SOURCE_SPATIALIZE_SOFT
:
1001 case AL_STEREO_MODE_SOFT
:
1004 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1005 if(sBufferSubDataCompat
)
1008 case AL_CONE_INNER_ANGLE
:
1009 case AL_CONE_OUTER_ANGLE
:
1014 case AL_REFERENCE_DISTANCE
:
1015 case AL_ROLLOFF_FACTOR
:
1016 case AL_CONE_OUTER_GAIN
:
1017 case AL_MAX_DISTANCE
:
1019 case AL_DOPPLER_FACTOR
:
1020 case AL_CONE_OUTER_GAINHF
:
1021 case AL_AIR_ABSORPTION_FACTOR
:
1022 case AL_ROOM_ROLLOFF_FACTOR
:
1023 case AL_SEC_LENGTH_SOFT
:
1024 case AL_SUPER_STEREO_WIDTH_SOFT
:
1025 return 1; /* 1x float */
1027 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1028 if(sBufferSubDataCompat
)
1032 case AL_AUXILIARY_SEND_FILTER
:
1038 return 3; /* 3x float */
1040 case AL_ORIENTATION
:
1041 return 6; /* 6x float */
1043 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1044 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1045 case AL_STEREO_ANGLES
:
1046 break; /* i64 only */
1047 case AL_SEC_OFFSET_LATENCY_SOFT
:
1048 case AL_SEC_OFFSET_CLOCK_SOFT
:
1049 break; /* double only */
1055 constexpr ALuint
Int64ValsByProp(ALenum prop
)
1057 switch(static_cast<SourceProp
>(prop
))
1059 case AL_SOURCE_STATE
:
1060 case AL_SOURCE_TYPE
:
1061 case AL_BUFFERS_QUEUED
:
1062 case AL_BUFFERS_PROCESSED
:
1063 case AL_BYTE_LENGTH_SOFT
:
1064 case AL_SAMPLE_LENGTH_SOFT
:
1065 case AL_SOURCE_RELATIVE
:
1068 case AL_SAMPLE_OFFSET
:
1069 case AL_BYTE_OFFSET
:
1070 case AL_DIRECT_FILTER
:
1071 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1072 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1073 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1074 case AL_DIRECT_CHANNELS_SOFT
:
1075 case AL_DISTANCE_MODEL
:
1076 case AL_SOURCE_RESAMPLER_SOFT
:
1077 case AL_SOURCE_SPATIALIZE_SOFT
:
1078 case AL_STEREO_MODE_SOFT
:
1081 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1082 if(sBufferSubDataCompat
)
1085 case AL_CONE_INNER_ANGLE
:
1086 case AL_CONE_OUTER_ANGLE
:
1091 case AL_REFERENCE_DISTANCE
:
1092 case AL_ROLLOFF_FACTOR
:
1093 case AL_CONE_OUTER_GAIN
:
1094 case AL_MAX_DISTANCE
:
1096 case AL_DOPPLER_FACTOR
:
1097 case AL_CONE_OUTER_GAINHF
:
1098 case AL_AIR_ABSORPTION_FACTOR
:
1099 case AL_ROOM_ROLLOFF_FACTOR
:
1100 case AL_SEC_LENGTH_SOFT
:
1101 case AL_SUPER_STEREO_WIDTH_SOFT
:
1102 return 1; /* 1x float */
1104 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1105 if(sBufferSubDataCompat
)
1109 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1110 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1111 case AL_STEREO_ANGLES
:
1114 case AL_AUXILIARY_SEND_FILTER
:
1120 return 3; /* 3x float */
1122 case AL_ORIENTATION
:
1123 return 6; /* 6x float */
1125 case AL_SEC_OFFSET_LATENCY_SOFT
:
1126 case AL_SEC_OFFSET_CLOCK_SOFT
:
1127 break; /* double only */
1133 constexpr ALuint
FloatValsByProp(ALenum prop
)
1135 switch(static_cast<SourceProp
>(prop
))
1141 case AL_MAX_DISTANCE
:
1142 case AL_ROLLOFF_FACTOR
:
1143 case AL_DOPPLER_FACTOR
:
1144 case AL_CONE_OUTER_GAIN
:
1146 case AL_SAMPLE_OFFSET
:
1147 case AL_BYTE_OFFSET
:
1148 case AL_CONE_INNER_ANGLE
:
1149 case AL_CONE_OUTER_ANGLE
:
1150 case AL_REFERENCE_DISTANCE
:
1151 case AL_CONE_OUTER_GAINHF
:
1152 case AL_AIR_ABSORPTION_FACTOR
:
1153 case AL_ROOM_ROLLOFF_FACTOR
:
1154 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1155 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1156 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1157 case AL_DIRECT_CHANNELS_SOFT
:
1158 case AL_DISTANCE_MODEL
:
1159 case AL_SOURCE_RELATIVE
:
1161 case AL_SOURCE_STATE
:
1162 case AL_BUFFERS_QUEUED
:
1163 case AL_BUFFERS_PROCESSED
:
1164 case AL_SOURCE_TYPE
:
1165 case AL_SOURCE_RESAMPLER_SOFT
:
1166 case AL_SOURCE_SPATIALIZE_SOFT
:
1167 case AL_BYTE_LENGTH_SOFT
:
1168 case AL_SAMPLE_LENGTH_SOFT
:
1169 case AL_SEC_LENGTH_SOFT
:
1170 case AL_STEREO_MODE_SOFT
:
1171 case AL_SUPER_STEREO_WIDTH_SOFT
:
1174 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1175 if(!sBufferSubDataCompat
)
1178 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1181 case AL_STEREO_ANGLES
:
1189 case AL_ORIENTATION
:
1192 case AL_SEC_OFFSET_LATENCY_SOFT
:
1193 case AL_SEC_OFFSET_CLOCK_SOFT
:
1194 break; /* Double only */
1197 case AL_DIRECT_FILTER
:
1198 case AL_AUXILIARY_SEND_FILTER
:
1199 break; /* i/i64 only */
1200 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1201 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1202 break; /* i64 only */
1206 constexpr ALuint
DoubleValsByProp(ALenum prop
)
1208 switch(static_cast<SourceProp
>(prop
))
1214 case AL_MAX_DISTANCE
:
1215 case AL_ROLLOFF_FACTOR
:
1216 case AL_DOPPLER_FACTOR
:
1217 case AL_CONE_OUTER_GAIN
:
1219 case AL_SAMPLE_OFFSET
:
1220 case AL_BYTE_OFFSET
:
1221 case AL_CONE_INNER_ANGLE
:
1222 case AL_CONE_OUTER_ANGLE
:
1223 case AL_REFERENCE_DISTANCE
:
1224 case AL_CONE_OUTER_GAINHF
:
1225 case AL_AIR_ABSORPTION_FACTOR
:
1226 case AL_ROOM_ROLLOFF_FACTOR
:
1227 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1228 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1229 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1230 case AL_DIRECT_CHANNELS_SOFT
:
1231 case AL_DISTANCE_MODEL
:
1232 case AL_SOURCE_RELATIVE
:
1234 case AL_SOURCE_STATE
:
1235 case AL_BUFFERS_QUEUED
:
1236 case AL_BUFFERS_PROCESSED
:
1237 case AL_SOURCE_TYPE
:
1238 case AL_SOURCE_RESAMPLER_SOFT
:
1239 case AL_SOURCE_SPATIALIZE_SOFT
:
1240 case AL_BYTE_LENGTH_SOFT
:
1241 case AL_SAMPLE_LENGTH_SOFT
:
1242 case AL_SEC_LENGTH_SOFT
:
1243 case AL_STEREO_MODE_SOFT
:
1244 case AL_SUPER_STEREO_WIDTH_SOFT
:
1247 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1248 if(!sBufferSubDataCompat
)
1251 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1254 case AL_SEC_OFFSET_LATENCY_SOFT
:
1255 case AL_SEC_OFFSET_CLOCK_SOFT
:
1256 case AL_STEREO_ANGLES
:
1264 case AL_ORIENTATION
:
1268 case AL_DIRECT_FILTER
:
1269 case AL_AUXILIARY_SEND_FILTER
:
1270 break; /* i/i64 only */
1271 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1272 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1273 break; /* i64 only */
1279 void SetSourcefv(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
, const al::span
<const float> values
);
1280 void SetSourceiv(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
, const al::span
<const int> values
);
1281 void SetSourcei64v(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
, const al::span
<const int64_t> values
);
1283 struct check_exception
: std::exception
{
1285 struct check_size_exception final
: check_exception
{
1286 const char *what() const noexcept override
1287 { return "check_size_exception"; }
1289 struct check_value_exception final
: check_exception
{
1290 const char *what() const noexcept override
1291 { return "check_value_exception"; }
1295 void UpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1297 if(!context
->mDeferUpdates
)
1299 if(Voice
*voice
{GetSourceVoice(source
, context
)})
1301 UpdateSourceProps(source
, voice
, context
);
1305 source
->mPropsDirty
= true;
1308 void CommitAndUpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1310 if(!context
->mDeferUpdates
)
1312 if(context
->hasEax())
1313 source
->eaxCommit();
1314 if(Voice
*voice
{GetSourceVoice(source
, context
)})
1316 UpdateSourceProps(source
, voice
, context
);
1320 source
->mPropsDirty
= true;
1325 inline void CommitAndUpdateSourceProps(ALsource
*source
, ALCcontext
*context
)
1326 { UpdateSourceProps(source
, context
); }
1331 * Returns a pair of lambdas to check the following setters and getters.
1333 * The first lambda checks the size of the span is valid for its given size,
1334 * setting the proper context error and throwing a check_size_exception if it
1337 * The second lambda tests the validity of the value check, setting the proper
1338 * context error and throwing a check_value_exception if it failed.
1340 template<typename T
, size_t N
>
1341 auto GetCheckers(ALCcontext
*const Context
, const SourceProp prop
, const al::span
<T
,N
> values
)
1343 return std::make_pair(
1344 [=](size_t expect
) -> void
1346 if(values
.size() == expect
) LIKELY
return;
1347 Context
->setError(AL_INVALID_ENUM
, "Property 0x%04x expects %zu value(s), got %zu",
1348 prop
, expect
, values
.size());
1349 throw check_size_exception
{};
1351 [Context
](bool passed
) -> void
1353 if(passed
) LIKELY
return;
1354 Context
->setError(AL_INVALID_VALUE
, "Value out of range");
1355 throw check_value_exception
{};
1360 void SetSourcefv(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
1361 const al::span
<const float> values
)
1363 /* Structured bindings would be nice (C++17). */
1364 auto Checkers
= GetCheckers(Context
, prop
, values
);
1365 auto &CheckSize
= Checkers
.first
;
1366 auto &CheckValue
= Checkers
.second
;
1371 case AL_SEC_LENGTH_SOFT
:
1372 case AL_SEC_OFFSET_LATENCY_SOFT
:
1373 case AL_SEC_OFFSET_CLOCK_SOFT
:
1375 return Context
->setError(AL_INVALID_OPERATION
,
1376 "Setting read-only source property 0x%04x", prop
);
1380 CheckValue(values
[0] >= 0.0f
);
1382 Source
->Pitch
= values
[0];
1383 return UpdateSourceProps(Source
, Context
);
1385 case AL_CONE_INNER_ANGLE
:
1387 CheckValue(values
[0] >= 0.0f
&& values
[0] <= 360.0f
);
1389 Source
->InnerAngle
= values
[0];
1390 return CommitAndUpdateSourceProps(Source
, Context
);
1392 case AL_CONE_OUTER_ANGLE
:
1394 CheckValue(values
[0] >= 0.0f
&& values
[0] <= 360.0f
);
1396 Source
->OuterAngle
= values
[0];
1397 return CommitAndUpdateSourceProps(Source
, Context
);
1401 CheckValue(values
[0] >= 0.0f
);
1403 Source
->Gain
= values
[0];
1404 return UpdateSourceProps(Source
, Context
);
1406 case AL_MAX_DISTANCE
:
1408 CheckValue(values
[0] >= 0.0f
);
1410 Source
->MaxDistance
= values
[0];
1411 return CommitAndUpdateSourceProps(Source
, Context
);
1413 case AL_ROLLOFF_FACTOR
:
1415 CheckValue(values
[0] >= 0.0f
);
1417 Source
->RolloffFactor
= values
[0];
1418 return CommitAndUpdateSourceProps(Source
, Context
);
1420 case AL_REFERENCE_DISTANCE
:
1422 CheckValue(values
[0] >= 0.0f
);
1424 Source
->RefDistance
= values
[0];
1425 return CommitAndUpdateSourceProps(Source
, Context
);
1429 CheckValue(values
[0] >= 0.0f
);
1431 Source
->MinGain
= values
[0];
1432 return UpdateSourceProps(Source
, Context
);
1436 CheckValue(values
[0] >= 0.0f
);
1438 Source
->MaxGain
= values
[0];
1439 return UpdateSourceProps(Source
, Context
);
1441 case AL_CONE_OUTER_GAIN
:
1443 CheckValue(values
[0] >= 0.0f
&& values
[0] <= 1.0f
);
1445 Source
->OuterGain
= values
[0];
1446 return UpdateSourceProps(Source
, Context
);
1448 case AL_CONE_OUTER_GAINHF
:
1450 CheckValue(values
[0] >= 0.0f
&& values
[0] <= 1.0f
);
1452 Source
->OuterGainHF
= values
[0];
1453 return UpdateSourceProps(Source
, Context
);
1455 case AL_AIR_ABSORPTION_FACTOR
:
1457 CheckValue(values
[0] >= 0.0f
&& values
[0] <= 10.0f
);
1459 Source
->AirAbsorptionFactor
= values
[0];
1460 return UpdateSourceProps(Source
, Context
);
1462 case AL_ROOM_ROLLOFF_FACTOR
:
1464 CheckValue(values
[0] >= 0.0f
&& values
[0] <= 10.0f
);
1466 Source
->RoomRolloffFactor
= values
[0];
1467 return UpdateSourceProps(Source
, Context
);
1469 case AL_DOPPLER_FACTOR
:
1471 CheckValue(values
[0] >= 0.0f
&& values
[0] <= 1.0f
);
1473 Source
->DopplerFactor
= values
[0];
1474 return UpdateSourceProps(Source
, Context
);
1477 case AL_SAMPLE_OFFSET
:
1478 case AL_BYTE_OFFSET
:
1480 CheckValue(std::isfinite(values
[0]));
1482 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1484 auto vpos
= GetSampleOffset(Source
->mQueue
, prop
, values
[0]);
1485 if(!vpos
) return Context
->setError(AL_INVALID_VALUE
, "Invalid offset");
1487 if(SetVoiceOffset(voice
, *vpos
, Source
, Context
, Context
->mALDevice
.get()))
1490 Source
->OffsetType
= prop
;
1491 Source
->Offset
= values
[0];
1494 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1497 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1498 if(sBufferSubDataCompat
)
1501 CheckValue(values
[0] >= 0.0f
&& std::isfinite(values
[0]));
1503 Source
->Radius
= values
[0];
1504 return UpdateSourceProps(Source
, Context
);
1506 case AL_SUPER_STEREO_WIDTH_SOFT
:
1508 CheckValue(values
[0] >= 0.0f
&& values
[0] <= 1.0f
);
1510 Source
->EnhWidth
= values
[0];
1511 return UpdateSourceProps(Source
, Context
);
1513 case AL_STEREO_ANGLES
:
1515 CheckValue(std::isfinite(values
[0]) && std::isfinite(values
[1]));
1517 Source
->StereoPan
[0] = values
[0];
1518 Source
->StereoPan
[1] = values
[1];
1519 return UpdateSourceProps(Source
, Context
);
1524 CheckValue(std::isfinite(values
[0]) && std::isfinite(values
[1]) && std::isfinite(values
[2]));
1526 Source
->Position
[0] = values
[0];
1527 Source
->Position
[1] = values
[1];
1528 Source
->Position
[2] = values
[2];
1529 return CommitAndUpdateSourceProps(Source
, Context
);
1533 CheckValue(std::isfinite(values
[0]) && std::isfinite(values
[1]) && std::isfinite(values
[2]));
1535 Source
->Velocity
[0] = values
[0];
1536 Source
->Velocity
[1] = values
[1];
1537 Source
->Velocity
[2] = values
[2];
1538 return CommitAndUpdateSourceProps(Source
, Context
);
1542 CheckValue(std::isfinite(values
[0]) && std::isfinite(values
[1]) && std::isfinite(values
[2]));
1544 Source
->Direction
[0] = values
[0];
1545 Source
->Direction
[1] = values
[1];
1546 Source
->Direction
[2] = values
[2];
1547 return CommitAndUpdateSourceProps(Source
, Context
);
1549 case AL_ORIENTATION
:
1551 CheckValue(std::isfinite(values
[0]) && std::isfinite(values
[1]) && std::isfinite(values
[2])
1552 && std::isfinite(values
[3]) && std::isfinite(values
[4]) && std::isfinite(values
[5]));
1554 Source
->OrientAt
[0] = values
[0];
1555 Source
->OrientAt
[1] = values
[1];
1556 Source
->OrientAt
[2] = values
[2];
1557 Source
->OrientUp
[0] = values
[3];
1558 Source
->OrientUp
[1] = values
[4];
1559 Source
->OrientUp
[2] = values
[5];
1560 return UpdateSourceProps(Source
, Context
);
1563 case AL_SOURCE_RELATIVE
:
1565 case AL_SOURCE_STATE
:
1566 case AL_SOURCE_TYPE
:
1567 case AL_DISTANCE_MODEL
:
1568 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1569 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1570 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1571 case AL_DIRECT_CHANNELS_SOFT
:
1572 case AL_SOURCE_RESAMPLER_SOFT
:
1573 case AL_SOURCE_SPATIALIZE_SOFT
:
1574 case AL_BYTE_LENGTH_SOFT
:
1575 case AL_SAMPLE_LENGTH_SOFT
:
1576 case AL_STEREO_MODE_SOFT
:
1578 ival
= static_cast<int>(values
[0]);
1579 return SetSourceiv(Source
, Context
, prop
, {&ival
, 1u});
1581 case AL_BUFFERS_QUEUED
:
1582 case AL_BUFFERS_PROCESSED
:
1584 ival
= static_cast<int>(static_cast<ALuint
>(values
[0]));
1585 return SetSourceiv(Source
, Context
, prop
, {&ival
, 1u});
1588 case AL_DIRECT_FILTER
:
1589 case AL_AUXILIARY_SEND_FILTER
:
1590 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1591 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1595 ERR("Unexpected property: 0x%04x\n", prop
);
1596 Context
->setError(AL_INVALID_ENUM
, "Invalid source float property 0x%04x", prop
);
1598 catch(check_exception
&) {
1601 void SetSourceiv(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
1602 const al::span
<const int> values
)
1604 auto Checkers
= GetCheckers(Context
, prop
, values
);
1605 auto &CheckSize
= Checkers
.first
;
1606 auto &CheckValue
= Checkers
.second
;
1607 ALCdevice
*device
{Context
->mALDevice
.get()};
1608 ALeffectslot
*slot
{nullptr};
1609 al::deque
<ALbufferQueueItem
> oldlist
;
1610 std::unique_lock
<std::mutex
> slotlock
;
1615 case AL_SOURCE_STATE
:
1616 case AL_SOURCE_TYPE
:
1617 case AL_BUFFERS_QUEUED
:
1618 case AL_BUFFERS_PROCESSED
:
1619 case AL_BYTE_LENGTH_SOFT
:
1620 case AL_SAMPLE_LENGTH_SOFT
:
1622 return Context
->setError(AL_INVALID_OPERATION
,
1623 "Setting read-only source property 0x%04x", prop
);
1625 case AL_SOURCE_RELATIVE
:
1627 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1629 Source
->HeadRelative
= values
[0] != AL_FALSE
;
1630 return CommitAndUpdateSourceProps(Source
, Context
);
1634 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1636 Source
->Looping
= values
[0] != AL_FALSE
;
1637 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1640 voice
->mLoopBuffer
.store(&Source
->mQueue
.front(), std::memory_order_release
);
1642 voice
->mLoopBuffer
.store(nullptr, std::memory_order_release
);
1644 /* If the source is playing, wait for the current mix to finish to
1645 * ensure it isn't currently looping back or reaching the end.
1647 device
->waitForMix();
1654 const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1655 if(state
== AL_PLAYING
|| state
== AL_PAUSED
)
1656 return Context
->setError(AL_INVALID_OPERATION
,
1657 "Setting buffer on playing or paused source %u", Source
->id
);
1661 std::lock_guard
<std::mutex
> _
{device
->BufferLock
};
1662 ALbuffer
*buffer
{LookupBuffer(device
, static_cast<ALuint
>(values
[0]))};
1664 return Context
->setError(AL_INVALID_VALUE
, "Invalid buffer ID %u",
1665 static_cast<ALuint
>(values
[0]));
1666 if(buffer
->MappedAccess
&& !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
1667 return Context
->setError(AL_INVALID_OPERATION
,
1668 "Setting non-persistently mapped buffer %u", buffer
->id
);
1669 if(buffer
->mCallback
&& ReadRef(buffer
->ref
) != 0)
1670 return Context
->setError(AL_INVALID_OPERATION
,
1671 "Setting already-set callback buffer %u", buffer
->id
);
1673 /* Add the selected buffer to a one-item queue */
1674 al::deque
<ALbufferQueueItem
> newlist
;
1675 newlist
.emplace_back();
1676 newlist
.back().mCallback
= buffer
->mCallback
;
1677 newlist
.back().mUserData
= buffer
->mUserData
;
1678 newlist
.back().mBlockAlign
= buffer
->mBlockAlign
;
1679 newlist
.back().mSampleLen
= buffer
->mSampleLen
;
1680 newlist
.back().mLoopStart
= buffer
->mLoopStart
;
1681 newlist
.back().mLoopEnd
= buffer
->mLoopEnd
;
1682 newlist
.back().mSamples
= buffer
->mData
.data();
1683 newlist
.back().mBuffer
= buffer
;
1684 IncrementRef(buffer
->ref
);
1686 /* Source is now Static */
1687 Source
->SourceType
= AL_STATIC
;
1688 Source
->mQueue
.swap(oldlist
);
1689 Source
->mQueue
.swap(newlist
);
1693 /* Source is now Undetermined */
1694 Source
->SourceType
= AL_UNDETERMINED
;
1695 Source
->mQueue
.swap(oldlist
);
1698 /* Delete all elements in the previous queue */
1699 for(auto &item
: oldlist
)
1701 if(ALbuffer
*buffer
{item
.mBuffer
})
1702 DecrementRef(buffer
->ref
);
1707 case AL_SAMPLE_OFFSET
:
1708 case AL_BYTE_OFFSET
:
1711 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
1713 auto vpos
= GetSampleOffset(Source
->mQueue
, prop
, values
[0]);
1714 if(!vpos
) return Context
->setError(AL_INVALID_VALUE
, "Invalid source offset");
1716 if(SetVoiceOffset(voice
, *vpos
, Source
, Context
, device
))
1719 Source
->OffsetType
= prop
;
1720 Source
->Offset
= values
[0];
1723 case AL_DIRECT_FILTER
:
1727 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
1728 ALfilter
*filter
{LookupFilter(device
, static_cast<ALuint
>(values
[0]))};
1730 return Context
->setError(AL_INVALID_VALUE
, "Invalid filter ID %u",
1731 static_cast<ALuint
>(values
[0]));
1732 Source
->Direct
.Gain
= filter
->Gain
;
1733 Source
->Direct
.GainHF
= filter
->GainHF
;
1734 Source
->Direct
.HFReference
= filter
->HFReference
;
1735 Source
->Direct
.GainLF
= filter
->GainLF
;
1736 Source
->Direct
.LFReference
= filter
->LFReference
;
1740 Source
->Direct
.Gain
= 1.0f
;
1741 Source
->Direct
.GainHF
= 1.0f
;
1742 Source
->Direct
.HFReference
= LOWPASSFREQREF
;
1743 Source
->Direct
.GainLF
= 1.0f
;
1744 Source
->Direct
.LFReference
= HIGHPASSFREQREF
;
1746 return UpdateSourceProps(Source
, Context
);
1748 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1750 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1752 Source
->DryGainHFAuto
= values
[0] != AL_FALSE
;
1753 return UpdateSourceProps(Source
, Context
);
1755 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1757 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1759 Source
->WetGainAuto
= values
[0] != AL_FALSE
;
1760 return UpdateSourceProps(Source
, Context
);
1762 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1764 CheckValue(values
[0] == AL_FALSE
|| values
[0] == AL_TRUE
);
1766 Source
->WetGainHFAuto
= values
[0] != AL_FALSE
;
1767 return UpdateSourceProps(Source
, Context
);
1769 case AL_DIRECT_CHANNELS_SOFT
:
1771 if(auto mode
= DirectModeFromEnum(values
[0]))
1773 Source
->DirectChannels
= *mode
;
1774 return UpdateSourceProps(Source
, Context
);
1776 Context
->setError(AL_INVALID_VALUE
, "Unsupported AL_DIRECT_CHANNELS_SOFT: 0x%04x\n",
1780 case AL_DISTANCE_MODEL
:
1782 if(auto model
= DistanceModelFromALenum(values
[0]))
1784 Source
->mDistanceModel
= *model
;
1785 if(Context
->mSourceDistanceModel
)
1786 UpdateSourceProps(Source
, Context
);
1789 Context
->setError(AL_INVALID_VALUE
, "Distance model out of range: 0x%04x", values
[0]);
1792 case AL_SOURCE_RESAMPLER_SOFT
:
1794 CheckValue(values
[0] >= 0 && values
[0] <= static_cast<int>(Resampler::Max
));
1796 Source
->mResampler
= static_cast<Resampler
>(values
[0]);
1797 return UpdateSourceProps(Source
, Context
);
1799 case AL_SOURCE_SPATIALIZE_SOFT
:
1801 if(auto mode
= SpatializeModeFromEnum(values
[0]))
1803 Source
->mSpatialize
= *mode
;
1804 return UpdateSourceProps(Source
, Context
);
1806 Context
->setError(AL_INVALID_VALUE
, "Unsupported AL_SOURCE_SPATIALIZE_SOFT: 0x%04x\n",
1810 case AL_STEREO_MODE_SOFT
:
1813 const ALenum state
{GetSourceState(Source
, GetSourceVoice(Source
, Context
))};
1814 if(state
== AL_PLAYING
|| state
== AL_PAUSED
)
1815 return Context
->setError(AL_INVALID_OPERATION
,
1816 "Modifying stereo mode on playing or paused source %u", Source
->id
);
1818 if(auto mode
= StereoModeFromEnum(values
[0]))
1820 Source
->mStereoMode
= *mode
;
1823 Context
->setError(AL_INVALID_VALUE
, "Unsupported AL_STEREO_MODE_SOFT: 0x%04x\n",
1827 case AL_AUXILIARY_SEND_FILTER
:
1829 slotlock
= std::unique_lock
<std::mutex
>{Context
->mEffectSlotLock
};
1830 if(values
[0] && (slot
=LookupEffectSlot(Context
, static_cast<ALuint
>(values
[0]))) == nullptr)
1831 return Context
->setError(AL_INVALID_VALUE
, "Invalid effect ID %u", values
[0]);
1832 if(static_cast<ALuint
>(values
[1]) >= device
->NumAuxSends
)
1833 return Context
->setError(AL_INVALID_VALUE
, "Invalid send %u", values
[1]);
1837 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
1838 ALfilter
*filter
{LookupFilter(device
, static_cast<ALuint
>(values
[2]))};
1840 return Context
->setError(AL_INVALID_VALUE
, "Invalid filter ID %u", values
[2]);
1842 auto &send
= Source
->Send
[static_cast<ALuint
>(values
[1])];
1843 send
.Gain
= filter
->Gain
;
1844 send
.GainHF
= filter
->GainHF
;
1845 send
.HFReference
= filter
->HFReference
;
1846 send
.GainLF
= filter
->GainLF
;
1847 send
.LFReference
= filter
->LFReference
;
1851 /* Disable filter */
1852 auto &send
= Source
->Send
[static_cast<ALuint
>(values
[1])];
1855 send
.HFReference
= LOWPASSFREQREF
;
1857 send
.LFReference
= HIGHPASSFREQREF
;
1860 /* We must force an update if the current auxiliary slot is valid and
1861 * about to be changed on an active source, in case the old slot is
1862 * about to be deleted.
1864 if(Source
->Send
[static_cast<ALuint
>(values
[1])].Slot
1865 && slot
!= Source
->Send
[static_cast<ALuint
>(values
[1])].Slot
1866 && IsPlayingOrPaused(Source
))
1868 /* Add refcount on the new slot, and release the previous slot */
1869 if(slot
) IncrementRef(slot
->ref
);
1870 if(auto *oldslot
= Source
->Send
[static_cast<ALuint
>(values
[1])].Slot
)
1871 DecrementRef(oldslot
->ref
);
1872 Source
->Send
[static_cast<ALuint
>(values
[1])].Slot
= slot
;
1874 Voice
*voice
{GetSourceVoice(Source
, Context
)};
1875 if(voice
) UpdateSourceProps(Source
, voice
, Context
);
1876 else Source
->mPropsDirty
= true;
1880 if(slot
) IncrementRef(slot
->ref
);
1881 if(auto *oldslot
= Source
->Send
[static_cast<ALuint
>(values
[1])].Slot
)
1882 DecrementRef(oldslot
->ref
);
1883 Source
->Send
[static_cast<ALuint
>(values
[1])].Slot
= slot
;
1884 UpdateSourceProps(Source
, Context
);
1889 case AL_SAMPLE_RW_OFFSETS_SOFT
:
1890 if(sBufferSubDataCompat
)
1892 return Context
->setError(AL_INVALID_OPERATION
,
1893 "Setting read-only source property 0x%04x", prop
);
1896 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
1897 if(sBufferSubDataCompat
)
1898 return Context
->setError(AL_INVALID_OPERATION
,
1899 "Setting read-only source property 0x%04x", prop
);
1903 case AL_CONE_INNER_ANGLE
:
1904 case AL_CONE_OUTER_ANGLE
:
1909 case AL_REFERENCE_DISTANCE
:
1910 case AL_ROLLOFF_FACTOR
:
1911 case AL_CONE_OUTER_GAIN
:
1912 case AL_MAX_DISTANCE
:
1913 case AL_DOPPLER_FACTOR
:
1914 case AL_CONE_OUTER_GAINHF
:
1915 case AL_AIR_ABSORPTION_FACTOR
:
1916 case AL_ROOM_ROLLOFF_FACTOR
:
1917 case AL_SEC_LENGTH_SOFT
:
1918 case AL_SUPER_STEREO_WIDTH_SOFT
:
1920 fvals
[0] = static_cast<float>(values
[0]);
1921 return SetSourcefv(Source
, Context
, prop
, {fvals
, 1u});
1928 fvals
[0] = static_cast<float>(values
[0]);
1929 fvals
[1] = static_cast<float>(values
[1]);
1930 fvals
[2] = static_cast<float>(values
[2]);
1931 return SetSourcefv(Source
, Context
, prop
, {fvals
, 3u});
1934 case AL_ORIENTATION
:
1936 fvals
[0] = static_cast<float>(values
[0]);
1937 fvals
[1] = static_cast<float>(values
[1]);
1938 fvals
[2] = static_cast<float>(values
[2]);
1939 fvals
[3] = static_cast<float>(values
[3]);
1940 fvals
[4] = static_cast<float>(values
[4]);
1941 fvals
[5] = static_cast<float>(values
[5]);
1942 return SetSourcefv(Source
, Context
, prop
, {fvals
, 6u});
1944 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1945 case AL_SEC_OFFSET_LATENCY_SOFT
:
1946 case AL_SEC_OFFSET_CLOCK_SOFT
:
1947 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1948 case AL_STEREO_ANGLES
:
1952 ERR("Unexpected property: 0x%04x\n", prop
);
1953 Context
->setError(AL_INVALID_ENUM
, "Invalid source integer property 0x%04x", prop
);
1955 catch(check_exception
&) {
1958 void SetSourcei64v(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
1959 const al::span
<const int64_t> values
)
1961 auto Checkers
= GetCheckers(Context
, prop
, values
);
1962 auto &CheckSize
= Checkers
.first
;
1963 auto &CheckValue
= Checkers
.second
;
1964 float fvals
[MaxValues
];
1965 int ivals
[MaxValues
];
1969 case AL_SOURCE_TYPE
:
1970 case AL_BUFFERS_QUEUED
:
1971 case AL_BUFFERS_PROCESSED
:
1972 case AL_SOURCE_STATE
:
1973 case AL_BYTE_LENGTH_SOFT
:
1974 case AL_SAMPLE_LENGTH_SOFT
:
1975 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
1976 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
1978 return Context
->setError(AL_INVALID_OPERATION
,
1979 "Setting read-only source property 0x%04x", prop
);
1982 case AL_SOURCE_RELATIVE
:
1985 case AL_SAMPLE_OFFSET
:
1986 case AL_BYTE_OFFSET
:
1987 case AL_DIRECT_FILTER_GAINHF_AUTO
:
1988 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
1989 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
1990 case AL_DIRECT_CHANNELS_SOFT
:
1991 case AL_DISTANCE_MODEL
:
1992 case AL_SOURCE_RESAMPLER_SOFT
:
1993 case AL_SOURCE_SPATIALIZE_SOFT
:
1994 case AL_STEREO_MODE_SOFT
:
1996 CheckValue(values
[0] <= INT_MAX
&& values
[0] >= INT_MIN
);
1998 ivals
[0] = static_cast<int>(values
[0]);
1999 return SetSourceiv(Source
, Context
, prop
, {ivals
, 1u});
2003 case AL_DIRECT_FILTER
:
2005 CheckValue(values
[0] <= UINT_MAX
&& values
[0] >= 0);
2007 ivals
[0] = static_cast<int>(values
[0]);
2008 return SetSourceiv(Source
, Context
, prop
, {ivals
, 1u});
2011 case AL_AUXILIARY_SEND_FILTER
:
2013 CheckValue(values
[0] <= UINT_MAX
&& values
[0] >= 0 && values
[1] <= UINT_MAX
2014 && values
[1] >= 0 && values
[2] <= UINT_MAX
&& values
[2] >= 0);
2016 ivals
[0] = static_cast<int>(values
[0]);
2017 ivals
[1] = static_cast<int>(values
[1]);
2018 ivals
[2] = static_cast<int>(values
[2]);
2019 return SetSourceiv(Source
, Context
, prop
, {ivals
, 3u});
2021 case AL_SAMPLE_RW_OFFSETS_SOFT
:
2022 if(sBufferSubDataCompat
)
2025 return Context
->setError(AL_INVALID_OPERATION
,
2026 "Setting read-only source property 0x%04x", prop
);
2030 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
2031 if(sBufferSubDataCompat
)
2032 return Context
->setError(AL_INVALID_OPERATION
,
2033 "Setting read-only source property 0x%04x", prop
);
2037 case AL_CONE_INNER_ANGLE
:
2038 case AL_CONE_OUTER_ANGLE
:
2043 case AL_REFERENCE_DISTANCE
:
2044 case AL_ROLLOFF_FACTOR
:
2045 case AL_CONE_OUTER_GAIN
:
2046 case AL_MAX_DISTANCE
:
2047 case AL_DOPPLER_FACTOR
:
2048 case AL_CONE_OUTER_GAINHF
:
2049 case AL_AIR_ABSORPTION_FACTOR
:
2050 case AL_ROOM_ROLLOFF_FACTOR
:
2051 case AL_SEC_LENGTH_SOFT
:
2052 case AL_SUPER_STEREO_WIDTH_SOFT
:
2054 fvals
[0] = static_cast<float>(values
[0]);
2055 return SetSourcefv(Source
, Context
, prop
, {fvals
, 1u});
2062 fvals
[0] = static_cast<float>(values
[0]);
2063 fvals
[1] = static_cast<float>(values
[1]);
2064 fvals
[2] = static_cast<float>(values
[2]);
2065 return SetSourcefv(Source
, Context
, prop
, {fvals
, 3u});
2068 case AL_ORIENTATION
:
2070 fvals
[0] = static_cast<float>(values
[0]);
2071 fvals
[1] = static_cast<float>(values
[1]);
2072 fvals
[2] = static_cast<float>(values
[2]);
2073 fvals
[3] = static_cast<float>(values
[3]);
2074 fvals
[4] = static_cast<float>(values
[4]);
2075 fvals
[5] = static_cast<float>(values
[5]);
2076 return SetSourcefv(Source
, Context
, prop
, {fvals
, 6u});
2078 case AL_SEC_OFFSET_LATENCY_SOFT
:
2079 case AL_SEC_OFFSET_CLOCK_SOFT
:
2080 case AL_STEREO_ANGLES
:
2084 ERR("Unexpected property: 0x%04x\n", prop
);
2085 Context
->setError(AL_INVALID_ENUM
, "Invalid source integer64 property 0x%04x", prop
);
2087 catch(check_exception
&) {
2091 template<typename T
, size_t N
>
2092 auto GetSizeChecker(ALCcontext
*const Context
, const SourceProp prop
, const al::span
<T
,N
> values
)
2094 return [=](size_t expect
) -> void
2096 if(values
.size() == expect
) LIKELY
return;
2097 Context
->setError(AL_INVALID_ENUM
, "Property 0x%04x expects %zu value(s), got %zu",
2098 prop
, expect
, values
.size());
2099 throw check_size_exception
{};
2103 bool GetSourcedv(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
, const al::span
<double> values
);
2104 bool GetSourceiv(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
, const al::span
<int> values
);
2105 bool GetSourcei64v(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
, const al::span
<int64_t> values
);
2107 bool GetSourcedv(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
2108 const al::span
<double> values
)
2110 auto CheckSize
= GetSizeChecker(Context
, prop
, values
);
2111 ALCdevice
*device
{Context
->mALDevice
.get()};
2112 ClockLatency clocktime
;
2113 nanoseconds srcclock
;
2114 int ivals
[MaxValues
];
2121 values
[0] = Source
->Gain
;
2126 values
[0] = Source
->Pitch
;
2129 case AL_MAX_DISTANCE
:
2131 values
[0] = Source
->MaxDistance
;
2134 case AL_ROLLOFF_FACTOR
:
2136 values
[0] = Source
->RolloffFactor
;
2139 case AL_REFERENCE_DISTANCE
:
2141 values
[0] = Source
->RefDistance
;
2144 case AL_CONE_INNER_ANGLE
:
2146 values
[0] = Source
->InnerAngle
;
2149 case AL_CONE_OUTER_ANGLE
:
2151 values
[0] = Source
->OuterAngle
;
2156 values
[0] = Source
->MinGain
;
2161 values
[0] = Source
->MaxGain
;
2164 case AL_CONE_OUTER_GAIN
:
2166 values
[0] = Source
->OuterGain
;
2170 case AL_SAMPLE_OFFSET
:
2171 case AL_BYTE_OFFSET
:
2173 values
[0] = GetSourceOffset(Source
, prop
, Context
);
2176 case AL_CONE_OUTER_GAINHF
:
2178 values
[0] = Source
->OuterGainHF
;
2181 case AL_AIR_ABSORPTION_FACTOR
:
2183 values
[0] = Source
->AirAbsorptionFactor
;
2186 case AL_ROOM_ROLLOFF_FACTOR
:
2188 values
[0] = Source
->RoomRolloffFactor
;
2191 case AL_DOPPLER_FACTOR
:
2193 values
[0] = Source
->DopplerFactor
;
2196 case AL_SAMPLE_RW_OFFSETS_SOFT
:
2198 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
2199 if(sBufferSubDataCompat
)
2203 values
[0] = Source
->Radius
;
2206 case AL_SUPER_STEREO_WIDTH_SOFT
:
2208 values
[0] = Source
->EnhWidth
;
2211 case AL_BYTE_LENGTH_SOFT
:
2212 case AL_SAMPLE_LENGTH_SOFT
:
2213 case AL_SEC_LENGTH_SOFT
:
2215 values
[0] = GetSourceLength(Source
, prop
);
2218 case AL_STEREO_ANGLES
:
2220 values
[0] = Source
->StereoPan
[0];
2221 values
[1] = Source
->StereoPan
[1];
2224 case AL_SEC_OFFSET_LATENCY_SOFT
:
2226 /* Get the source offset with the clock time first. Then get the clock
2227 * time with the device latency. Order is important.
2229 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2231 std::lock_guard
<std::mutex
> _
{device
->StateLock
};
2232 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2234 if(srcclock
== clocktime
.ClockTime
)
2235 values
[1] = static_cast<double>(clocktime
.Latency
.count()) / 1000000000.0;
2238 /* If the clock time incremented, reduce the latency by that much
2239 * since it's that much closer to the source offset it got earlier.
2241 const nanoseconds diff
{clocktime
.ClockTime
- srcclock
};
2242 const nanoseconds latency
{clocktime
.Latency
- std::min(clocktime
.Latency
, diff
)};
2243 values
[1] = static_cast<double>(latency
.count()) / 1000000000.0;
2247 case AL_SEC_OFFSET_CLOCK_SOFT
:
2249 values
[0] = GetSourceSecOffset(Source
, Context
, &srcclock
);
2250 values
[1] = static_cast<double>(srcclock
.count()) / 1000000000.0;
2255 values
[0] = Source
->Position
[0];
2256 values
[1] = Source
->Position
[1];
2257 values
[2] = Source
->Position
[2];
2262 values
[0] = Source
->Velocity
[0];
2263 values
[1] = Source
->Velocity
[1];
2264 values
[2] = Source
->Velocity
[2];
2269 values
[0] = Source
->Direction
[0];
2270 values
[1] = Source
->Direction
[1];
2271 values
[2] = Source
->Direction
[2];
2274 case AL_ORIENTATION
:
2276 values
[0] = Source
->OrientAt
[0];
2277 values
[1] = Source
->OrientAt
[1];
2278 values
[2] = Source
->OrientAt
[2];
2279 values
[3] = Source
->OrientUp
[0];
2280 values
[4] = Source
->OrientUp
[1];
2281 values
[5] = Source
->OrientUp
[2];
2285 case AL_SOURCE_RELATIVE
:
2287 case AL_SOURCE_STATE
:
2288 case AL_BUFFERS_QUEUED
:
2289 case AL_BUFFERS_PROCESSED
:
2290 case AL_SOURCE_TYPE
:
2291 case AL_DIRECT_FILTER_GAINHF_AUTO
:
2292 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
2293 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
2294 case AL_DIRECT_CHANNELS_SOFT
:
2295 case AL_DISTANCE_MODEL
:
2296 case AL_SOURCE_RESAMPLER_SOFT
:
2297 case AL_SOURCE_SPATIALIZE_SOFT
:
2298 case AL_STEREO_MODE_SOFT
:
2300 if((err
=GetSourceiv(Source
, Context
, prop
, {ivals
, 1u})) != false)
2301 values
[0] = static_cast<double>(ivals
[0]);
2305 case AL_DIRECT_FILTER
:
2306 case AL_AUXILIARY_SEND_FILTER
:
2307 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
2308 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
2312 ERR("Unexpected property: 0x%04x\n", prop
);
2313 Context
->setError(AL_INVALID_ENUM
, "Invalid source double property 0x%04x", prop
);
2316 catch(check_exception
&) {
2320 bool GetSourceiv(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
2321 const al::span
<int> values
)
2323 auto CheckSize
= GetSizeChecker(Context
, prop
, values
);
2324 double dvals
[MaxValues
];
2329 case AL_SOURCE_RELATIVE
:
2331 values
[0] = Source
->HeadRelative
;
2336 values
[0] = Source
->Looping
;
2342 ALbufferQueueItem
*BufferList
{};
2343 /* HACK: This query should technically only return the buffer set
2344 * on a static source. However, some apps had used it to detect
2345 * when a streaming source changed buffers, so report the current
2346 * buffer's ID when playing.
2348 if(Source
->SourceType
== AL_STATIC
|| Source
->state
== AL_INITIAL
)
2350 if(!Source
->mQueue
.empty())
2351 BufferList
= &Source
->mQueue
.front();
2353 else if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2355 VoiceBufferItem
*Current
{voice
->mCurrentBuffer
.load(std::memory_order_relaxed
)};
2356 BufferList
= static_cast<ALbufferQueueItem
*>(Current
);
2358 ALbuffer
*buffer
{BufferList
? BufferList
->mBuffer
: nullptr};
2359 values
[0] = buffer
? static_cast<int>(buffer
->id
) : 0;
2363 case AL_SOURCE_STATE
:
2365 values
[0] = GetSourceState(Source
, GetSourceVoice(Source
, Context
));
2368 case AL_BUFFERS_QUEUED
:
2370 values
[0] = static_cast<int>(Source
->mQueue
.size());
2373 case AL_BUFFERS_PROCESSED
:
2375 if(Source
->Looping
|| Source
->SourceType
!= AL_STREAMING
)
2377 /* Buffers on a looping source are in a perpetual state of PENDING,
2378 * so don't report any as PROCESSED
2385 if(Source
->state
!= AL_INITIAL
)
2387 const VoiceBufferItem
*Current
{nullptr};
2388 if(Voice
*voice
{GetSourceVoice(Source
, Context
)})
2389 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
2390 for(auto &item
: Source
->mQueue
)
2392 if(&item
== Current
)
2401 case AL_SOURCE_TYPE
:
2403 values
[0] = Source
->SourceType
;
2406 case AL_DIRECT_FILTER_GAINHF_AUTO
:
2408 values
[0] = Source
->DryGainHFAuto
;
2411 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
2413 values
[0] = Source
->WetGainAuto
;
2416 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
2418 values
[0] = Source
->WetGainHFAuto
;
2421 case AL_DIRECT_CHANNELS_SOFT
:
2423 values
[0] = EnumFromDirectMode(Source
->DirectChannels
);
2426 case AL_DISTANCE_MODEL
:
2428 values
[0] = ALenumFromDistanceModel(Source
->mDistanceModel
);
2431 case AL_BYTE_LENGTH_SOFT
:
2432 case AL_SAMPLE_LENGTH_SOFT
:
2433 case AL_SEC_LENGTH_SOFT
:
2435 values
[0] = static_cast<int>(mind(GetSourceLength(Source
, prop
),
2436 std::numeric_limits
<int>::max()));
2439 case AL_SOURCE_RESAMPLER_SOFT
:
2441 values
[0] = static_cast<int>(Source
->mResampler
);
2444 case AL_SOURCE_SPATIALIZE_SOFT
:
2446 values
[0] = EnumFromSpatializeMode(Source
->mSpatialize
);
2449 case AL_STEREO_MODE_SOFT
:
2451 values
[0] = EnumFromStereoMode(Source
->mStereoMode
);
2454 case AL_SAMPLE_RW_OFFSETS_SOFT
:
2455 if(sBufferSubDataCompat
)
2458 const auto offset
= GetSourceOffset(Source
, AL_SAMPLE_OFFSET
, Context
);
2459 /* FIXME: values[1] should be ahead of values[0] by the device
2460 * update time. It needs to clamp or wrap the length of the buffer
2463 values
[0] = static_cast<int>(mind(offset
, std::numeric_limits
<int>::max()));
2464 values
[1] = values
[0];
2468 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
2469 if(sBufferSubDataCompat
)
2472 const auto offset
= GetSourceOffset(Source
, AL_BYTE_OFFSET
, Context
);
2473 /* FIXME: values[1] should be ahead of values[0] by the device
2474 * update time. It needs to clamp or wrap the length of the buffer
2477 values
[0] = static_cast<int>(mind(offset
, std::numeric_limits
<int>::max()));
2478 values
[1] = values
[0];
2483 /* 1x float/double */
2484 case AL_CONE_INNER_ANGLE
:
2485 case AL_CONE_OUTER_ANGLE
:
2490 case AL_REFERENCE_DISTANCE
:
2491 case AL_ROLLOFF_FACTOR
:
2492 case AL_CONE_OUTER_GAIN
:
2493 case AL_MAX_DISTANCE
:
2495 case AL_SAMPLE_OFFSET
:
2496 case AL_BYTE_OFFSET
:
2497 case AL_DOPPLER_FACTOR
:
2498 case AL_AIR_ABSORPTION_FACTOR
:
2499 case AL_ROOM_ROLLOFF_FACTOR
:
2500 case AL_CONE_OUTER_GAINHF
:
2501 case AL_SUPER_STEREO_WIDTH_SOFT
:
2503 if((err
=GetSourcedv(Source
, Context
, prop
, {dvals
, 1u})) != false)
2504 values
[0] = static_cast<int>(dvals
[0]);
2507 /* 3x float/double */
2512 if((err
=GetSourcedv(Source
, Context
, prop
, {dvals
, 3u})) != false)
2514 values
[0] = static_cast<int>(dvals
[0]);
2515 values
[1] = static_cast<int>(dvals
[1]);
2516 values
[2] = static_cast<int>(dvals
[2]);
2520 /* 6x float/double */
2521 case AL_ORIENTATION
:
2523 if((err
=GetSourcedv(Source
, Context
, prop
, {dvals
, 6u})) != false)
2525 values
[0] = static_cast<int>(dvals
[0]);
2526 values
[1] = static_cast<int>(dvals
[1]);
2527 values
[2] = static_cast<int>(dvals
[2]);
2528 values
[3] = static_cast<int>(dvals
[3]);
2529 values
[4] = static_cast<int>(dvals
[4]);
2530 values
[5] = static_cast<int>(dvals
[5]);
2534 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
2535 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
2536 break; /* i64 only */
2537 case AL_SEC_OFFSET_LATENCY_SOFT
:
2538 case AL_SEC_OFFSET_CLOCK_SOFT
:
2539 break; /* Double only */
2540 case AL_STEREO_ANGLES
:
2541 break; /* Float/double only */
2543 case AL_DIRECT_FILTER
:
2544 case AL_AUXILIARY_SEND_FILTER
:
2548 ERR("Unexpected property: 0x%04x\n", prop
);
2549 Context
->setError(AL_INVALID_ENUM
, "Invalid source integer property 0x%04x", prop
);
2552 catch(check_exception
&) {
2556 bool GetSourcei64v(ALsource
*const Source
, ALCcontext
*const Context
, const SourceProp prop
,
2557 const al::span
<int64_t> values
)
2559 auto CheckSize
= GetSizeChecker(Context
, prop
, values
);
2560 ALCdevice
*device
{Context
->mALDevice
.get()};
2561 ClockLatency clocktime
;
2562 nanoseconds srcclock
;
2563 double dvals
[MaxValues
];
2564 int ivals
[MaxValues
];
2569 case AL_BYTE_LENGTH_SOFT
:
2570 case AL_SAMPLE_LENGTH_SOFT
:
2571 case AL_SEC_LENGTH_SOFT
:
2573 values
[0] = static_cast<int64_t>(GetSourceLength(Source
, prop
));
2576 case AL_SAMPLE_OFFSET_LATENCY_SOFT
:
2578 /* Get the source offset with the clock time first. Then get the clock
2579 * time with the device latency. Order is important.
2581 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2583 std::lock_guard
<std::mutex
> _
{device
->StateLock
};
2584 clocktime
= GetClockLatency(device
, device
->Backend
.get());
2586 if(srcclock
== clocktime
.ClockTime
)
2587 values
[1] = clocktime
.Latency
.count();
2590 /* If the clock time incremented, reduce the latency by that much
2591 * since it's that much closer to the source offset it got earlier.
2593 const nanoseconds diff
{clocktime
.ClockTime
- srcclock
};
2594 values
[1] = nanoseconds
{clocktime
.Latency
- std::min(clocktime
.Latency
, diff
)}.count();
2598 case AL_SAMPLE_OFFSET_CLOCK_SOFT
:
2600 values
[0] = GetSourceSampleOffset(Source
, Context
, &srcclock
);
2601 values
[1] = srcclock
.count();
2604 case AL_SAMPLE_RW_OFFSETS_SOFT
:
2605 if(sBufferSubDataCompat
)
2608 /* FIXME: values[1] should be ahead of values[0] by the device
2609 * update time. It needs to clamp or wrap the length of the buffer
2612 values
[0] = static_cast<int64_t>(GetSourceOffset(Source
, AL_SAMPLE_OFFSET
, Context
));
2613 values
[1] = values
[0];
2617 case AL_SOURCE_RADIUS
: /*AL_BYTE_RW_OFFSETS_SOFT:*/
2618 if(sBufferSubDataCompat
)
2621 /* FIXME: values[1] should be ahead of values[0] by the device
2622 * update time. It needs to clamp or wrap the length of the buffer
2625 values
[0] = static_cast<int64_t>(GetSourceOffset(Source
, AL_BYTE_OFFSET
, Context
));
2626 values
[1] = values
[0];
2631 /* 1x float/double */
2632 case AL_CONE_INNER_ANGLE
:
2633 case AL_CONE_OUTER_ANGLE
:
2638 case AL_REFERENCE_DISTANCE
:
2639 case AL_ROLLOFF_FACTOR
:
2640 case AL_CONE_OUTER_GAIN
:
2641 case AL_MAX_DISTANCE
:
2643 case AL_SAMPLE_OFFSET
:
2644 case AL_BYTE_OFFSET
:
2645 case AL_DOPPLER_FACTOR
:
2646 case AL_AIR_ABSORPTION_FACTOR
:
2647 case AL_ROOM_ROLLOFF_FACTOR
:
2648 case AL_CONE_OUTER_GAINHF
:
2649 case AL_SUPER_STEREO_WIDTH_SOFT
:
2651 if((err
=GetSourcedv(Source
, Context
, prop
, {dvals
, 1u})) != false)
2652 values
[0] = static_cast<int64_t>(dvals
[0]);
2655 /* 3x float/double */
2660 if((err
=GetSourcedv(Source
, Context
, prop
, {dvals
, 3u})) != false)
2662 values
[0] = static_cast<int64_t>(dvals
[0]);
2663 values
[1] = static_cast<int64_t>(dvals
[1]);
2664 values
[2] = static_cast<int64_t>(dvals
[2]);
2668 /* 6x float/double */
2669 case AL_ORIENTATION
:
2671 if((err
=GetSourcedv(Source
, Context
, prop
, {dvals
, 6u})) != false)
2673 values
[0] = static_cast<int64_t>(dvals
[0]);
2674 values
[1] = static_cast<int64_t>(dvals
[1]);
2675 values
[2] = static_cast<int64_t>(dvals
[2]);
2676 values
[3] = static_cast<int64_t>(dvals
[3]);
2677 values
[4] = static_cast<int64_t>(dvals
[4]);
2678 values
[5] = static_cast<int64_t>(dvals
[5]);
2683 case AL_SOURCE_RELATIVE
:
2685 case AL_SOURCE_STATE
:
2686 case AL_BUFFERS_QUEUED
:
2687 case AL_BUFFERS_PROCESSED
:
2688 case AL_SOURCE_TYPE
:
2689 case AL_DIRECT_FILTER_GAINHF_AUTO
:
2690 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
:
2691 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
:
2692 case AL_DIRECT_CHANNELS_SOFT
:
2693 case AL_DISTANCE_MODEL
:
2694 case AL_SOURCE_RESAMPLER_SOFT
:
2695 case AL_SOURCE_SPATIALIZE_SOFT
:
2696 case AL_STEREO_MODE_SOFT
:
2698 if((err
=GetSourceiv(Source
, Context
, prop
, {ivals
, 1u})) != false)
2699 values
[0] = ivals
[0];
2704 case AL_DIRECT_FILTER
:
2706 if((err
=GetSourceiv(Source
, Context
, prop
, {ivals
, 1u})) != false)
2707 values
[0] = static_cast<ALuint
>(ivals
[0]);
2711 case AL_AUXILIARY_SEND_FILTER
:
2713 if((err
=GetSourceiv(Source
, Context
, prop
, {ivals
, 3u})) != false)
2715 values
[0] = static_cast<ALuint
>(ivals
[0]);
2716 values
[1] = static_cast<ALuint
>(ivals
[1]);
2717 values
[2] = static_cast<ALuint
>(ivals
[2]);
2721 case AL_SEC_OFFSET_LATENCY_SOFT
:
2722 case AL_SEC_OFFSET_CLOCK_SOFT
:
2723 break; /* Double only */
2724 case AL_STEREO_ANGLES
:
2725 break; /* Float/double only */
2728 ERR("Unexpected property: 0x%04x\n", prop
);
2729 Context
->setError(AL_INVALID_ENUM
, "Invalid source integer64 property 0x%04x", prop
);
2732 catch(check_exception
&) {
2737 void StartSources(ALCcontext
*const context
, const al::span
<ALsource
*> srchandles
,
2738 const nanoseconds start_time
=nanoseconds::min())
2740 ALCdevice
*device
{context
->mALDevice
.get()};
2741 /* If the device is disconnected, and voices stop on disconnect, go right
2744 if(!device
->Connected
.load(std::memory_order_acquire
)) UNLIKELY
2746 if(context
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
2748 for(ALsource
*source
: srchandles
)
2750 /* TODO: Send state change event? */
2751 source
->Offset
= 0.0;
2752 source
->OffsetType
= AL_NONE
;
2753 source
->state
= AL_STOPPED
;
2759 /* Count the number of reusable voices. */
2760 auto voicelist
= context
->getVoicesSpan();
2761 size_t free_voices
{0};
2762 for(const Voice
*voice
: voicelist
)
2764 free_voices
+= (voice
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2765 && voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2766 && voice
->mPendingChange
.load(std::memory_order_relaxed
) == false);
2767 if(free_voices
== srchandles
.size())
2770 if(srchandles
.size() != free_voices
) UNLIKELY
2772 const size_t inc_amount
{srchandles
.size() - free_voices
};
2773 auto &allvoices
= *context
->mVoices
.load(std::memory_order_relaxed
);
2774 if(inc_amount
> allvoices
.size() - voicelist
.size())
2776 /* Increase the number of voices to handle the request. */
2777 context
->allocVoices(inc_amount
- (allvoices
.size() - voicelist
.size()));
2779 context
->mActiveVoiceCount
.fetch_add(inc_amount
, std::memory_order_release
);
2780 voicelist
= context
->getVoicesSpan();
2783 auto voiceiter
= voicelist
.begin();
2785 VoiceChange
*tail
{}, *cur
{};
2786 for(ALsource
*source
: srchandles
)
2788 /* Check that there is a queue containing at least one valid, non zero
2791 auto find_buffer
= [](ALbufferQueueItem
&entry
) noexcept
2792 { return entry
.mSampleLen
!= 0 || entry
.mCallback
!= nullptr; };
2793 auto BufferList
= std::find_if(source
->mQueue
.begin(), source
->mQueue
.end(), find_buffer
);
2795 /* If there's nothing to play, go right to stopped. */
2796 if(BufferList
== source
->mQueue
.end()) UNLIKELY
2798 /* NOTE: A source without any playable buffers should not have a
2799 * Voice since it shouldn't be in a playing or paused state. So
2800 * there's no need to look up its voice and clear the source.
2802 source
->Offset
= 0.0;
2803 source
->OffsetType
= AL_NONE
;
2804 source
->state
= AL_STOPPED
;
2809 cur
= tail
= GetVoiceChanger(context
);
2812 cur
->mNext
.store(GetVoiceChanger(context
), std::memory_order_relaxed
);
2813 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
2816 Voice
*voice
{GetSourceVoice(source
, context
)};
2817 switch(GetSourceState(source
, voice
))
2820 /* A source that's paused simply resumes. If there's no voice, it
2821 * was lost from a disconnect, so just start over with a new one.
2823 cur
->mOldVoice
= nullptr;
2825 cur
->mVoice
= voice
;
2826 cur
->mSourceID
= source
->id
;
2827 cur
->mState
= VChangeState::Play
;
2828 source
->state
= AL_PLAYING
;
2830 if(context
->hasEax())
2831 source
->eaxCommit();
2832 #endif // ALSOFT_EAX
2836 /* A source that's already playing is restarted from the beginning.
2837 * Stop the current voice and start a new one so it properly cross-
2838 * fades back to the beginning.
2841 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
2842 cur
->mOldVoice
= voice
;
2847 assert(voice
== nullptr);
2848 cur
->mOldVoice
= nullptr;
2850 if(context
->hasEax())
2851 source
->eaxCommit();
2852 #endif // ALSOFT_EAX
2856 /* Find the next unused voice to play this source with. */
2857 for(;voiceiter
!= voicelist
.end();++voiceiter
,++vidx
)
2859 Voice
*v
{*voiceiter
};
2860 if(v
->mPlayState
.load(std::memory_order_acquire
) == Voice::Stopped
2861 && v
->mSourceID
.load(std::memory_order_relaxed
) == 0u
2862 && v
->mPendingChange
.load(std::memory_order_relaxed
) == false)
2868 ASSUME(voice
!= nullptr);
2870 voice
->mPosition
.store(0, std::memory_order_relaxed
);
2871 voice
->mPositionFrac
.store(0, std::memory_order_relaxed
);
2872 voice
->mCurrentBuffer
.store(&source
->mQueue
.front(), std::memory_order_relaxed
);
2873 voice
->mStartTime
= start_time
;
2874 voice
->mFlags
.reset();
2875 /* A source that's not playing or paused has any offset applied when it
2878 if(const ALenum offsettype
{source
->OffsetType
})
2880 const double offset
{source
->Offset
};
2881 source
->OffsetType
= AL_NONE
;
2882 source
->Offset
= 0.0;
2883 if(auto vpos
= GetSampleOffset(source
->mQueue
, offsettype
, offset
))
2885 voice
->mPosition
.store(vpos
->pos
, std::memory_order_relaxed
);
2886 voice
->mPositionFrac
.store(vpos
->frac
, std::memory_order_relaxed
);
2887 voice
->mCurrentBuffer
.store(vpos
->bufferitem
, std::memory_order_relaxed
);
2888 if(vpos
->pos
> 0 || (vpos
->pos
== 0 && vpos
->frac
> 0)
2889 || vpos
->bufferitem
!= &source
->mQueue
.front())
2890 voice
->mFlags
.set(VoiceIsFading
);
2893 InitVoice(voice
, source
, al::to_address(BufferList
), context
, device
);
2895 source
->VoiceIdx
= vidx
;
2896 source
->state
= AL_PLAYING
;
2898 cur
->mVoice
= voice
;
2899 cur
->mSourceID
= source
->id
;
2900 cur
->mState
= VChangeState::Play
;
2903 SendVoiceChanges(context
, tail
);
2908 AL_API
void AL_APIENTRY
alGenSources(ALsizei n
, ALuint
*sources
)
2911 ContextRef context
{GetContextRef()};
2912 if(!context
) UNLIKELY
return;
2915 context
->setError(AL_INVALID_VALUE
, "Generating %d sources", n
);
2916 if(n
<= 0) UNLIKELY
return;
2918 std::unique_lock
<std::mutex
> srclock
{context
->mSourceLock
};
2919 ALCdevice
*device
{context
->mALDevice
.get()};
2920 if(static_cast<ALuint
>(n
) > device
->SourcesMax
-context
->mNumSources
)
2922 context
->setError(AL_OUT_OF_MEMORY
, "Exceeding %u source limit (%u + %d)",
2923 device
->SourcesMax
, context
->mNumSources
, n
);
2926 if(!EnsureSources(context
.get(), static_cast<ALuint
>(n
)))
2928 context
->setError(AL_OUT_OF_MEMORY
, "Failed to allocate %d source%s", n
, (n
==1)?"":"s");
2934 ALsource
*source
{AllocSource(context
.get())};
2935 sources
[0] = source
->id
;
2938 source
->eaxInitialize(context
.get());
2939 #endif // ALSOFT_EAX
2943 al::vector
<ALuint
> ids
;
2944 ids
.reserve(static_cast<ALuint
>(n
));
2946 ALsource
*source
{AllocSource(context
.get())};
2947 ids
.emplace_back(source
->id
);
2950 source
->eaxInitialize(context
.get());
2951 #endif // ALSOFT_EAX
2953 std::copy(ids
.cbegin(), ids
.cend(), sources
);
2958 AL_API
void AL_APIENTRY
alDeleteSources(ALsizei n
, const ALuint
*sources
)
2961 ContextRef context
{GetContextRef()};
2962 if(!context
) UNLIKELY
return;
2965 context
->setError(AL_INVALID_VALUE
, "Deleting %d sources", n
);
2966 if(n
<= 0) UNLIKELY
return;
2968 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
2970 /* Check that all Sources are valid */
2971 auto validate_source
= [&context
](const ALuint sid
) -> bool
2972 { return LookupSource(context
.get(), sid
) != nullptr; };
2974 const ALuint
*sources_end
= sources
+ n
;
2975 auto invsrc
= std::find_if_not(sources
, sources_end
, validate_source
);
2976 if(invsrc
!= sources_end
) UNLIKELY
2977 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", *invsrc
);
2979 /* All good. Delete source IDs. */
2980 auto delete_source
= [&context
](const ALuint sid
) -> void
2982 ALsource
*src
{LookupSource(context
.get(), sid
)};
2983 if(src
) FreeSource(context
.get(), src
);
2985 std::for_each(sources
, sources_end
, delete_source
);
2989 AL_API ALboolean AL_APIENTRY
alIsSource(ALuint source
)
2992 ContextRef context
{GetContextRef()};
2995 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
2996 if(LookupSource(context
.get(), source
) != nullptr)
3004 AL_API
void AL_APIENTRY
alSourcef(ALuint source
, ALenum param
, ALfloat value
)
3007 ContextRef context
{GetContextRef()};
3008 if(!context
) UNLIKELY
return;
3010 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3011 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3012 ALsource
*Source
= LookupSource(context
.get(), source
);
3013 if(!Source
) UNLIKELY
3014 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3016 SetSourcefv(Source
, context
.get(), static_cast<SourceProp
>(param
), {&value
, 1u});
3020 AL_API
void AL_APIENTRY
alSource3f(ALuint source
, ALenum param
, ALfloat value1
, ALfloat value2
, ALfloat value3
)
3023 ContextRef context
{GetContextRef()};
3024 if(!context
) UNLIKELY
return;
3026 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3027 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3028 ALsource
*Source
= LookupSource(context
.get(), source
);
3029 if(!Source
) UNLIKELY
3030 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3033 const float fvals
[3]{ value1
, value2
, value3
};
3034 SetSourcefv(Source
, context
.get(), static_cast<SourceProp
>(param
), fvals
);
3039 AL_API
void AL_APIENTRY
alSourcefv(ALuint source
, ALenum param
, const ALfloat
*values
)
3042 ContextRef context
{GetContextRef()};
3043 if(!context
) UNLIKELY
return;
3045 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3046 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3047 ALsource
*Source
= LookupSource(context
.get(), source
);
3048 if(!Source
) UNLIKELY
3049 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3050 if(!values
) UNLIKELY
3051 return context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3053 const ALuint count
{FloatValsByProp(param
)};
3054 SetSourcefv(Source
, context
.get(), static_cast<SourceProp
>(param
), {values
, count
});
3059 AL_API
void AL_APIENTRY
alSourcedSOFT(ALuint source
, ALenum param
, ALdouble value
)
3062 ContextRef context
{GetContextRef()};
3063 if(!context
) UNLIKELY
return;
3065 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3066 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3067 ALsource
*Source
= LookupSource(context
.get(), source
);
3068 if(!Source
) UNLIKELY
3069 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3072 const float fval
[1]{static_cast<float>(value
)};
3073 SetSourcefv(Source
, context
.get(), static_cast<SourceProp
>(param
), fval
);
3078 AL_API
void AL_APIENTRY
alSource3dSOFT(ALuint source
, ALenum param
, ALdouble value1
, ALdouble value2
, ALdouble value3
)
3081 ContextRef context
{GetContextRef()};
3082 if(!context
) UNLIKELY
return;
3084 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3085 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3086 ALsource
*Source
= LookupSource(context
.get(), source
);
3087 if(!Source
) UNLIKELY
3088 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3091 const float fvals
[3]{static_cast<float>(value1
), static_cast<float>(value2
),
3092 static_cast<float>(value3
)};
3093 SetSourcefv(Source
, context
.get(), static_cast<SourceProp
>(param
), fvals
);
3098 AL_API
void AL_APIENTRY
alSourcedvSOFT(ALuint source
, ALenum param
, const ALdouble
*values
)
3101 ContextRef context
{GetContextRef()};
3102 if(!context
) UNLIKELY
return;
3104 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3105 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3106 ALsource
*Source
= LookupSource(context
.get(), source
);
3107 if(!Source
) UNLIKELY
3108 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3109 if(!values
) UNLIKELY
3110 return context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3112 const ALuint count
{DoubleValsByProp(param
)};
3113 float fvals
[MaxValues
];
3114 std::copy_n(values
, count
, fvals
);
3115 SetSourcefv(Source
, context
.get(), static_cast<SourceProp
>(param
), {fvals
, count
});
3120 AL_API
void AL_APIENTRY
alSourcei(ALuint source
, ALenum param
, ALint value
)
3123 ContextRef context
{GetContextRef()};
3124 if(!context
) UNLIKELY
return;
3126 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3127 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3128 ALsource
*Source
= LookupSource(context
.get(), source
);
3129 if(!Source
) UNLIKELY
3130 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3132 SetSourceiv(Source
, context
.get(), static_cast<SourceProp
>(param
), {&value
, 1u});
3136 AL_API
void AL_APIENTRY
alSource3i(ALuint source
, ALenum param
, ALint value1
, ALint value2
, ALint value3
)
3139 ContextRef context
{GetContextRef()};
3140 if(!context
) UNLIKELY
return;
3142 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3143 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3144 ALsource
*Source
= LookupSource(context
.get(), source
);
3145 if(!Source
) UNLIKELY
3146 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3149 const int ivals
[3]{ value1
, value2
, value3
};
3150 SetSourceiv(Source
, context
.get(), static_cast<SourceProp
>(param
), ivals
);
3155 AL_API
void AL_APIENTRY
alSourceiv(ALuint source
, ALenum param
, const ALint
*values
)
3158 ContextRef context
{GetContextRef()};
3159 if(!context
) UNLIKELY
return;
3161 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3162 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3163 ALsource
*Source
= LookupSource(context
.get(), source
);
3164 if(!Source
) UNLIKELY
3165 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3166 if(!values
) UNLIKELY
3167 return context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3169 const ALuint count
{IntValsByProp(param
)};
3170 SetSourceiv(Source
, context
.get(), static_cast<SourceProp
>(param
), {values
, count
});
3175 AL_API
void AL_APIENTRY
alSourcei64SOFT(ALuint source
, ALenum param
, ALint64SOFT value
)
3178 ContextRef context
{GetContextRef()};
3179 if(!context
) UNLIKELY
return;
3181 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3182 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3183 ALsource
*Source
{LookupSource(context
.get(), source
)};
3184 if(!Source
) UNLIKELY
3185 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3187 SetSourcei64v(Source
, context
.get(), static_cast<SourceProp
>(param
), {&value
, 1u});
3191 AL_API
void AL_APIENTRY
alSource3i64SOFT(ALuint source
, ALenum param
, ALint64SOFT value1
, ALint64SOFT value2
, ALint64SOFT value3
)
3194 ContextRef context
{GetContextRef()};
3195 if(!context
) UNLIKELY
return;
3197 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3198 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3199 ALsource
*Source
{LookupSource(context
.get(), source
)};
3200 if(!Source
) UNLIKELY
3201 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3204 const int64_t i64vals
[3]{ value1
, value2
, value3
};
3205 SetSourcei64v(Source
, context
.get(), static_cast<SourceProp
>(param
), i64vals
);
3210 AL_API
void AL_APIENTRY
alSourcei64vSOFT(ALuint source
, ALenum param
, const ALint64SOFT
*values
)
3213 ContextRef context
{GetContextRef()};
3214 if(!context
) UNLIKELY
return;
3216 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
3217 std::lock_guard
<std::mutex
> __
{context
->mSourceLock
};
3218 ALsource
*Source
{LookupSource(context
.get(), source
)};
3219 if(!Source
) UNLIKELY
3220 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3221 if(!values
) UNLIKELY
3222 return context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3224 const ALuint count
{Int64ValsByProp(param
)};
3225 SetSourcei64v(Source
, context
.get(), static_cast<SourceProp
>(param
), {values
, count
});
3230 AL_API
void AL_APIENTRY
alGetSourcef(ALuint source
, ALenum param
, ALfloat
*value
)
3233 ContextRef context
{GetContextRef()};
3234 if(!context
) UNLIKELY
return;
3236 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3237 ALsource
*Source
{LookupSource(context
.get(), source
)};
3238 if(!Source
) UNLIKELY
3239 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3240 else if(!value
) UNLIKELY
3241 context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3245 if(GetSourcedv(Source
, context
.get(), static_cast<SourceProp
>(param
), dval
))
3246 *value
= static_cast<float>(dval
[0]);
3251 AL_API
void AL_APIENTRY
alGetSource3f(ALuint source
, ALenum param
, ALfloat
*value1
, ALfloat
*value2
, ALfloat
*value3
)
3254 ContextRef context
{GetContextRef()};
3255 if(!context
) UNLIKELY
return;
3257 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3258 ALsource
*Source
{LookupSource(context
.get(), source
)};
3259 if(!Source
) UNLIKELY
3260 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3261 else if(!(value1
&& value2
&& value3
)) UNLIKELY
3262 context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3266 if(GetSourcedv(Source
, context
.get(), static_cast<SourceProp
>(param
), dvals
))
3268 *value1
= static_cast<float>(dvals
[0]);
3269 *value2
= static_cast<float>(dvals
[1]);
3270 *value3
= static_cast<float>(dvals
[2]);
3276 AL_API
void AL_APIENTRY
alGetSourcefv(ALuint source
, ALenum param
, ALfloat
*values
)
3279 ContextRef context
{GetContextRef()};
3280 if(!context
) UNLIKELY
return;
3282 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3283 ALsource
*Source
{LookupSource(context
.get(), source
)};
3284 if(!Source
) UNLIKELY
3285 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3286 if(!values
) UNLIKELY
3287 return context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3289 const ALuint count
{FloatValsByProp(param
)};
3290 double dvals
[MaxValues
];
3291 if(GetSourcedv(Source
, context
.get(), static_cast<SourceProp
>(param
), {dvals
, count
}))
3292 std::copy_n(dvals
, count
, values
);
3297 AL_API
void AL_APIENTRY
alGetSourcedSOFT(ALuint source
, ALenum param
, ALdouble
*value
)
3300 ContextRef context
{GetContextRef()};
3301 if(!context
) UNLIKELY
return;
3303 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3304 ALsource
*Source
{LookupSource(context
.get(), source
)};
3305 if(!Source
) UNLIKELY
3306 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3307 else if(!value
) UNLIKELY
3308 context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3310 GetSourcedv(Source
, context
.get(), static_cast<SourceProp
>(param
), {value
, 1u});
3314 AL_API
void AL_APIENTRY
alGetSource3dSOFT(ALuint source
, ALenum param
, ALdouble
*value1
, ALdouble
*value2
, ALdouble
*value3
)
3317 ContextRef context
{GetContextRef()};
3318 if(!context
) UNLIKELY
return;
3320 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3321 ALsource
*Source
{LookupSource(context
.get(), source
)};
3322 if(!Source
) UNLIKELY
3323 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3324 else if(!(value1
&& value2
&& value3
)) UNLIKELY
3325 context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3329 if(GetSourcedv(Source
, context
.get(), static_cast<SourceProp
>(param
), dvals
))
3339 AL_API
void AL_APIENTRY
alGetSourcedvSOFT(ALuint source
, ALenum param
, ALdouble
*values
)
3342 ContextRef context
{GetContextRef()};
3343 if(!context
) UNLIKELY
return;
3345 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3346 ALsource
*Source
{LookupSource(context
.get(), source
)};
3347 if(!Source
) UNLIKELY
3348 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3349 if(!values
) UNLIKELY
3350 return context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3352 const ALuint count
{DoubleValsByProp(param
)};
3353 GetSourcedv(Source
, context
.get(), static_cast<SourceProp
>(param
), {values
, count
});
3358 AL_API
void AL_APIENTRY
alGetSourcei(ALuint source
, ALenum param
, ALint
*value
)
3361 ContextRef context
{GetContextRef()};
3362 if(!context
) UNLIKELY
return;
3364 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3365 ALsource
*Source
{LookupSource(context
.get(), source
)};
3366 if(!Source
) UNLIKELY
3367 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3368 else if(!value
) UNLIKELY
3369 context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3371 GetSourceiv(Source
, context
.get(), static_cast<SourceProp
>(param
), {value
, 1u});
3375 AL_API
void AL_APIENTRY
alGetSource3i(ALuint source
, ALenum param
, ALint
*value1
, ALint
*value2
, ALint
*value3
)
3378 ContextRef context
{GetContextRef()};
3379 if(!context
) UNLIKELY
return;
3381 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3382 ALsource
*Source
{LookupSource(context
.get(), source
)};
3383 if(!Source
) UNLIKELY
3384 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3385 else if(!(value1
&& value2
&& value3
)) UNLIKELY
3386 context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3390 if(GetSourceiv(Source
, context
.get(), static_cast<SourceProp
>(param
), ivals
))
3400 AL_API
void AL_APIENTRY
alGetSourceiv(ALuint source
, ALenum param
, ALint
*values
)
3403 ContextRef context
{GetContextRef()};
3404 if(!context
) UNLIKELY
return;
3406 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3407 ALsource
*Source
{LookupSource(context
.get(), source
)};
3408 if(!Source
) UNLIKELY
3409 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3410 if(!values
) UNLIKELY
3411 return context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3413 const ALuint count
{IntValsByProp(param
)};
3414 GetSourceiv(Source
, context
.get(), static_cast<SourceProp
>(param
), {values
, count
});
3419 AL_API
void AL_APIENTRY
alGetSourcei64SOFT(ALuint source
, ALenum param
, ALint64SOFT
*value
)
3422 ContextRef context
{GetContextRef()};
3423 if(!context
) UNLIKELY
return;
3425 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3426 ALsource
*Source
{LookupSource(context
.get(), source
)};
3427 if(!Source
) UNLIKELY
3428 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3429 else if(!value
) UNLIKELY
3430 context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3432 GetSourcei64v(Source
, context
.get(), static_cast<SourceProp
>(param
), {value
, 1u});
3436 AL_API
void AL_APIENTRY
alGetSource3i64SOFT(ALuint source
, ALenum param
, ALint64SOFT
*value1
, ALint64SOFT
*value2
, ALint64SOFT
*value3
)
3439 ContextRef context
{GetContextRef()};
3440 if(!context
) UNLIKELY
return;
3442 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3443 ALsource
*Source
{LookupSource(context
.get(), source
)};
3444 if(!Source
) UNLIKELY
3445 context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3446 else if(!(value1
&& value2
&& value3
)) UNLIKELY
3447 context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3451 if(GetSourcei64v(Source
, context
.get(), static_cast<SourceProp
>(param
), i64vals
))
3453 *value1
= i64vals
[0];
3454 *value2
= i64vals
[1];
3455 *value3
= i64vals
[2];
3461 AL_API
void AL_APIENTRY
alGetSourcei64vSOFT(ALuint source
, ALenum param
, ALint64SOFT
*values
)
3464 ContextRef context
{GetContextRef()};
3465 if(!context
) UNLIKELY
return;
3467 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3468 ALsource
*Source
{LookupSource(context
.get(), source
)};
3469 if(!Source
) UNLIKELY
3470 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3471 if(!values
) UNLIKELY
3472 return context
->setError(AL_INVALID_VALUE
, "NULL pointer");
3474 const ALuint count
{Int64ValsByProp(param
)};
3475 GetSourcei64v(Source
, context
.get(), static_cast<SourceProp
>(param
), {values
, count
});
3480 AL_API
void AL_APIENTRY
alSourcePlay(ALuint source
)
3483 ContextRef context
{GetContextRef()};
3484 if(!context
) UNLIKELY
return;
3486 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3487 ALsource
*srchandle
{LookupSource(context
.get(), source
)};
3489 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3491 StartSources(context
.get(), {&srchandle
, 1});
3495 void AL_APIENTRY
alSourcePlayAtTimeSOFT(ALuint source
, ALint64SOFT start_time
)
3498 ContextRef context
{GetContextRef()};
3499 if(!context
) UNLIKELY
return;
3501 if(start_time
< 0) UNLIKELY
3502 return context
->setError(AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
);
3504 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3505 ALsource
*srchandle
{LookupSource(context
.get(), source
)};
3507 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", source
);
3509 StartSources(context
.get(), {&srchandle
, 1}, nanoseconds
{start_time
});
3513 AL_API
void AL_APIENTRY
alSourcePlayv(ALsizei n
, const ALuint
*sources
)
3516 ContextRef context
{GetContextRef()};
3517 if(!context
) UNLIKELY
return;
3520 context
->setError(AL_INVALID_VALUE
, "Playing %d sources", n
);
3521 if(n
<= 0) UNLIKELY
return;
3523 al::vector
<ALsource
*> extra_sources
;
3524 std::array
<ALsource
*,8> source_storage
;
3525 al::span
<ALsource
*> srchandles
;
3526 if(static_cast<ALuint
>(n
) <= source_storage
.size()) LIKELY
3527 srchandles
= {source_storage
.data(), static_cast<ALuint
>(n
)};
3530 extra_sources
.resize(static_cast<ALuint
>(n
));
3531 srchandles
= {extra_sources
.data(), extra_sources
.size()};
3534 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3535 for(auto &srchdl
: srchandles
)
3537 srchdl
= LookupSource(context
.get(), *sources
);
3538 if(!srchdl
) UNLIKELY
3539 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", *sources
);
3543 StartSources(context
.get(), srchandles
);
3547 void AL_APIENTRY
alSourcePlayAtTimevSOFT(ALsizei n
, const ALuint
*sources
, ALint64SOFT start_time
)
3550 ContextRef context
{GetContextRef()};
3551 if(!context
) UNLIKELY
return;
3554 context
->setError(AL_INVALID_VALUE
, "Playing %d sources", n
);
3555 if(n
<= 0) UNLIKELY
return;
3557 if(start_time
< 0) UNLIKELY
3558 return context
->setError(AL_INVALID_VALUE
, "Invalid time point %" PRId64
, start_time
);
3560 al::vector
<ALsource
*> extra_sources
;
3561 std::array
<ALsource
*,8> source_storage
;
3562 al::span
<ALsource
*> srchandles
;
3563 if(static_cast<ALuint
>(n
) <= source_storage
.size()) LIKELY
3564 srchandles
= {source_storage
.data(), static_cast<ALuint
>(n
)};
3567 extra_sources
.resize(static_cast<ALuint
>(n
));
3568 srchandles
= {extra_sources
.data(), extra_sources
.size()};
3571 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3572 for(auto &srchdl
: srchandles
)
3574 srchdl
= LookupSource(context
.get(), *sources
);
3576 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", *sources
);
3580 StartSources(context
.get(), srchandles
, nanoseconds
{start_time
});
3585 AL_API
void AL_APIENTRY
alSourcePause(ALuint source
)
3587 { alSourcePausev(1, &source
); }
3590 AL_API
void AL_APIENTRY
alSourcePausev(ALsizei n
, const ALuint
*sources
)
3593 ContextRef context
{GetContextRef()};
3594 if(!context
) UNLIKELY
return;
3597 context
->setError(AL_INVALID_VALUE
, "Pausing %d sources", n
);
3598 if(n
<= 0) UNLIKELY
return;
3600 al::vector
<ALsource
*> extra_sources
;
3601 std::array
<ALsource
*,8> source_storage
;
3602 al::span
<ALsource
*> srchandles
;
3603 if(static_cast<ALuint
>(n
) <= source_storage
.size()) LIKELY
3604 srchandles
= {source_storage
.data(), static_cast<ALuint
>(n
)};
3607 extra_sources
.resize(static_cast<ALuint
>(n
));
3608 srchandles
= {extra_sources
.data(), extra_sources
.size()};
3611 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3612 for(auto &srchdl
: srchandles
)
3614 srchdl
= LookupSource(context
.get(), *sources
);
3616 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", *sources
);
3620 /* Pausing has to be done in two steps. First, for each source that's
3621 * detected to be playing, chamge the voice (asynchronously) to
3624 VoiceChange
*tail
{}, *cur
{};
3625 for(ALsource
*source
: srchandles
)
3627 Voice
*voice
{GetSourceVoice(source
, context
.get())};
3628 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3631 cur
= tail
= GetVoiceChanger(context
.get());
3634 cur
->mNext
.store(GetVoiceChanger(context
.get()), std::memory_order_relaxed
);
3635 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3637 cur
->mVoice
= voice
;
3638 cur
->mSourceID
= source
->id
;
3639 cur
->mState
= VChangeState::Pause
;
3644 SendVoiceChanges(context
.get(), tail
);
3645 /* Second, now that the voice changes have been sent, because it's
3646 * possible that the voice stopped after it was detected playing and
3647 * before the voice got paused, recheck that the source is still
3648 * considered playing and set it to paused if so.
3650 for(ALsource
*source
: srchandles
)
3652 Voice
*voice
{GetSourceVoice(source
, context
.get())};
3653 if(GetSourceState(source
, voice
) == AL_PLAYING
)
3654 source
->state
= AL_PAUSED
;
3661 AL_API
void AL_APIENTRY
alSourceStop(ALuint source
)
3663 { alSourceStopv(1, &source
); }
3666 AL_API
void AL_APIENTRY
alSourceStopv(ALsizei n
, const ALuint
*sources
)
3669 ContextRef context
{GetContextRef()};
3670 if(!context
) UNLIKELY
return;
3673 context
->setError(AL_INVALID_VALUE
, "Stopping %d sources", n
);
3674 if(n
<= 0) UNLIKELY
return;
3676 al::vector
<ALsource
*> extra_sources
;
3677 std::array
<ALsource
*,8> source_storage
;
3678 al::span
<ALsource
*> srchandles
;
3679 if(static_cast<ALuint
>(n
) <= source_storage
.size()) LIKELY
3680 srchandles
= {source_storage
.data(), static_cast<ALuint
>(n
)};
3683 extra_sources
.resize(static_cast<ALuint
>(n
));
3684 srchandles
= {extra_sources
.data(), extra_sources
.size()};
3687 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3688 for(auto &srchdl
: srchandles
)
3690 srchdl
= LookupSource(context
.get(), *sources
);
3692 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", *sources
);
3696 VoiceChange
*tail
{}, *cur
{};
3697 for(ALsource
*source
: srchandles
)
3699 if(Voice
*voice
{GetSourceVoice(source
, context
.get())})
3702 cur
= tail
= GetVoiceChanger(context
.get());
3705 cur
->mNext
.store(GetVoiceChanger(context
.get()), std::memory_order_relaxed
);
3706 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3708 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3709 cur
->mVoice
= voice
;
3710 cur
->mSourceID
= source
->id
;
3711 cur
->mState
= VChangeState::Stop
;
3712 source
->state
= AL_STOPPED
;
3714 source
->Offset
= 0.0;
3715 source
->OffsetType
= AL_NONE
;
3716 source
->VoiceIdx
= INVALID_VOICE_IDX
;
3719 SendVoiceChanges(context
.get(), tail
);
3724 AL_API
void AL_APIENTRY
alSourceRewind(ALuint source
)
3726 { alSourceRewindv(1, &source
); }
3729 AL_API
void AL_APIENTRY
alSourceRewindv(ALsizei n
, const ALuint
*sources
)
3732 ContextRef context
{GetContextRef()};
3733 if(!context
) UNLIKELY
return;
3736 context
->setError(AL_INVALID_VALUE
, "Rewinding %d sources", n
);
3737 if(n
<= 0) UNLIKELY
return;
3739 al::vector
<ALsource
*> extra_sources
;
3740 std::array
<ALsource
*,8> source_storage
;
3741 al::span
<ALsource
*> srchandles
;
3742 if(static_cast<ALuint
>(n
) <= source_storage
.size()) LIKELY
3743 srchandles
= {source_storage
.data(), static_cast<ALuint
>(n
)};
3746 extra_sources
.resize(static_cast<ALuint
>(n
));
3747 srchandles
= {extra_sources
.data(), extra_sources
.size()};
3750 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3751 for(auto &srchdl
: srchandles
)
3753 srchdl
= LookupSource(context
.get(), *sources
);
3755 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", *sources
);
3759 VoiceChange
*tail
{}, *cur
{};
3760 for(ALsource
*source
: srchandles
)
3762 Voice
*voice
{GetSourceVoice(source
, context
.get())};
3763 if(source
->state
!= AL_INITIAL
)
3766 cur
= tail
= GetVoiceChanger(context
.get());
3769 cur
->mNext
.store(GetVoiceChanger(context
.get()), std::memory_order_relaxed
);
3770 cur
= cur
->mNext
.load(std::memory_order_relaxed
);
3773 voice
->mPendingChange
.store(true, std::memory_order_relaxed
);
3774 cur
->mVoice
= voice
;
3775 cur
->mSourceID
= source
->id
;
3776 cur
->mState
= VChangeState::Reset
;
3777 source
->state
= AL_INITIAL
;
3779 source
->Offset
= 0.0;
3780 source
->OffsetType
= AL_NONE
;
3781 source
->VoiceIdx
= INVALID_VOICE_IDX
;
3784 SendVoiceChanges(context
.get(), tail
);
3789 AL_API
void AL_APIENTRY
alSourceQueueBuffers(ALuint src
, ALsizei nb
, const ALuint
*buffers
)
3792 ContextRef context
{GetContextRef()};
3793 if(!context
) UNLIKELY
return;
3796 context
->setError(AL_INVALID_VALUE
, "Queueing %d buffers", nb
);
3797 if(nb
<= 0) UNLIKELY
return;
3799 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3800 ALsource
*source
{LookupSource(context
.get(),src
)};
3801 if(!source
) UNLIKELY
3802 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", src
);
3804 /* Can't queue on a Static Source */
3805 if(source
->SourceType
== AL_STATIC
) UNLIKELY
3806 return context
->setError(AL_INVALID_OPERATION
, "Queueing onto static source %u", src
);
3808 /* Check for a valid Buffer, for its frequency and format */
3809 ALCdevice
*device
{context
->mALDevice
.get()};
3810 ALbuffer
*BufferFmt
{nullptr};
3811 for(auto &item
: source
->mQueue
)
3813 BufferFmt
= item
.mBuffer
;
3814 if(BufferFmt
) break;
3817 std::unique_lock
<std::mutex
> buflock
{device
->BufferLock
};
3818 const size_t NewListStart
{source
->mQueue
.size()};
3819 ALbufferQueueItem
*BufferList
{nullptr};
3820 for(ALsizei i
{0};i
< nb
;i
++)
3822 bool fmt_mismatch
{false};
3823 ALbuffer
*buffer
{nullptr};
3824 if(buffers
[i
] && (buffer
=LookupBuffer(device
, buffers
[i
])) == nullptr)
3826 context
->setError(AL_INVALID_NAME
, "Queueing invalid buffer ID %u", buffers
[i
]);
3831 if(buffer
->mSampleRate
< 1)
3833 context
->setError(AL_INVALID_OPERATION
, "Queueing buffer %u with no format",
3837 if(buffer
->mCallback
)
3839 context
->setError(AL_INVALID_OPERATION
, "Queueing callback buffer %u", buffer
->id
);
3842 if(buffer
->MappedAccess
!= 0 && !(buffer
->MappedAccess
&AL_MAP_PERSISTENT_BIT_SOFT
))
3844 context
->setError(AL_INVALID_OPERATION
,
3845 "Queueing non-persistently mapped buffer %u", buffer
->id
);
3850 source
->mQueue
.emplace_back();
3852 BufferList
= &source
->mQueue
.back();
3855 auto &item
= source
->mQueue
.back();
3856 BufferList
->mNext
.store(&item
, std::memory_order_relaxed
);
3859 if(!buffer
) continue;
3860 BufferList
->mBlockAlign
= buffer
->mBlockAlign
;
3861 BufferList
->mSampleLen
= buffer
->mSampleLen
;
3862 BufferList
->mLoopEnd
= buffer
->mSampleLen
;
3863 BufferList
->mSamples
= buffer
->mData
.data();
3864 BufferList
->mBuffer
= buffer
;
3865 IncrementRef(buffer
->ref
);
3867 if(BufferFmt
== nullptr)
3871 fmt_mismatch
|= BufferFmt
->mSampleRate
!= buffer
->mSampleRate
;
3872 fmt_mismatch
|= BufferFmt
->mChannels
!= buffer
->mChannels
;
3873 fmt_mismatch
|= BufferFmt
->mType
!= buffer
->mType
;
3874 if(BufferFmt
->isBFormat())
3876 fmt_mismatch
|= BufferFmt
->mAmbiLayout
!= buffer
->mAmbiLayout
;
3877 fmt_mismatch
|= BufferFmt
->mAmbiScaling
!= buffer
->mAmbiScaling
;
3879 fmt_mismatch
|= BufferFmt
->mAmbiOrder
!= buffer
->mAmbiOrder
;
3881 if(fmt_mismatch
) UNLIKELY
3883 context
->setError(AL_INVALID_OPERATION
, "Queueing buffer with mismatched format\n"
3884 " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt
->mSampleRate
,
3885 NameFromFormat(BufferFmt
->mType
), NameFromFormat(BufferFmt
->mChannels
),
3886 buffer
->mSampleRate
, NameFromFormat(buffer
->mType
),
3887 NameFromFormat(buffer
->mChannels
));
3890 /* A buffer failed (invalid ID or format), so unlock and release
3891 * each buffer we had.
3893 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3894 for(;iter
!= source
->mQueue
.end();++iter
)
3896 if(ALbuffer
*buf
{iter
->mBuffer
})
3897 DecrementRef(buf
->ref
);
3899 source
->mQueue
.resize(NewListStart
);
3903 /* All buffers good. */
3906 /* Source is now streaming */
3907 source
->SourceType
= AL_STREAMING
;
3909 if(NewListStart
!= 0)
3911 auto iter
= source
->mQueue
.begin() + ptrdiff_t(NewListStart
);
3912 (iter
-1)->mNext
.store(al::to_address(iter
), std::memory_order_release
);
3917 AL_API
void AL_APIENTRY
alSourceUnqueueBuffers(ALuint src
, ALsizei nb
, ALuint
*buffers
)
3920 ContextRef context
{GetContextRef()};
3921 if(!context
) UNLIKELY
return;
3924 context
->setError(AL_INVALID_VALUE
, "Unqueueing %d buffers", nb
);
3925 if(nb
<= 0) UNLIKELY
return;
3927 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
3928 ALsource
*source
{LookupSource(context
.get(),src
)};
3929 if(!source
) UNLIKELY
3930 return context
->setError(AL_INVALID_NAME
, "Invalid source ID %u", src
);
3932 if(source
->SourceType
!= AL_STREAMING
) UNLIKELY
3933 return context
->setError(AL_INVALID_VALUE
, "Unqueueing from a non-streaming source %u",
3935 if(source
->Looping
) UNLIKELY
3936 return context
->setError(AL_INVALID_VALUE
, "Unqueueing from looping source %u", src
);
3938 /* Make sure enough buffers have been processed to unqueue. */
3940 if(source
->state
!= AL_INITIAL
) LIKELY
3942 VoiceBufferItem
*Current
{nullptr};
3943 if(Voice
*voice
{GetSourceVoice(source
, context
.get())})
3944 Current
= voice
->mCurrentBuffer
.load(std::memory_order_relaxed
);
3945 for(auto &item
: source
->mQueue
)
3947 if(&item
== Current
)
3952 if(processed
< static_cast<ALuint
>(nb
)) UNLIKELY
3953 return context
->setError(AL_INVALID_VALUE
, "Unqueueing %d buffer%s (only %u processed)",
3954 nb
, (nb
==1)?"":"s", processed
);
3957 auto &head
= source
->mQueue
.front();
3958 if(ALbuffer
*buffer
{head
.mBuffer
})
3960 *(buffers
++) = buffer
->id
;
3961 DecrementRef(buffer
->ref
);
3965 source
->mQueue
.pop_front();
3971 AL_API
void AL_APIENTRY
alSourceQueueBufferLayersSOFT(ALuint
, ALsizei
, const ALuint
*)
3974 ContextRef context
{GetContextRef()};
3975 if(!context
) UNLIKELY
return;
3977 context
->setError(AL_INVALID_OPERATION
, "alSourceQueueBufferLayersSOFT not supported");
3982 ALsource::ALsource()
3985 Direct
.GainHF
= 1.0f
;
3986 Direct
.HFReference
= LOWPASSFREQREF
;
3987 Direct
.GainLF
= 1.0f
;
3988 Direct
.LFReference
= HIGHPASSFREQREF
;
3989 for(auto &send
: Send
)
3991 send
.Slot
= nullptr;
3994 send
.HFReference
= LOWPASSFREQREF
;
3996 send
.LFReference
= HIGHPASSFREQREF
;
4000 ALsource::~ALsource()
4002 for(auto &item
: mQueue
)
4004 if(ALbuffer
*buffer
{item
.mBuffer
})
4005 DecrementRef(buffer
->ref
);
4008 auto clear_send
= [](ALsource::SendData
&send
) -> void
4009 { if(send
.Slot
) DecrementRef(send
.Slot
->ref
); };
4010 std::for_each(Send
.begin(), Send
.end(), clear_send
);
4013 void UpdateAllSourceProps(ALCcontext
*context
)
4015 std::lock_guard
<std::mutex
> _
{context
->mSourceLock
};
4016 auto voicelist
= context
->getVoicesSpan();
4018 for(Voice
*voice
: voicelist
)
4020 ALuint sid
{voice
->mSourceID
.load(std::memory_order_acquire
)};
4021 ALsource
*source
= sid
? LookupSource(context
, sid
) : nullptr;
4022 if(source
&& source
->VoiceIdx
== vidx
)
4024 if(std::exchange(source
->mPropsDirty
, false))
4025 UpdateSourceProps(source
, voice
, context
);
4031 SourceSubList::~SourceSubList()
4033 uint64_t usemask
{~FreeMask
};
4036 const int idx
{al::countr_zero(usemask
)};
4037 usemask
&= ~(1_u64
<< idx
);
4038 al::destroy_at(Sources
+idx
);
4040 FreeMask
= ~usemask
;
4047 constexpr const ALsource::EaxFxSlotIds
ALsource::eax4_fx_slot_ids
;
4048 constexpr const ALsource::EaxFxSlotIds
ALsource::eax5_fx_slot_ids
;
4050 void ALsource::eaxInitialize(ALCcontext
*context
) noexcept
4052 assert(context
!= nullptr);
4053 mEaxAlContext
= context
;
4055 mEaxPrimaryFxSlotId
= context
->eaxGetPrimaryFxSlotIndex();
4058 eax1_translate(mEax1
.i
, mEax
);
4063 void ALsource::eaxDispatch(const EaxCall
& call
)
4065 call
.is_get() ? eax_get(call
) : eax_set(call
);
4068 ALsource
* ALsource::EaxLookupSource(ALCcontext
& al_context
, ALuint source_id
) noexcept
4070 return LookupSource(&al_context
, source_id
);
4073 [[noreturn
]] void ALsource::eax_fail(const char* message
)
4075 throw Exception
{message
};
4078 [[noreturn
]] void ALsource::eax_fail_unknown_property_id()
4080 eax_fail("Unknown property id.");
4083 [[noreturn
]] void ALsource::eax_fail_unknown_version()
4085 eax_fail("Unknown version.");
4088 [[noreturn
]] void ALsource::eax_fail_unknown_active_fx_slot_id()
4090 eax_fail("Unknown active FX slot ID.");
4093 [[noreturn
]] void ALsource::eax_fail_unknown_receiving_fx_slot_id()
4095 eax_fail("Unknown receiving FX slot ID.");
4098 void ALsource::eax_set_sends_defaults(EaxSends
& sends
, const EaxFxSlotIds
& ids
) noexcept
4100 for (auto i
= size_t{}; i
< EAX_MAX_FXSLOTS
; ++i
) {
4101 auto& send
= sends
[i
];
4102 send
.guidReceivingFXSlotID
= *(ids
[i
]);
4103 send
.lSend
= EAXSOURCE_DEFAULTSEND
;
4104 send
.lSendHF
= EAXSOURCE_DEFAULTSENDHF
;
4105 send
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
4106 send
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
4107 send
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
4108 send
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
4109 send
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
4110 send
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
4114 void ALsource::eax1_set_defaults(Eax1Props
& props
) noexcept
4116 props
.fMix
= EAX_REVERBMIX_USEDISTANCE
;
4119 void ALsource::eax1_set_defaults() noexcept
4121 eax1_set_defaults(mEax1
.i
);
4125 void ALsource::eax2_set_defaults(Eax2Props
& props
) noexcept
4127 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
4128 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
4129 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
4130 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
4131 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
4132 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
4133 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
4134 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
4135 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
4136 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
4137 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
4138 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
4139 props
.dwFlags
= EAXSOURCE_DEFAULTFLAGS
;
4142 void ALsource::eax2_set_defaults() noexcept
4144 eax2_set_defaults(mEax2
.i
);
4148 void ALsource::eax3_set_defaults(Eax3Props
& props
) noexcept
4150 props
.lDirect
= EAXSOURCE_DEFAULTDIRECT
;
4151 props
.lDirectHF
= EAXSOURCE_DEFAULTDIRECTHF
;
4152 props
.lRoom
= EAXSOURCE_DEFAULTROOM
;
4153 props
.lRoomHF
= EAXSOURCE_DEFAULTROOMHF
;
4154 props
.lObstruction
= EAXSOURCE_DEFAULTOBSTRUCTION
;
4155 props
.flObstructionLFRatio
= EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO
;
4156 props
.lOcclusion
= EAXSOURCE_DEFAULTOCCLUSION
;
4157 props
.flOcclusionLFRatio
= EAXSOURCE_DEFAULTOCCLUSIONLFRATIO
;
4158 props
.flOcclusionRoomRatio
= EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO
;
4159 props
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
4160 props
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
4161 props
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
4162 props
.lOutsideVolumeHF
= EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF
;
4163 props
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
4164 props
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
4165 props
.flRoomRolloffFactor
= EAXSOURCE_DEFAULTROOMROLLOFFFACTOR
;
4166 props
.flAirAbsorptionFactor
= EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR
;
4167 props
.ulFlags
= EAXSOURCE_DEFAULTFLAGS
;
4170 void ALsource::eax3_set_defaults() noexcept
4172 eax3_set_defaults(mEax3
.i
);
4176 void ALsource::eax4_set_sends_defaults(EaxSends
& sends
) noexcept
4178 eax_set_sends_defaults(sends
, eax4_fx_slot_ids
);
4181 void ALsource::eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS
& slots
) noexcept
4183 slots
= EAX40SOURCE_DEFAULTACTIVEFXSLOTID
;
4186 void ALsource::eax4_set_defaults() noexcept
4188 eax3_set_defaults(mEax4
.i
.source
);
4189 eax4_set_sends_defaults(mEax4
.i
.sends
);
4190 eax4_set_active_fx_slots_defaults(mEax4
.i
.active_fx_slots
);
4194 void ALsource::eax5_set_source_defaults(EAX50SOURCEPROPERTIES
& props
) noexcept
4196 eax3_set_defaults(static_cast<Eax3Props
&>(props
));
4197 props
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4200 void ALsource::eax5_set_sends_defaults(EaxSends
& sends
) noexcept
4202 eax_set_sends_defaults(sends
, eax5_fx_slot_ids
);
4205 void ALsource::eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS
& slots
) noexcept
4207 slots
= EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID
;
4210 void ALsource::eax5_set_speaker_levels_defaults(EaxSpeakerLevels
& speaker_levels
) noexcept
4212 for (auto i
= size_t{}; i
< eax_max_speakers
; ++i
) {
4213 auto& speaker_level
= speaker_levels
[i
];
4214 speaker_level
.lSpeakerID
= static_cast<long>(EAXSPEAKER_FRONT_LEFT
+ i
);
4215 speaker_level
.lLevel
= EAXSOURCE_DEFAULTSPEAKERLEVEL
;
4219 void ALsource::eax5_set_defaults(Eax5Props
& props
) noexcept
4221 eax5_set_source_defaults(props
.source
);
4222 eax5_set_sends_defaults(props
.sends
);
4223 eax5_set_active_fx_slots_defaults(props
.active_fx_slots
);
4224 eax5_set_speaker_levels_defaults(props
.speaker_levels
);
4227 void ALsource::eax5_set_defaults() noexcept
4229 eax5_set_defaults(mEax5
.i
);
4233 void ALsource::eax_set_defaults() noexcept
4235 eax1_set_defaults();
4236 eax2_set_defaults();
4237 eax3_set_defaults();
4238 eax4_set_defaults();
4239 eax5_set_defaults();
4242 void ALsource::eax1_translate(const Eax1Props
& src
, Eax5Props
& dst
) noexcept
4244 eax5_set_defaults(dst
);
4246 if (src
.fMix
== EAX_REVERBMIX_USEDISTANCE
)
4248 dst
.source
.ulFlags
|= EAXSOURCEFLAGS_ROOMAUTO
;
4249 dst
.sends
[0].lSend
= 0;
4253 dst
.source
.ulFlags
&= ~EAXSOURCEFLAGS_ROOMAUTO
;
4254 dst
.sends
[0].lSend
= clamp(static_cast<long>(gain_to_level_mb(src
.fMix
)),
4255 EAXSOURCE_MINSEND
, EAXSOURCE_MAXSEND
);
4259 void ALsource::eax2_translate(const Eax2Props
& src
, Eax5Props
& dst
) noexcept
4263 dst
.source
.lDirect
= src
.lDirect
;
4264 dst
.source
.lDirectHF
= src
.lDirectHF
;
4265 dst
.source
.lRoom
= src
.lRoom
;
4266 dst
.source
.lRoomHF
= src
.lRoomHF
;
4267 dst
.source
.lObstruction
= src
.lObstruction
;
4268 dst
.source
.flObstructionLFRatio
= src
.flObstructionLFRatio
;
4269 dst
.source
.lOcclusion
= src
.lOcclusion
;
4270 dst
.source
.flOcclusionLFRatio
= src
.flOcclusionLFRatio
;
4271 dst
.source
.flOcclusionRoomRatio
= src
.flOcclusionRoomRatio
;
4272 dst
.source
.flOcclusionDirectRatio
= EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO
;
4273 dst
.source
.lExclusion
= EAXSOURCE_DEFAULTEXCLUSION
;
4274 dst
.source
.flExclusionLFRatio
= EAXSOURCE_DEFAULTEXCLUSIONLFRATIO
;
4275 dst
.source
.lOutsideVolumeHF
= src
.lOutsideVolumeHF
;
4276 dst
.source
.flDopplerFactor
= EAXSOURCE_DEFAULTDOPPLERFACTOR
;
4277 dst
.source
.flRolloffFactor
= EAXSOURCE_DEFAULTROLLOFFFACTOR
;
4278 dst
.source
.flRoomRolloffFactor
= src
.flRoomRolloffFactor
;
4279 dst
.source
.flAirAbsorptionFactor
= src
.flAirAbsorptionFactor
;
4280 dst
.source
.ulFlags
= src
.dwFlags
;
4281 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4283 // Set everyting else to defaults.
4285 eax5_set_sends_defaults(dst
.sends
);
4286 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
4287 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4290 void ALsource::eax3_translate(const Eax3Props
& src
, Eax5Props
& dst
) noexcept
4294 static_cast<Eax3Props
&>(dst
.source
) = src
;
4295 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4297 // Set everyting else to defaults.
4299 eax5_set_sends_defaults(dst
.sends
);
4300 eax5_set_active_fx_slots_defaults(dst
.active_fx_slots
);
4301 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4304 void ALsource::eax4_translate(const Eax4Props
& src
, Eax5Props
& dst
) noexcept
4308 static_cast<Eax3Props
&>(dst
.source
) = src
.source
;
4309 dst
.source
.flMacroFXFactor
= EAXSOURCE_DEFAULTMACROFXFACTOR
;
4313 dst
.sends
= src
.sends
;
4315 for (auto i
= size_t{}; i
< EAX_MAX_FXSLOTS
; ++i
)
4316 dst
.sends
[i
].guidReceivingFXSlotID
= *(eax5_fx_slot_ids
[i
]);
4320 for (auto i
= 0; i
< EAX50_MAX_ACTIVE_FXSLOTS
; ++i
) {
4321 auto& dst_id
= dst
.active_fx_slots
.guidActiveFXSlots
[i
];
4323 if (i
< EAX40_MAX_ACTIVE_FXSLOTS
) {
4324 const auto& src_id
= src
.active_fx_slots
.guidActiveFXSlots
[i
];
4326 if (src_id
== EAX_NULL_GUID
)
4327 dst_id
= EAX_NULL_GUID
;
4328 else if (src_id
== EAX_PrimaryFXSlotID
)
4329 dst_id
= EAX_PrimaryFXSlotID
;
4330 else if (src_id
== EAXPROPERTYID_EAX40_FXSlot0
)
4331 dst_id
= EAXPROPERTYID_EAX50_FXSlot0
;
4332 else if (src_id
== EAXPROPERTYID_EAX40_FXSlot1
)
4333 dst_id
= EAXPROPERTYID_EAX50_FXSlot1
;
4334 else if (src_id
== EAXPROPERTYID_EAX40_FXSlot2
)
4335 dst_id
= EAXPROPERTYID_EAX50_FXSlot2
;
4336 else if (src_id
== EAXPROPERTYID_EAX40_FXSlot3
)
4337 dst_id
= EAXPROPERTYID_EAX50_FXSlot3
;
4339 assert(false && "Unknown active FX slot ID.");
4341 dst_id
= EAX_NULL_GUID
;
4346 eax5_set_speaker_levels_defaults(dst
.speaker_levels
);
4349 float ALsource::eax_calculate_dst_occlusion_mb(
4350 long src_occlusion_mb
,
4352 float lf_ratio
) noexcept
4354 const auto ratio_1
= path_ratio
+ lf_ratio
- 1.0F
;
4355 const auto ratio_2
= path_ratio
* lf_ratio
;
4356 const auto ratio
= (ratio_2
> ratio_1
) ? ratio_2
: ratio_1
;
4357 const auto dst_occlustion_mb
= static_cast<float>(src_occlusion_mb
) * ratio
;
4358 return dst_occlustion_mb
;
4361 EaxAlLowPassParam
ALsource::eax_create_direct_filter_param() const noexcept
4364 static_cast<float>(mEax
.source
.lDirect
) +
4365 (static_cast<float>(mEax
.source
.lObstruction
) * mEax
.source
.flObstructionLFRatio
) +
4366 eax_calculate_dst_occlusion_mb(
4367 mEax
.source
.lOcclusion
,
4368 mEax
.source
.flOcclusionDirectRatio
,
4369 mEax
.source
.flOcclusionLFRatio
);
4371 const auto has_source_occlusion
= (mEax
.source
.lOcclusion
!= 0);
4374 static_cast<float>(mEax
.source
.lDirectHF
) +
4375 static_cast<float>(mEax
.source
.lObstruction
);
4377 for (auto i
= std::size_t{}; i
< EAX_MAX_FXSLOTS
; ++i
)
4379 if(!mEaxActiveFxSlots
[i
])
4382 if(has_source_occlusion
) {
4383 const auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4384 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4385 const auto is_environmental_fx
= ((fx_slot_eax
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0);
4386 const auto is_primary
= (mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index());
4387 const auto is_listener_environment
= (is_environmental_fx
&& is_primary
);
4389 if(is_listener_environment
) {
4390 gain_mb
+= eax_calculate_dst_occlusion_mb(
4391 mEax
.source
.lOcclusion
,
4392 mEax
.source
.flOcclusionDirectRatio
,
4393 mEax
.source
.flOcclusionLFRatio
);
4395 gain_hf_mb
+= static_cast<float>(mEax
.source
.lOcclusion
) * mEax
.source
.flOcclusionDirectRatio
;
4399 const auto& send
= mEax
.sends
[i
];
4401 if(send
.lOcclusion
!= 0) {
4402 gain_mb
+= eax_calculate_dst_occlusion_mb(
4404 send
.flOcclusionDirectRatio
,
4405 send
.flOcclusionLFRatio
);
4407 gain_hf_mb
+= static_cast<float>(send
.lOcclusion
) * send
.flOcclusionDirectRatio
;
4411 const auto al_low_pass_param
= EaxAlLowPassParam
{
4412 level_mb_to_gain(gain_mb
),
4413 minf(level_mb_to_gain(gain_hf_mb
), 1.0f
)};
4415 return al_low_pass_param
;
4418 EaxAlLowPassParam
ALsource::eax_create_room_filter_param(
4419 const ALeffectslot
& fx_slot
,
4420 const EAXSOURCEALLSENDPROPERTIES
& send
) const noexcept
4422 const auto& fx_slot_eax
= fx_slot
.eax_get_eax_fx_slot();
4423 const auto is_environmental_fx
= ((fx_slot_eax
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0);
4424 const auto is_primary
= (mEaxPrimaryFxSlotId
.value_or(-1) == fx_slot
.eax_get_index());
4425 const auto is_listener_environment
= (is_environmental_fx
&& is_primary
);
4427 const auto gain_mb
=
4428 (static_cast<float>(fx_slot_eax
.lOcclusion
) * fx_slot_eax
.flOcclusionLFRatio
) +
4429 static_cast<float>((is_environmental_fx
? mEax
.source
.lRoom
: 0) + send
.lSend
) +
4430 (is_listener_environment
?
4431 eax_calculate_dst_occlusion_mb(
4432 mEax
.source
.lOcclusion
,
4433 mEax
.source
.flOcclusionRoomRatio
,
4434 mEax
.source
.flOcclusionLFRatio
) :
4436 eax_calculate_dst_occlusion_mb(
4438 send
.flOcclusionRoomRatio
,
4439 send
.flOcclusionLFRatio
) +
4440 (is_listener_environment
?
4441 (static_cast<float>(mEax
.source
.lExclusion
) * mEax
.source
.flExclusionLFRatio
) :
4443 (static_cast<float>(send
.lExclusion
) * send
.flExclusionLFRatio
);
4445 const auto gain_hf_mb
=
4446 static_cast<float>(fx_slot_eax
.lOcclusion
) +
4447 static_cast<float>((is_environmental_fx
? mEax
.source
.lRoomHF
: 0) + send
.lSendHF
) +
4448 (is_listener_environment
?
4449 ((static_cast<float>(mEax
.source
.lOcclusion
) * mEax
.source
.flOcclusionRoomRatio
)) :
4451 (static_cast<float>(send
.lOcclusion
) * send
.flOcclusionRoomRatio
) +
4452 (is_listener_environment
?
4453 static_cast<float>(mEax
.source
.lExclusion
+ send
.lExclusion
) :
4456 const auto al_low_pass_param
= EaxAlLowPassParam
{
4457 level_mb_to_gain(gain_mb
),
4458 minf(level_mb_to_gain(gain_hf_mb
), 1.0f
)};
4460 return al_low_pass_param
;
4463 void ALsource::eax_update_direct_filter()
4465 const auto& direct_param
= eax_create_direct_filter_param();
4466 Direct
.Gain
= direct_param
.gain
;
4467 Direct
.GainHF
= direct_param
.gain_hf
;
4468 Direct
.HFReference
= LOWPASSFREQREF
;
4469 Direct
.GainLF
= 1.0f
;
4470 Direct
.LFReference
= HIGHPASSFREQREF
;
4474 void ALsource::eax_update_room_filters()
4476 for (auto i
= size_t{}; i
< EAX_MAX_FXSLOTS
; ++i
) {
4477 if (!mEaxActiveFxSlots
[i
])
4480 auto& fx_slot
= mEaxAlContext
->eaxGetFxSlot(i
);
4481 const auto& send
= mEax
.sends
[i
];
4482 const auto& room_param
= eax_create_room_filter_param(fx_slot
, send
);
4483 eax_set_al_source_send(&fx_slot
, i
, room_param
);
4487 void ALsource::eax_set_efx_outer_gain_hf()
4489 OuterGainHF
= clamp(
4490 level_mb_to_gain(static_cast<float>(mEax
.source
.lOutsideVolumeHF
)),
4491 AL_MIN_CONE_OUTER_GAINHF
,
4492 AL_MAX_CONE_OUTER_GAINHF
);
4495 void ALsource::eax_set_efx_doppler_factor()
4497 DopplerFactor
= mEax
.source
.flDopplerFactor
;
4500 void ALsource::eax_set_efx_rolloff_factor()
4502 RolloffFactor2
= mEax
.source
.flRolloffFactor
;
4505 void ALsource::eax_set_efx_room_rolloff_factor()
4507 RoomRolloffFactor
= mEax
.source
.flRoomRolloffFactor
;
4510 void ALsource::eax_set_efx_air_absorption_factor()
4512 AirAbsorptionFactor
= mEax
.source
.flAirAbsorptionFactor
;
4515 void ALsource::eax_set_efx_dry_gain_hf_auto()
4517 DryGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_DIRECTHFAUTO
) != 0);
4520 void ALsource::eax_set_efx_wet_gain_auto()
4522 WetGainAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMAUTO
) != 0);
4525 void ALsource::eax_set_efx_wet_gain_hf_auto()
4527 WetGainHFAuto
= ((mEax
.source
.ulFlags
& EAXSOURCEFLAGS_ROOMHFAUTO
) != 0);
4530 void ALsource::eax1_set(const EaxCall
& call
, Eax1Props
& props
)
4532 switch (call
.get_property_id()) {
4533 case DSPROPERTY_EAXBUFFER_ALL
:
4534 eax_defer
<Eax1SourceAllValidator
>(call
, props
);
4537 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4538 eax_defer
<Eax1SourceReverbMixValidator
>(call
, props
.fMix
);
4542 eax_fail_unknown_property_id();
4546 void ALsource::eax2_set(const EaxCall
& call
, Eax2Props
& props
)
4548 switch (call
.get_property_id()) {
4549 case DSPROPERTY_EAX20BUFFER_NONE
:
4552 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4553 eax_defer
<Eax2SourceAllValidator
>(call
, props
);
4556 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4557 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4560 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4561 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4564 case DSPROPERTY_EAX20BUFFER_ROOM
:
4565 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4568 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4569 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4572 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4573 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4576 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4577 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4580 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4581 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4584 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4585 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4588 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4589 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4592 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4593 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4596 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4597 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4600 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4601 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4604 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4605 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.dwFlags
);
4609 eax_fail_unknown_property_id();
4613 void ALsource::eax3_set(const EaxCall
& call
, Eax3Props
& props
)
4615 switch (call
.get_property_id()) {
4616 case EAXSOURCE_NONE
:
4619 case EAXSOURCE_ALLPARAMETERS
:
4620 eax_defer
<Eax3SourceAllValidator
>(call
, props
);
4623 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4624 eax_defer_sub
<Eax4ObstructionValidator
, EAXOBSTRUCTIONPROPERTIES
>(call
, props
.lObstruction
);
4627 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4628 eax_defer_sub
<Eax4OcclusionValidator
, EAXOCCLUSIONPROPERTIES
>(call
, props
.lOcclusion
);
4631 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4632 eax_defer_sub
<Eax4ExclusionValidator
, EAXEXCLUSIONPROPERTIES
>(call
, props
.lExclusion
);
4635 case EAXSOURCE_DIRECT
:
4636 eax_defer
<Eax2SourceDirectValidator
>(call
, props
.lDirect
);
4639 case EAXSOURCE_DIRECTHF
:
4640 eax_defer
<Eax2SourceDirectHfValidator
>(call
, props
.lDirectHF
);
4643 case EAXSOURCE_ROOM
:
4644 eax_defer
<Eax2SourceRoomValidator
>(call
, props
.lRoom
);
4647 case EAXSOURCE_ROOMHF
:
4648 eax_defer
<Eax2SourceRoomHfValidator
>(call
, props
.lRoomHF
);
4651 case EAXSOURCE_OBSTRUCTION
:
4652 eax_defer
<Eax2SourceObstructionValidator
>(call
, props
.lObstruction
);
4655 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4656 eax_defer
<Eax2SourceObstructionLfRatioValidator
>(call
, props
.flObstructionLFRatio
);
4659 case EAXSOURCE_OCCLUSION
:
4660 eax_defer
<Eax2SourceOcclusionValidator
>(call
, props
.lOcclusion
);
4663 case EAXSOURCE_OCCLUSIONLFRATIO
:
4664 eax_defer
<Eax2SourceOcclusionLfRatioValidator
>(call
, props
.flOcclusionLFRatio
);
4667 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4668 eax_defer
<Eax2SourceOcclusionRoomRatioValidator
>(call
, props
.flOcclusionRoomRatio
);
4671 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4672 eax_defer
<Eax3SourceOcclusionDirectRatioValidator
>(call
, props
.flOcclusionDirectRatio
);
4675 case EAXSOURCE_EXCLUSION
:
4676 eax_defer
<Eax3SourceExclusionValidator
>(call
, props
.lExclusion
);
4679 case EAXSOURCE_EXCLUSIONLFRATIO
:
4680 eax_defer
<Eax3SourceExclusionLfRatioValidator
>(call
, props
.flExclusionLFRatio
);
4683 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4684 eax_defer
<Eax2SourceOutsideVolumeHfValidator
>(call
, props
.lOutsideVolumeHF
);
4687 case EAXSOURCE_DOPPLERFACTOR
:
4688 eax_defer
<Eax3SourceDopplerFactorValidator
>(call
, props
.flDopplerFactor
);
4691 case EAXSOURCE_ROLLOFFFACTOR
:
4692 eax_defer
<Eax3SourceRolloffFactorValidator
>(call
, props
.flRolloffFactor
);
4695 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4696 eax_defer
<Eax2SourceRoomRolloffFactorValidator
>(call
, props
.flRoomRolloffFactor
);
4699 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4700 eax_defer
<Eax2SourceAirAbsorptionFactorValidator
>(call
, props
.flAirAbsorptionFactor
);
4703 case EAXSOURCE_FLAGS
:
4704 eax_defer
<Eax2SourceFlagsValidator
>(call
, props
.ulFlags
);
4708 eax_fail_unknown_property_id();
4712 void ALsource::eax4_set(const EaxCall
& call
, Eax4Props
& props
)
4714 switch (call
.get_property_id()) {
4715 case EAXSOURCE_NONE
:
4716 case EAXSOURCE_ALLPARAMETERS
:
4717 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4718 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4719 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4720 case EAXSOURCE_DIRECT
:
4721 case EAXSOURCE_DIRECTHF
:
4722 case EAXSOURCE_ROOM
:
4723 case EAXSOURCE_ROOMHF
:
4724 case EAXSOURCE_OBSTRUCTION
:
4725 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4726 case EAXSOURCE_OCCLUSION
:
4727 case EAXSOURCE_OCCLUSIONLFRATIO
:
4728 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4729 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4730 case EAXSOURCE_EXCLUSION
:
4731 case EAXSOURCE_EXCLUSIONLFRATIO
:
4732 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4733 case EAXSOURCE_DOPPLERFACTOR
:
4734 case EAXSOURCE_ROLLOFFFACTOR
:
4735 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4736 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4737 case EAXSOURCE_FLAGS
:
4738 eax3_set(call
, props
.source
);
4741 case EAXSOURCE_SENDPARAMETERS
:
4742 eax4_defer_sends
<Eax4SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4745 case EAXSOURCE_ALLSENDPARAMETERS
:
4746 eax4_defer_sends
<Eax4AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4749 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4750 eax4_defer_sends
<Eax4OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4753 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4754 eax4_defer_sends
<Eax4ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4757 case EAXSOURCE_ACTIVEFXSLOTID
:
4758 eax4_defer_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4762 eax_fail_unknown_property_id();
4766 void ALsource::eax5_defer_all_2d(const EaxCall
& call
, EAX50SOURCEPROPERTIES
& props
)
4768 const auto& src_props
= call
.get_value
<Exception
, const EAXSOURCE2DPROPERTIES
>();
4769 Eax5SourceAll2dValidator
{}(src_props
);
4770 props
.lDirect
= src_props
.lDirect
;
4771 props
.lDirectHF
= src_props
.lDirectHF
;
4772 props
.lRoom
= src_props
.lRoom
;
4773 props
.lRoomHF
= src_props
.lRoomHF
;
4774 props
.ulFlags
= src_props
.ulFlags
;
4777 void ALsource::eax5_defer_speaker_levels(const EaxCall
& call
, EaxSpeakerLevels
& props
)
4779 const auto values
= call
.get_values
<const EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
4780 std::for_each(values
.cbegin(), values
.cend(), Eax5SpeakerAllValidator
{});
4782 for (const auto& value
: values
) {
4783 const auto index
= static_cast<size_t>(value
.lSpeakerID
- EAXSPEAKER_FRONT_LEFT
);
4784 props
[index
].lLevel
= value
.lLevel
;
4788 void ALsource::eax5_set(const EaxCall
& call
, Eax5Props
& props
)
4790 switch (call
.get_property_id()) {
4791 case EAXSOURCE_NONE
:
4794 case EAXSOURCE_ALLPARAMETERS
:
4795 eax_defer
<Eax5SourceAllValidator
>(call
, props
.source
);
4798 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4799 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4800 case EAXSOURCE_EXCLUSIONPARAMETERS
:
4801 case EAXSOURCE_DIRECT
:
4802 case EAXSOURCE_DIRECTHF
:
4803 case EAXSOURCE_ROOM
:
4804 case EAXSOURCE_ROOMHF
:
4805 case EAXSOURCE_OBSTRUCTION
:
4806 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
4807 case EAXSOURCE_OCCLUSION
:
4808 case EAXSOURCE_OCCLUSIONLFRATIO
:
4809 case EAXSOURCE_OCCLUSIONROOMRATIO
:
4810 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
4811 case EAXSOURCE_EXCLUSION
:
4812 case EAXSOURCE_EXCLUSIONLFRATIO
:
4813 case EAXSOURCE_OUTSIDEVOLUMEHF
:
4814 case EAXSOURCE_DOPPLERFACTOR
:
4815 case EAXSOURCE_ROLLOFFFACTOR
:
4816 case EAXSOURCE_ROOMROLLOFFFACTOR
:
4817 case EAXSOURCE_AIRABSORPTIONFACTOR
:
4818 case EAXSOURCE_FLAGS
:
4819 eax3_set(call
, props
.source
);
4822 case EAXSOURCE_SENDPARAMETERS
:
4823 eax5_defer_sends
<Eax5SendValidator
, EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
4826 case EAXSOURCE_ALLSENDPARAMETERS
:
4827 eax5_defer_sends
<Eax5AllSendValidator
, EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
4830 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
4831 eax5_defer_sends
<Eax5OcclusionSendValidator
, EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4834 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
4835 eax5_defer_sends
<Eax5ExclusionSendValidator
, EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
4838 case EAXSOURCE_ACTIVEFXSLOTID
:
4839 eax5_defer_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
);
4842 case EAXSOURCE_MACROFXFACTOR
:
4843 eax_defer
<Eax5SourceMacroFXFactorValidator
>(call
, props
.source
.flMacroFXFactor
);
4846 case EAXSOURCE_SPEAKERLEVELS
:
4847 eax5_defer_speaker_levels(call
, props
.speaker_levels
);
4850 case EAXSOURCE_ALL2DPARAMETERS
:
4851 eax5_defer_all_2d(call
, props
.source
);
4855 eax_fail_unknown_property_id();
4859 void ALsource::eax_set(const EaxCall
& call
)
4861 const auto eax_version
= call
.get_version();
4864 case 1: eax1_set(call
, mEax1
.d
); break;
4865 case 2: eax2_set(call
, mEax2
.d
); break;
4866 case 3: eax3_set(call
, mEax3
.d
); break;
4867 case 4: eax4_set(call
, mEax4
.d
); break;
4868 case 5: eax5_set(call
, mEax5
.d
); break;
4869 default: eax_fail_unknown_property_id();
4872 mEaxVersion
= eax_version
;
4875 void ALsource::eax_get_active_fx_slot_id(const EaxCall
& call
, const GUID
* ids
, size_t max_count
)
4877 assert(ids
!= nullptr);
4878 assert(max_count
== EAX40_MAX_ACTIVE_FXSLOTS
|| max_count
== EAX50_MAX_ACTIVE_FXSLOTS
);
4879 const auto dst_ids
= call
.get_values
<GUID
>(max_count
);
4880 const auto count
= dst_ids
.size();
4881 std::uninitialized_copy_n(ids
, count
, dst_ids
.begin());
4884 void ALsource::eax1_get(const EaxCall
& call
, const Eax1Props
& props
)
4886 switch (call
.get_property_id()) {
4887 case DSPROPERTY_EAXBUFFER_ALL
:
4888 case DSPROPERTY_EAXBUFFER_REVERBMIX
:
4889 call
.set_value
<Exception
>(props
.fMix
);
4893 eax_fail_unknown_property_id();
4897 void ALsource::eax2_get(const EaxCall
& call
, const Eax2Props
& props
)
4899 switch (call
.get_property_id()) {
4900 case DSPROPERTY_EAX20BUFFER_NONE
:
4903 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS
:
4904 call
.set_value
<Exception
>(props
);
4907 case DSPROPERTY_EAX20BUFFER_DIRECT
:
4908 call
.set_value
<Exception
>(props
.lDirect
);
4911 case DSPROPERTY_EAX20BUFFER_DIRECTHF
:
4912 call
.set_value
<Exception
>(props
.lDirectHF
);
4915 case DSPROPERTY_EAX20BUFFER_ROOM
:
4916 call
.set_value
<Exception
>(props
.lRoom
);
4919 case DSPROPERTY_EAX20BUFFER_ROOMHF
:
4920 call
.set_value
<Exception
>(props
.lRoomHF
);
4923 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR
:
4924 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
4927 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION
:
4928 call
.set_value
<Exception
>(props
.lObstruction
);
4931 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO
:
4932 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
4935 case DSPROPERTY_EAX20BUFFER_OCCLUSION
:
4936 call
.set_value
<Exception
>(props
.lOcclusion
);
4939 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO
:
4940 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
4943 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO
:
4944 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
4947 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF
:
4948 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
4951 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR
:
4952 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
4955 case DSPROPERTY_EAX20BUFFER_FLAGS
:
4956 call
.set_value
<Exception
>(props
.dwFlags
);
4960 eax_fail_unknown_property_id();
4964 void ALsource::eax3_get_obstruction(const EaxCall
& call
, const Eax3Props
& props
)
4966 const auto& subprops
= reinterpret_cast<const EAXOBSTRUCTIONPROPERTIES
&>(props
.lObstruction
);
4967 call
.set_value
<Exception
>(subprops
);
4970 void ALsource::eax3_get_occlusion(const EaxCall
& call
, const Eax3Props
& props
)
4972 const auto& subprops
= reinterpret_cast<const EAXOCCLUSIONPROPERTIES
&>(props
.lOcclusion
);
4973 call
.set_value
<Exception
>(subprops
);
4976 void ALsource::eax3_get_exclusion(const EaxCall
& call
, const Eax3Props
& props
)
4978 const auto& subprops
= reinterpret_cast<const EAXEXCLUSIONPROPERTIES
&>(props
.lExclusion
);
4979 call
.set_value
<Exception
>(subprops
);
4982 void ALsource::eax3_get(const EaxCall
& call
, const Eax3Props
& props
)
4984 switch (call
.get_property_id()) {
4985 case EAXSOURCE_NONE
:
4988 case EAXSOURCE_ALLPARAMETERS
:
4989 call
.set_value
<Exception
>(props
);
4992 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
4993 eax3_get_obstruction(call
, props
);
4996 case EAXSOURCE_OCCLUSIONPARAMETERS
:
4997 eax3_get_occlusion(call
, props
);
5000 case EAXSOURCE_EXCLUSIONPARAMETERS
:
5001 eax3_get_exclusion(call
, props
);
5004 case EAXSOURCE_DIRECT
:
5005 call
.set_value
<Exception
>(props
.lDirect
);
5008 case EAXSOURCE_DIRECTHF
:
5009 call
.set_value
<Exception
>(props
.lDirectHF
);
5012 case EAXSOURCE_ROOM
:
5013 call
.set_value
<Exception
>(props
.lRoom
);
5016 case EAXSOURCE_ROOMHF
:
5017 call
.set_value
<Exception
>(props
.lRoomHF
);
5020 case EAXSOURCE_OBSTRUCTION
:
5021 call
.set_value
<Exception
>(props
.lObstruction
);
5024 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
5025 call
.set_value
<Exception
>(props
.flObstructionLFRatio
);
5028 case EAXSOURCE_OCCLUSION
:
5029 call
.set_value
<Exception
>(props
.lOcclusion
);
5032 case EAXSOURCE_OCCLUSIONLFRATIO
:
5033 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
5036 case EAXSOURCE_OCCLUSIONROOMRATIO
:
5037 call
.set_value
<Exception
>(props
.flOcclusionRoomRatio
);
5040 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
5041 call
.set_value
<Exception
>(props
.flOcclusionDirectRatio
);
5044 case EAXSOURCE_EXCLUSION
:
5045 call
.set_value
<Exception
>(props
.lExclusion
);
5048 case EAXSOURCE_EXCLUSIONLFRATIO
:
5049 call
.set_value
<Exception
>(props
.flExclusionLFRatio
);
5052 case EAXSOURCE_OUTSIDEVOLUMEHF
:
5053 call
.set_value
<Exception
>(props
.lOutsideVolumeHF
);
5056 case EAXSOURCE_DOPPLERFACTOR
:
5057 call
.set_value
<Exception
>(props
.flDopplerFactor
);
5060 case EAXSOURCE_ROLLOFFFACTOR
:
5061 call
.set_value
<Exception
>(props
.flRolloffFactor
);
5064 case EAXSOURCE_ROOMROLLOFFFACTOR
:
5065 call
.set_value
<Exception
>(props
.flRoomRolloffFactor
);
5068 case EAXSOURCE_AIRABSORPTIONFACTOR
:
5069 call
.set_value
<Exception
>(props
.flAirAbsorptionFactor
);
5072 case EAXSOURCE_FLAGS
:
5073 call
.set_value
<Exception
>(props
.ulFlags
);
5077 eax_fail_unknown_property_id();
5081 void ALsource::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
5083 switch (call
.get_property_id()) {
5084 case EAXSOURCE_NONE
:
5087 case EAXSOURCE_ALLPARAMETERS
:
5088 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
5089 case EAXSOURCE_OCCLUSIONPARAMETERS
:
5090 case EAXSOURCE_EXCLUSIONPARAMETERS
:
5091 case EAXSOURCE_DIRECT
:
5092 case EAXSOURCE_DIRECTHF
:
5093 case EAXSOURCE_ROOM
:
5094 case EAXSOURCE_ROOMHF
:
5095 case EAXSOURCE_OBSTRUCTION
:
5096 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
5097 case EAXSOURCE_OCCLUSION
:
5098 case EAXSOURCE_OCCLUSIONLFRATIO
:
5099 case EAXSOURCE_OCCLUSIONROOMRATIO
:
5100 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
5101 case EAXSOURCE_EXCLUSION
:
5102 case EAXSOURCE_EXCLUSIONLFRATIO
:
5103 case EAXSOURCE_OUTSIDEVOLUMEHF
:
5104 case EAXSOURCE_DOPPLERFACTOR
:
5105 case EAXSOURCE_ROLLOFFFACTOR
:
5106 case EAXSOURCE_ROOMROLLOFFFACTOR
:
5107 case EAXSOURCE_AIRABSORPTIONFACTOR
:
5108 case EAXSOURCE_FLAGS
:
5109 eax3_get(call
, props
.source
);
5112 case EAXSOURCE_SENDPARAMETERS
:
5113 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
5116 case EAXSOURCE_ALLSENDPARAMETERS
:
5117 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
5120 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
5121 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
5124 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
5125 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
5128 case EAXSOURCE_ACTIVEFXSLOTID
:
5129 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
, EAX40_MAX_ACTIVE_FXSLOTS
);
5133 eax_fail_unknown_property_id();
5137 void ALsource::eax5_get_all_2d(const EaxCall
& call
, const EAX50SOURCEPROPERTIES
& props
)
5139 auto& subprops
= call
.get_value
<Exception
, EAXSOURCE2DPROPERTIES
>();
5140 subprops
.lDirect
= props
.lDirect
;
5141 subprops
.lDirectHF
= props
.lDirectHF
;
5142 subprops
.lRoom
= props
.lRoom
;
5143 subprops
.lRoomHF
= props
.lRoomHF
;
5144 subprops
.ulFlags
= props
.ulFlags
;
5147 void ALsource::eax5_get_speaker_levels(const EaxCall
& call
, const EaxSpeakerLevels
& props
)
5149 const auto subprops
= call
.get_values
<EAXSPEAKERLEVELPROPERTIES
>(eax_max_speakers
);
5150 std::uninitialized_copy_n(props
.cbegin(), subprops
.size(), subprops
.begin());
5153 void ALsource::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
5155 switch (call
.get_property_id()) {
5156 case EAXSOURCE_NONE
:
5159 case EAXSOURCE_ALLPARAMETERS
:
5160 case EAXSOURCE_OBSTRUCTIONPARAMETERS
:
5161 case EAXSOURCE_OCCLUSIONPARAMETERS
:
5162 case EAXSOURCE_EXCLUSIONPARAMETERS
:
5163 case EAXSOURCE_DIRECT
:
5164 case EAXSOURCE_DIRECTHF
:
5165 case EAXSOURCE_ROOM
:
5166 case EAXSOURCE_ROOMHF
:
5167 case EAXSOURCE_OBSTRUCTION
:
5168 case EAXSOURCE_OBSTRUCTIONLFRATIO
:
5169 case EAXSOURCE_OCCLUSION
:
5170 case EAXSOURCE_OCCLUSIONLFRATIO
:
5171 case EAXSOURCE_OCCLUSIONROOMRATIO
:
5172 case EAXSOURCE_OCCLUSIONDIRECTRATIO
:
5173 case EAXSOURCE_EXCLUSION
:
5174 case EAXSOURCE_EXCLUSIONLFRATIO
:
5175 case EAXSOURCE_OUTSIDEVOLUMEHF
:
5176 case EAXSOURCE_DOPPLERFACTOR
:
5177 case EAXSOURCE_ROLLOFFFACTOR
:
5178 case EAXSOURCE_ROOMROLLOFFFACTOR
:
5179 case EAXSOURCE_AIRABSORPTIONFACTOR
:
5180 case EAXSOURCE_FLAGS
:
5181 eax3_get(call
, props
.source
);
5184 case EAXSOURCE_SENDPARAMETERS
:
5185 eax_get_sends
<EAXSOURCESENDPROPERTIES
>(call
, props
.sends
);
5188 case EAXSOURCE_ALLSENDPARAMETERS
:
5189 eax_get_sends
<EAXSOURCEALLSENDPROPERTIES
>(call
, props
.sends
);
5192 case EAXSOURCE_OCCLUSIONSENDPARAMETERS
:
5193 eax_get_sends
<EAXSOURCEOCCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
5196 case EAXSOURCE_EXCLUSIONSENDPARAMETERS
:
5197 eax_get_sends
<EAXSOURCEEXCLUSIONSENDPROPERTIES
>(call
, props
.sends
);
5200 case EAXSOURCE_ACTIVEFXSLOTID
:
5201 eax_get_active_fx_slot_id(call
, props
.active_fx_slots
.guidActiveFXSlots
, EAX50_MAX_ACTIVE_FXSLOTS
);
5204 case EAXSOURCE_MACROFXFACTOR
:
5205 call
.set_value
<Exception
>(props
.source
.flMacroFXFactor
);
5208 case EAXSOURCE_SPEAKERLEVELS
:
5209 call
.set_value
<Exception
>(props
.speaker_levels
);
5212 case EAXSOURCE_ALL2DPARAMETERS
:
5213 eax5_get_all_2d(call
, props
.source
);
5217 eax_fail_unknown_property_id();
5221 void ALsource::eax_get(const EaxCall
& call
)
5223 switch (call
.get_version()) {
5224 case 1: eax1_get(call
, mEax1
.i
); break;
5225 case 2: eax2_get(call
, mEax2
.i
); break;
5226 case 3: eax3_get(call
, mEax3
.i
); break;
5227 case 4: eax4_get(call
, mEax4
.i
); break;
5228 case 5: eax5_get(call
, mEax5
.i
); break;
5229 default: eax_fail_unknown_version();
5233 void ALsource::eax_set_al_source_send(ALeffectslot
*slot
, size_t sendidx
, const EaxAlLowPassParam
&filter
)
5235 if(sendidx
>= EAX_MAX_FXSLOTS
)
5238 auto &send
= Send
[sendidx
];
5239 send
.Gain
= filter
.gain
;
5240 send
.GainHF
= filter
.gain_hf
;
5241 send
.HFReference
= LOWPASSFREQREF
;
5243 send
.LFReference
= HIGHPASSFREQREF
;
5246 IncrementRef(slot
->ref
);
5247 if(auto *oldslot
= send
.Slot
)
5248 DecrementRef(oldslot
->ref
);
5254 void ALsource::eax_commit_active_fx_slots()
5256 // Clear all slots to an inactive state.
5257 mEaxActiveFxSlots
.fill(false);
5259 // Mark the set slots as active.
5260 for(const auto& slot_id
: mEax
.active_fx_slots
.guidActiveFXSlots
)
5262 if(slot_id
== EAX_NULL_GUID
)
5265 else if(slot_id
== EAX_PrimaryFXSlotID
)
5267 // Mark primary FX slot as active.
5268 if(mEaxPrimaryFxSlotId
.has_value())
5269 mEaxActiveFxSlots
[*mEaxPrimaryFxSlotId
] = true;
5271 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot0
)
5272 mEaxActiveFxSlots
[0] = true;
5273 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot1
)
5274 mEaxActiveFxSlots
[1] = true;
5275 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot2
)
5276 mEaxActiveFxSlots
[2] = true;
5277 else if(slot_id
== EAXPROPERTYID_EAX50_FXSlot3
)
5278 mEaxActiveFxSlots
[3] = true;
5281 // Deactivate EFX auxiliary effect slots for inactive slots. Active slots
5282 // will be updated with the room filters.
5283 for(auto i
= size_t{}; i
< EAX_MAX_FXSLOTS
; ++i
)
5285 if(!mEaxActiveFxSlots
[i
])
5286 eax_set_al_source_send(nullptr, i
, EaxAlLowPassParam
{1.0f
, 1.0f
});
5290 void ALsource::eax_commit_filters()
5292 eax_update_direct_filter();
5293 eax_update_room_filters();
5296 void ALsource::eaxCommit()
5298 const auto primary_fx_slot_id
= mEaxAlContext
->eaxGetPrimaryFxSlotIndex();
5299 const auto is_primary_fx_slot_id_changed
= (mEaxPrimaryFxSlotId
!= primary_fx_slot_id
);
5301 if(!mEaxChanged
&& !is_primary_fx_slot_id_changed
)
5304 mEaxPrimaryFxSlotId
= primary_fx_slot_id
;
5305 mEaxChanged
= false;
5311 eax1_translate(mEax1
.i
, mEax
);
5315 eax2_translate(mEax2
.i
, mEax
);
5319 eax3_translate(mEax3
.i
, mEax
);
5323 eax4_translate(mEax4
.i
, mEax
);
5331 eax_set_efx_outer_gain_hf();
5332 eax_set_efx_doppler_factor();
5333 eax_set_efx_rolloff_factor();
5334 eax_set_efx_room_rolloff_factor();
5335 eax_set_efx_air_absorption_factor();
5336 eax_set_efx_dry_gain_hf_auto();
5337 eax_set_efx_wet_gain_auto();
5338 eax_set_efx_wet_gain_hf_auto();
5340 eax_commit_active_fx_slots();
5341 eax_commit_filters();
5344 #endif // ALSOFT_EAX