1 /**
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.
8  *
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.
13  *
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
19  */
20 
21 #include "config.h"
22 
23 #include "source.h"
24 
25 #include <algorithm>
26 #include <array>
27 #include <atomic>
28 #include <cassert>
29 #include <chrono>
30 #include <climits>
31 #include <cmath>
32 #include <cstdint>
33 #include <functional>
34 #include <iterator>
35 #include <limits>
36 #include <memory>
37 #include <mutex>
38 #include <new>
39 #include <numeric>
40 #include <thread>
41 #include <utility>
42 
43 #include "AL/al.h"
44 #include "AL/alc.h"
45 #include "AL/alext.h"
46 #include "AL/efx.h"
47 
48 #include "albit.h"
49 #include "alcmain.h"
50 #include "alcontext.h"
51 #include "almalloc.h"
52 #include "alnumeric.h"
53 #include "aloptional.h"
54 #include "alspan.h"
55 #include "alu.h"
56 #include "atomic.h"
57 #include "auxeffectslot.h"
58 #include "backends/base.h"
59 #include "bformatdec.h"
60 #include "buffer.h"
61 #include "core/ambidefs.h"
62 #include "core/except.h"
63 #include "core/filters/nfc.h"
64 #include "core/filters/splitter.h"
65 #include "core/logging.h"
66 #include "event.h"
67 #include "filter.h"
68 #include "inprogext.h"
69 #include "math_defs.h"
70 #include "opthelpers.h"
71 #include "ringbuffer.h"
72 #include "threads.h"
73 #include "voice_change.h"
74 
75 
76 namespace {
77 
78 using namespace std::placeholders;
79 using std::chrono::nanoseconds;
80 
GetSourceVoice(ALsource * source,ALCcontext * context)81 Voice *GetSourceVoice(ALsource *source, ALCcontext *context)
82 {
83     auto voicelist = context->getVoicesSpan();
84     ALuint idx{source->VoiceIdx};
85     if(idx < voicelist.size())
86     {
87         ALuint sid{source->id};
88         Voice *voice = voicelist[idx];
89         if(voice->mSourceID.load(std::memory_order_acquire) == sid)
90             return voice;
91     }
92     source->VoiceIdx = INVALID_VOICE_IDX;
93     return nullptr;
94 }
95 
96 
UpdateSourceProps(const ALsource * source,Voice * voice,ALCcontext * context)97 void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context)
98 {
99     /* Get an unused property container, or allocate a new one as needed. */
100     VoicePropsItem *props{context->mFreeVoiceProps.load(std::memory_order_acquire)};
101     if(!props)
102         props = new VoicePropsItem{};
103     else
104     {
105         VoicePropsItem *next;
106         do {
107             next = props->next.load(std::memory_order_relaxed);
108         } while(context->mFreeVoiceProps.compare_exchange_weak(props, next,
109                 std::memory_order_acq_rel, std::memory_order_acquire) == 0);
110     }
111 
112     props->Pitch = source->Pitch;
113     props->Gain = source->Gain;
114     props->OuterGain = source->OuterGain;
115     props->MinGain = source->MinGain;
116     props->MaxGain = source->MaxGain;
117     props->InnerAngle = source->InnerAngle;
118     props->OuterAngle = source->OuterAngle;
119     props->RefDistance = source->RefDistance;
120     props->MaxDistance = source->MaxDistance;
121     props->RolloffFactor = source->RolloffFactor;
122     props->Position = source->Position;
123     props->Velocity = source->Velocity;
124     props->Direction = source->Direction;
125     props->OrientAt = source->OrientAt;
126     props->OrientUp = source->OrientUp;
127     props->HeadRelative = source->HeadRelative;
128     props->mDistanceModel = source->mDistanceModel;
129     props->mResampler = source->mResampler;
130     props->DirectChannels = source->DirectChannels;
131     props->mSpatializeMode = source->mSpatialize;
132 
133     props->DryGainHFAuto = source->DryGainHFAuto;
134     props->WetGainAuto = source->WetGainAuto;
135     props->WetGainHFAuto = source->WetGainHFAuto;
136     props->OuterGainHF = source->OuterGainHF;
137 
138     props->AirAbsorptionFactor = source->AirAbsorptionFactor;
139     props->RoomRolloffFactor = source->RoomRolloffFactor;
140     props->DopplerFactor = source->DopplerFactor;
141 
142     props->StereoPan = source->StereoPan;
143 
144     props->Radius = source->Radius;
145 
146     props->Direct.Gain = source->Direct.Gain;
147     props->Direct.GainHF = source->Direct.GainHF;
148     props->Direct.HFReference = source->Direct.HFReference;
149     props->Direct.GainLF = source->Direct.GainLF;
150     props->Direct.LFReference = source->Direct.LFReference;
151 
152     auto copy_send = [](const ALsource::SendData &srcsend) noexcept -> VoiceProps::SendData
153     {
154         VoiceProps::SendData ret{};
155         ret.Slot = srcsend.Slot ? &srcsend.Slot->mSlot : nullptr;
156         ret.Gain = srcsend.Gain;
157         ret.GainHF = srcsend.GainHF;
158         ret.HFReference = srcsend.HFReference;
159         ret.GainLF = srcsend.GainLF;
160         ret.LFReference = srcsend.LFReference;
161         return ret;
162     };
163     std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send);
164     if(!props->Send[0].Slot && context->mDefaultSlot)
165         props->Send[0].Slot = &context->mDefaultSlot->mSlot;
166 
167     /* Set the new container for updating internal parameters. */
168     props = voice->mUpdate.exchange(props, std::memory_order_acq_rel);
169     if(props)
170     {
171         /* If there was an unused update container, put it back in the
172          * freelist.
173          */
174         AtomicReplaceHead(context->mFreeVoiceProps, props);
175     }
176 }
177 
178 /* GetSourceSampleOffset
179  *
180  * Gets the current read offset for the given Source, in 32.32 fixed-point
181  * samples. The offset is relative to the start of the queue (not the start of
182  * the current buffer).
183  */
GetSourceSampleOffset(ALsource * Source,ALCcontext * context,nanoseconds * clocktime)184 int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
185 {
186     ALCdevice *device{context->mDevice.get()};
187     const VoiceBufferItem *Current{};
188     uint64_t readPos{};
189     ALuint refcount;
190     Voice *voice;
191 
192     do {
193         refcount = device->waitForMix();
194         *clocktime = GetDeviceClockTime(device);
195         voice = GetSourceVoice(Source, context);
196         if(voice)
197         {
198             Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
199 
200             readPos  = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << 32;
201             readPos |= uint64_t{voice->mPositionFrac.load(std::memory_order_relaxed)} <<
202                        (32-MixerFracBits);
203         }
204         std::atomic_thread_fence(std::memory_order_acquire);
205     } while(refcount != device->MixCount.load(std::memory_order_relaxed));
206 
207     if(!voice)
208         return 0;
209 
210     for(auto &item : Source->mQueue)
211     {
212         if(&item == Current) break;
213         readPos += uint64_t{item.mSampleLen} << 32;
214     }
215     return static_cast<int64_t>(minu64(readPos, 0x7fffffffffffffff_u64));
216 }
217 
218 /* GetSourceSecOffset
219  *
220  * Gets the current read offset for the given Source, in seconds. The offset is
221  * relative to the start of the queue (not the start of the current buffer).
222  */
GetSourceSecOffset(ALsource * Source,ALCcontext * context,nanoseconds * clocktime)223 double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
224 {
225     ALCdevice *device{context->mDevice.get()};
226     const VoiceBufferItem *Current{};
227     uint64_t readPos{};
228     ALuint refcount;
229     Voice *voice;
230 
231     do {
232         refcount = device->waitForMix();
233         *clocktime = GetDeviceClockTime(device);
234         voice = GetSourceVoice(Source, context);
235         if(voice)
236         {
237             Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
238 
239             readPos  = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << MixerFracBits;
240             readPos |= voice->mPositionFrac.load(std::memory_order_relaxed);
241         }
242         std::atomic_thread_fence(std::memory_order_acquire);
243     } while(refcount != device->MixCount.load(std::memory_order_relaxed));
244 
245     if(!voice)
246         return 0.0f;
247 
248     const ALbuffer *BufferFmt{nullptr};
249     auto BufferList = Source->mQueue.cbegin();
250     while(BufferList != Source->mQueue.cend() && std::addressof(*BufferList) != Current)
251     {
252         if(!BufferFmt) BufferFmt = BufferList->mBuffer;
253         readPos += uint64_t{BufferList->mSampleLen} << MixerFracBits;
254         ++BufferList;
255     }
256     while(BufferList != Source->mQueue.cend() && !BufferFmt)
257     {
258         BufferFmt = BufferList->mBuffer;
259         ++BufferList;
260     }
261     assert(BufferFmt != nullptr);
262 
263     return static_cast<double>(readPos) / double{MixerFracOne} / BufferFmt->mSampleRate;
264 }
265 
266 /* GetSourceOffset
267  *
268  * Gets the current read offset for the given Source, in the appropriate format
269  * (Bytes, Samples or Seconds). The offset is relative to the start of the
270  * queue (not the start of the current buffer).
271  */
GetSourceOffset(ALsource * Source,ALenum name,ALCcontext * context)272 double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
273 {
274     ALCdevice *device{context->mDevice.get()};
275     const VoiceBufferItem *Current{};
276     ALuint readPos{};
277     ALuint readPosFrac{};
278     ALuint refcount;
279     Voice *voice;
280 
281     do {
282         refcount = device->waitForMix();
283         voice = GetSourceVoice(Source, context);
284         if(voice)
285         {
286             Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
287 
288             readPos = voice->mPosition.load(std::memory_order_relaxed);
289             readPosFrac = voice->mPositionFrac.load(std::memory_order_relaxed);
290         }
291         std::atomic_thread_fence(std::memory_order_acquire);
292     } while(refcount != device->MixCount.load(std::memory_order_relaxed));
293 
294     if(!voice)
295         return 0.0;
296 
297     const ALbuffer *BufferFmt{nullptr};
298     auto BufferList = Source->mQueue.cbegin();
299     while(BufferList != Source->mQueue.cend() && std::addressof(*BufferList) != Current)
300     {
301         if(!BufferFmt) BufferFmt = BufferList->mBuffer;
302         readPos += BufferList->mSampleLen;
303         ++BufferList;
304     }
305     while(BufferList != Source->mQueue.cend() && !BufferFmt)
306     {
307         BufferFmt = BufferList->mBuffer;
308         ++BufferList;
309     }
310     assert(BufferFmt != nullptr);
311 
312     double offset{};
313     switch(name)
314     {
315     case AL_SEC_OFFSET:
316         offset = (readPos + readPosFrac/double{MixerFracOne}) / BufferFmt->mSampleRate;
317         break;
318 
319     case AL_SAMPLE_OFFSET:
320         offset = readPos + readPosFrac/double{MixerFracOne};
321         break;
322 
323     case AL_BYTE_OFFSET:
324         if(BufferFmt->OriginalType == UserFmtIMA4)
325         {
326             ALuint FrameBlockSize{BufferFmt->OriginalAlign};
327             ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
328             ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
329 
330             /* Round down to nearest ADPCM block */
331             offset = static_cast<double>(readPos / FrameBlockSize * BlockSize);
332         }
333         else if(BufferFmt->OriginalType == UserFmtMSADPCM)
334         {
335             ALuint FrameBlockSize{BufferFmt->OriginalAlign};
336             ALuint align{(FrameBlockSize-2)/2 + 7};
337             ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
338 
339             /* Round down to nearest ADPCM block */
340             offset = static_cast<double>(readPos / FrameBlockSize * BlockSize);
341         }
342         else
343         {
344             const ALuint FrameSize{BufferFmt->frameSizeFromFmt()};
345             offset = static_cast<double>(readPos * FrameSize);
346         }
347         break;
348     }
349     return offset;
350 }
351 
352 
353 struct VoicePos {
354     ALuint pos, frac;
355     ALbufferQueueItem *bufferitem;
356 };
357 
358 /**
359  * GetSampleOffset
360  *
361  * Retrieves the voice position, fixed-point fraction, and bufferlist item
362  * using the givem offset type and offset. If the offset is out of range,
363  * returns an empty optional.
364  */
GetSampleOffset(al::deque<ALbufferQueueItem> & BufferList,ALenum OffsetType,double Offset)365 al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList, ALenum OffsetType,
366     double Offset)
367 {
368     /* Find the first valid Buffer in the Queue */
369     const ALbuffer *BufferFmt{nullptr};
370     for(auto &item : BufferList)
371     {
372         BufferFmt = item.mBuffer;
373         if(BufferFmt) break;
374     }
375     if(!BufferFmt)
376         return al::nullopt;
377 
378     /* Get sample frame offset */
379     ALuint offset{0u}, frac{0u};
380     double dbloff, dblfrac;
381     switch(OffsetType)
382     {
383     case AL_SEC_OFFSET:
384         dblfrac = std::modf(Offset*BufferFmt->mSampleRate, &dbloff);
385         offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
386         frac = static_cast<ALuint>(mind(dblfrac*MixerFracOne, MixerFracOne-1.0));
387         break;
388 
389     case AL_SAMPLE_OFFSET:
390         dblfrac = std::modf(Offset, &dbloff);
391         offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
392         frac = static_cast<ALuint>(mind(dblfrac*MixerFracOne, MixerFracOne-1.0));
393         break;
394 
395     case AL_BYTE_OFFSET:
396         /* Determine the ByteOffset (and ensure it is block aligned) */
397         offset = static_cast<ALuint>(Offset);
398         if(BufferFmt->OriginalType == UserFmtIMA4)
399         {
400             const ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
401             offset /= align * BufferFmt->channelsFromFmt();
402             offset *= BufferFmt->OriginalAlign;
403         }
404         else if(BufferFmt->OriginalType == UserFmtMSADPCM)
405         {
406             const ALuint align{(BufferFmt->OriginalAlign-2)/2 + 7};
407             offset /= align * BufferFmt->channelsFromFmt();
408             offset *= BufferFmt->OriginalAlign;
409         }
410         else
411             offset /= BufferFmt->frameSizeFromFmt();
412         frac = 0;
413         break;
414     }
415 
416     /* Find the bufferlist item this offset belongs to. */
417     ALuint totalBufferLen{0u};
418     for(auto &item : BufferList)
419     {
420         if(totalBufferLen > offset)
421             break;
422         if(item.mSampleLen > offset-totalBufferLen)
423         {
424             /* Offset is in this buffer */
425             return al::make_optional(VoicePos{offset-totalBufferLen, frac, &item});
426         }
427         totalBufferLen += item.mSampleLen;
428     }
429 
430     /* Offset is out of range of the queue */
431     return al::nullopt;
432 }
433 
434 
InitVoice(Voice * voice,ALsource * source,ALbufferQueueItem * BufferList,ALCcontext * context,ALCdevice * device)435 void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, ALCcontext *context,
436     ALCdevice *device)
437 {
438     voice->mLoopBuffer.store(source->Looping ? &source->mQueue.front() : nullptr,
439         std::memory_order_relaxed);
440 
441     ALbuffer *buffer{BufferList->mBuffer};
442     ALuint num_channels{buffer->channelsFromFmt()};
443     voice->mFrequency = buffer->mSampleRate;
444     voice->mFmtChannels = buffer->mChannels;
445     voice->mFmtType = buffer->mType;
446     voice->mSampleSize  = buffer->bytesFromFmt();
447     voice->mAmbiLayout = buffer->mAmbiLayout;
448     voice->mAmbiScaling = buffer->mAmbiScaling;
449     voice->mAmbiOrder = buffer->mAmbiOrder;
450 
451     if(buffer->mCallback) voice->mFlags |= VoiceIsCallback;
452     else if(source->SourceType == AL_STATIC) voice->mFlags |= VoiceIsStatic;
453     voice->mNumCallbackSamples = 0;
454 
455     /* Clear the stepping value explicitly so the mixer knows not to mix this
456      * until the update gets applied.
457      */
458     voice->mStep = 0;
459 
460     if(voice->mChans.capacity() > 2 && num_channels < voice->mChans.capacity())
461         al::vector<Voice::ChannelData>{}.swap(voice->mChans);
462     voice->mChans.reserve(maxu(2, num_channels));
463     voice->mChans.resize(num_channels);
464 
465     /* Don't need to set the VOICE_IS_AMBISONIC flag if the device is not
466      * higher order than the voice. No HF scaling is necessary to mix it.
467      */
468     if(voice->mAmbiOrder && device->mAmbiOrder > voice->mAmbiOrder)
469     {
470         const uint8_t *OrderFromChan{(voice->mFmtChannels == FmtBFormat2D) ?
471             AmbiIndex::OrderFrom2DChannel().data() :
472             AmbiIndex::OrderFromChannel().data()};
473         const auto scales = BFormatDec::GetHFOrderScales(voice->mAmbiOrder, device->mAmbiOrder);
474 
475         const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
476 
477         for(auto &chandata : voice->mChans)
478         {
479             chandata.mPrevSamples.fill(0.0f);
480             chandata.mAmbiScale = scales[*(OrderFromChan++)];
481             chandata.mAmbiSplitter = splitter;
482             chandata.mDryParams = DirectParams{};
483             std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
484         }
485 
486         voice->mFlags |= VoiceIsAmbisonic;
487     }
488     else
489     {
490         /* Clear previous samples. */
491         for(auto &chandata : voice->mChans)
492         {
493             chandata.mPrevSamples.fill(0.0f);
494             chandata.mDryParams = DirectParams{};
495             std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
496         }
497     }
498 
499     if(device->AvgSpeakerDist > 0.0f)
500     {
501         const float w1{SpeedOfSoundMetersPerSec /
502             (device->AvgSpeakerDist * static_cast<float>(device->Frequency))};
503         for(auto &chandata : voice->mChans)
504             chandata.mDryParams.NFCtrlFilter.init(w1);
505     }
506 
507     source->PropsClean.test_and_set(std::memory_order_acq_rel);
508     UpdateSourceProps(source, voice, context);
509 
510     voice->mSourceID.store(source->id, std::memory_order_release);
511 }
512 
513 
GetVoiceChanger(ALCcontext * ctx)514 VoiceChange *GetVoiceChanger(ALCcontext *ctx)
515 {
516     VoiceChange *vchg{ctx->mVoiceChangeTail};
517     if UNLIKELY(vchg == ctx->mCurrentVoiceChange.load(std::memory_order_acquire))
518     {
519         ctx->allocVoiceChanges(1);
520         vchg = ctx->mVoiceChangeTail;
521     }
522 
523     ctx->mVoiceChangeTail = vchg->mNext.exchange(nullptr, std::memory_order_relaxed);
524 
525     return vchg;
526 }
527 
SendVoiceChanges(ALCcontext * ctx,VoiceChange * tail)528 void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
529 {
530     ALCdevice *device{ctx->mDevice.get()};
531 
532     VoiceChange *oldhead{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
533     while(VoiceChange *next{oldhead->mNext.load(std::memory_order_relaxed)})
534         oldhead = next;
535     oldhead->mNext.store(tail, std::memory_order_release);
536 
537     const bool connected{device->Connected.load(std::memory_order_acquire)};
538     device->waitForMix();
539     if UNLIKELY(!connected)
540     {
541         /* If the device is disconnected, just ignore all pending changes. */
542         VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
543         while(VoiceChange *next{cur->mNext.load(std::memory_order_acquire)})
544         {
545             cur = next;
546             if(Voice *voice{cur->mVoice})
547                 voice->mSourceID.store(0, std::memory_order_relaxed);
548         }
549         ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
550     }
551 }
552 
553 
SetVoiceOffset(Voice * oldvoice,const VoicePos & vpos,ALsource * source,ALCcontext * context,ALCdevice * device)554 bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALCcontext *context,
555     ALCdevice *device)
556 {
557     /* First, get a free voice to start at the new offset. */
558     auto voicelist = context->getVoicesSpan();
559     Voice *newvoice{};
560     ALuint vidx{0};
561     for(Voice *voice : voicelist)
562     {
563         if(voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
564             && voice->mSourceID.load(std::memory_order_relaxed) == 0u
565             && voice->mPendingChange.load(std::memory_order_relaxed) == false)
566         {
567             newvoice = voice;
568             break;
569         }
570         ++vidx;
571     }
572     if UNLIKELY(!newvoice)
573     {
574         auto &allvoices = *context->mVoices.load(std::memory_order_relaxed);
575         if(allvoices.size() == voicelist.size())
576             context->allocVoices(1);
577         context->mActiveVoiceCount.fetch_add(1, std::memory_order_release);
578         voicelist = context->getVoicesSpan();
579 
580         vidx = 0;
581         for(Voice *voice : voicelist)
582         {
583             if(voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
584                 && voice->mSourceID.load(std::memory_order_relaxed) == 0u
585                 && voice->mPendingChange.load(std::memory_order_relaxed) == false)
586             {
587                 newvoice = voice;
588                 break;
589             }
590             ++vidx;
591         }
592     }
593 
594     /* Initialize the new voice and set its starting offset.
595      * TODO: It might be better to have the VoiceChange processing copy the old
596      * voice's mixing parameters (and pending update) insead of initializing it
597      * all here. This would just need to set the minimum properties to link the
598      * voice to the source and its position-dependent properties (including the
599      * fading flag).
600      */
601     newvoice->mPlayState.store(Voice::Pending, std::memory_order_relaxed);
602     newvoice->mPosition.store(vpos.pos, std::memory_order_relaxed);
603     newvoice->mPositionFrac.store(vpos.frac, std::memory_order_relaxed);
604     newvoice->mCurrentBuffer.store(vpos.bufferitem, std::memory_order_relaxed);
605     newvoice->mFlags = 0u;
606     if(vpos.pos > 0 || vpos.frac > 0 || vpos.bufferitem != &source->mQueue.front())
607         newvoice->mFlags |= VoiceIsFading;
608     InitVoice(newvoice, source, vpos.bufferitem, context, device);
609     source->VoiceIdx = vidx;
610 
611     /* Set the old voice as having a pending change, and send it off with the
612      * new one with a new offset voice change.
613      */
614     oldvoice->mPendingChange.store(true, std::memory_order_relaxed);
615 
616     VoiceChange *vchg{GetVoiceChanger(context)};
617     vchg->mOldVoice = oldvoice;
618     vchg->mVoice = newvoice;
619     vchg->mSourceID = source->id;
620     vchg->mState = VChangeState::Restart;
621     SendVoiceChanges(context, vchg);
622 
623     /* If the old voice still has a sourceID, it's still active and the change-
624      * over will work on the next update.
625      */
626     if LIKELY(oldvoice->mSourceID.load(std::memory_order_acquire) != 0u)
627         return true;
628 
629     /* Otherwise, if the new voice's state is not pending, the change-over
630      * already happened.
631      */
632     if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending)
633         return true;
634 
635     /* Otherwise, wait for any current mix to finish and check one last time. */
636     device->waitForMix();
637     if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending)
638         return true;
639     /* The change-over failed because the old voice stopped before the new
640      * voice could start at the new offset. Let go of the new voice and have
641      * the caller store the source offset since it's stopped.
642      */
643     newvoice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
644     newvoice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
645     newvoice->mSourceID.store(0u, std::memory_order_relaxed);
646     newvoice->mPlayState.store(Voice::Stopped, std::memory_order_relaxed);
647     return false;
648 }
649 
650 
651 /**
652  * Returns if the last known state for the source was playing or paused. Does
653  * not sync with the mixer voice.
654  */
IsPlayingOrPaused(ALsource * source)655 inline bool IsPlayingOrPaused(ALsource *source)
656 { return source->state == AL_PLAYING || source->state == AL_PAUSED; }
657 
658 /**
659  * Returns an updated source state using the matching voice's status (or lack
660  * thereof).
661  */
GetSourceState(ALsource * source,Voice * voice)662 inline ALenum GetSourceState(ALsource *source, Voice *voice)
663 {
664     if(!voice && source->state == AL_PLAYING)
665         source->state = AL_STOPPED;
666     return source->state;
667 }
668 
669 /**
670  * Returns if the source should specify an update, given the context's
671  * deferring state and the source's last known state.
672  */
SourceShouldUpdate(ALsource * source,ALCcontext * context)673 inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context)
674 {
675     return !context->mDeferUpdates.load(std::memory_order_acquire) &&
676            IsPlayingOrPaused(source);
677 }
678 
679 
EnsureSources(ALCcontext * context,size_t needed)680 bool EnsureSources(ALCcontext *context, size_t needed)
681 {
682     size_t count{std::accumulate(context->mSourceList.cbegin(), context->mSourceList.cend(),
683         size_t{0},
684         [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
685         { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
686 
687     while(needed > count)
688     {
689         if UNLIKELY(context->mSourceList.size() >= 1<<25)
690             return false;
691 
692         context->mSourceList.emplace_back();
693         auto sublist = context->mSourceList.end() - 1;
694         sublist->FreeMask = ~0_u64;
695         sublist->Sources = static_cast<ALsource*>(al_calloc(alignof(ALsource), sizeof(ALsource)*64));
696         if UNLIKELY(!sublist->Sources)
697         {
698             context->mSourceList.pop_back();
699             return false;
700         }
701         count += 64;
702     }
703     return true;
704 }
705 
AllocSource(ALCcontext * context)706 ALsource *AllocSource(ALCcontext *context)
707 {
708     auto sublist = std::find_if(context->mSourceList.begin(), context->mSourceList.end(),
709         [](const SourceSubList &entry) noexcept -> bool
710         { return entry.FreeMask != 0; }
711     );
712     auto lidx = static_cast<ALuint>(std::distance(context->mSourceList.begin(), sublist));
713     auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
714 
715     ALsource *source{::new(sublist->Sources + slidx) ALsource{}};
716 
717     /* Add 1 to avoid source ID 0. */
718     source->id = ((lidx<<6) | slidx) + 1;
719 
720     context->mNumSources += 1;
721     sublist->FreeMask &= ~(1_u64 << slidx);
722 
723     return source;
724 }
725 
FreeSource(ALCcontext * context,ALsource * source)726 void FreeSource(ALCcontext *context, ALsource *source)
727 {
728     const ALuint id{source->id - 1};
729     const size_t lidx{id >> 6};
730     const ALuint slidx{id & 0x3f};
731 
732     if(IsPlayingOrPaused(source))
733     {
734         if(Voice *voice{GetSourceVoice(source, context)})
735         {
736             VoiceChange *vchg{GetVoiceChanger(context)};
737 
738             voice->mPendingChange.store(true, std::memory_order_relaxed);
739             vchg->mVoice = voice;
740             vchg->mSourceID = source->id;
741             vchg->mState = VChangeState::Stop;
742 
743             SendVoiceChanges(context, vchg);
744         }
745     }
746 
747     al::destroy_at(source);
748 
749     context->mSourceList[lidx].FreeMask |= 1_u64 << slidx;
750     context->mNumSources--;
751 }
752 
753 
LookupSource(ALCcontext * context,ALuint id)754 inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept
755 {
756     const size_t lidx{(id-1) >> 6};
757     const ALuint slidx{(id-1) & 0x3f};
758 
759     if UNLIKELY(lidx >= context->mSourceList.size())
760         return nullptr;
761     SourceSubList &sublist{context->mSourceList[lidx]};
762     if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
763         return nullptr;
764     return sublist.Sources + slidx;
765 }
766 
LookupBuffer(ALCdevice * device,ALuint id)767 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
768 {
769     const size_t lidx{(id-1) >> 6};
770     const ALuint slidx{(id-1) & 0x3f};
771 
772     if UNLIKELY(lidx >= device->BufferList.size())
773         return nullptr;
774     BufferSubList &sublist = device->BufferList[lidx];
775     if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
776         return nullptr;
777     return sublist.Buffers + slidx;
778 }
779 
LookupFilter(ALCdevice * device,ALuint id)780 inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept
781 {
782     const size_t lidx{(id-1) >> 6};
783     const ALuint slidx{(id-1) & 0x3f};
784 
785     if UNLIKELY(lidx >= device->FilterList.size())
786         return nullptr;
787     FilterSubList &sublist = device->FilterList[lidx];
788     if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
789         return nullptr;
790     return sublist.Filters + slidx;
791 }
792 
LookupEffectSlot(ALCcontext * context,ALuint id)793 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
794 {
795     const size_t lidx{(id-1) >> 6};
796     const ALuint slidx{(id-1) & 0x3f};
797 
798     if UNLIKELY(lidx >= context->mEffectSlotList.size())
799         return nullptr;
800     EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
801     if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
802         return nullptr;
803     return sublist.EffectSlots + slidx;
804 }
805 
806 
SpatializeModeFromEnum(ALenum mode)807 al::optional<SpatializeMode> SpatializeModeFromEnum(ALenum mode)
808 {
809     switch(mode)
810     {
811     case AL_FALSE: return al::make_optional(SpatializeMode::Off);
812     case AL_TRUE: return al::make_optional(SpatializeMode::On);
813     case AL_AUTO_SOFT: return al::make_optional(SpatializeMode::Auto);
814     }
815     WARN("Unsupported spatialize mode: 0x%04x\n", mode);
816     return al::nullopt;
817 }
EnumFromSpatializeMode(SpatializeMode mode)818 ALenum EnumFromSpatializeMode(SpatializeMode mode)
819 {
820     switch(mode)
821     {
822     case SpatializeMode::Off: return AL_FALSE;
823     case SpatializeMode::On: return AL_TRUE;
824     case SpatializeMode::Auto: return AL_AUTO_SOFT;
825     }
826     throw std::runtime_error{"Invalid SpatializeMode: "+std::to_string(int(mode))};
827 }
828 
DirectModeFromEnum(ALenum mode)829 al::optional<DirectMode> DirectModeFromEnum(ALenum mode)
830 {
831     switch(mode)
832     {
833     case AL_FALSE: return al::make_optional(DirectMode::Off);
834     case AL_DROP_UNMATCHED_SOFT: return al::make_optional(DirectMode::DropMismatch);
835     case AL_REMIX_UNMATCHED_SOFT: return al::make_optional(DirectMode::RemixMismatch);
836     }
837     WARN("Unsupported direct mode: 0x%04x\n", mode);
838     return al::nullopt;
839 }
EnumFromDirectMode(DirectMode mode)840 ALenum EnumFromDirectMode(DirectMode mode)
841 {
842     switch(mode)
843     {
844     case DirectMode::Off: return AL_FALSE;
845     case DirectMode::DropMismatch: return AL_DROP_UNMATCHED_SOFT;
846     case DirectMode::RemixMismatch: return AL_REMIX_UNMATCHED_SOFT;
847     }
848     throw std::runtime_error{"Invalid DirectMode: "+std::to_string(int(mode))};
849 }
850 
DistanceModelFromALenum(ALenum model)851 al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
852 {
853     switch(model)
854     {
855     case AL_NONE: return al::make_optional(DistanceModel::Disable);
856     case AL_INVERSE_DISTANCE: return al::make_optional(DistanceModel::Inverse);
857     case AL_INVERSE_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::InverseClamped);
858     case AL_LINEAR_DISTANCE: return al::make_optional(DistanceModel::Linear);
859     case AL_LINEAR_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::LinearClamped);
860     case AL_EXPONENT_DISTANCE: return al::make_optional(DistanceModel::Exponent);
861     case AL_EXPONENT_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::ExponentClamped);
862     }
863     return al::nullopt;
864 }
ALenumFromDistanceModel(DistanceModel model)865 ALenum ALenumFromDistanceModel(DistanceModel model)
866 {
867     switch(model)
868     {
869     case DistanceModel::Disable: return AL_NONE;
870     case DistanceModel::Inverse: return AL_INVERSE_DISTANCE;
871     case DistanceModel::InverseClamped: return AL_INVERSE_DISTANCE_CLAMPED;
872     case DistanceModel::Linear: return AL_LINEAR_DISTANCE;
873     case DistanceModel::LinearClamped: return AL_LINEAR_DISTANCE_CLAMPED;
874     case DistanceModel::Exponent: return AL_EXPONENT_DISTANCE;
875     case DistanceModel::ExponentClamped: return AL_EXPONENT_DISTANCE_CLAMPED;
876     }
877     throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast<int>(model))};
878 }
879 
880 enum SourceProp : ALenum {
881     srcPitch = AL_PITCH,
882     srcGain = AL_GAIN,
883     srcMinGain = AL_MIN_GAIN,
884     srcMaxGain = AL_MAX_GAIN,
885     srcMaxDistance = AL_MAX_DISTANCE,
886     srcRolloffFactor = AL_ROLLOFF_FACTOR,
887     srcDopplerFactor = AL_DOPPLER_FACTOR,
888     srcConeOuterGain = AL_CONE_OUTER_GAIN,
889     srcSecOffset = AL_SEC_OFFSET,
890     srcSampleOffset = AL_SAMPLE_OFFSET,
891     srcByteOffset = AL_BYTE_OFFSET,
892     srcConeInnerAngle = AL_CONE_INNER_ANGLE,
893     srcConeOuterAngle = AL_CONE_OUTER_ANGLE,
894     srcRefDistance = AL_REFERENCE_DISTANCE,
895 
896     srcPosition = AL_POSITION,
897     srcVelocity = AL_VELOCITY,
898     srcDirection = AL_DIRECTION,
899 
900     srcSourceRelative = AL_SOURCE_RELATIVE,
901     srcLooping = AL_LOOPING,
902     srcBuffer = AL_BUFFER,
903     srcSourceState = AL_SOURCE_STATE,
904     srcBuffersQueued = AL_BUFFERS_QUEUED,
905     srcBuffersProcessed = AL_BUFFERS_PROCESSED,
906     srcSourceType = AL_SOURCE_TYPE,
907 
908     /* ALC_EXT_EFX */
909     srcConeOuterGainHF = AL_CONE_OUTER_GAINHF,
910     srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR,
911     srcRoomRolloffFactor =  AL_ROOM_ROLLOFF_FACTOR,
912     srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO,
913     srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO,
914     srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO,
915     srcDirectFilter = AL_DIRECT_FILTER,
916     srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER,
917 
918     /* AL_SOFT_direct_channels */
919     srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT,
920 
921     /* AL_EXT_source_distance_model */
922     srcDistanceModel = AL_DISTANCE_MODEL,
923 
924     /* AL_SOFT_source_latency */
925     srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT,
926     srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT,
927 
928     /* AL_EXT_STEREO_ANGLES */
929     srcAngles = AL_STEREO_ANGLES,
930 
931     /* AL_EXT_SOURCE_RADIUS */
932     srcRadius = AL_SOURCE_RADIUS,
933 
934     /* AL_EXT_BFORMAT */
935     srcOrientation = AL_ORIENTATION,
936 
937     /* AL_SOFT_source_resampler */
938     srcResampler = AL_SOURCE_RESAMPLER_SOFT,
939 
940     /* AL_SOFT_source_spatialize */
941     srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT,
942 
943     /* ALC_SOFT_device_clock */
944     srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT,
945     srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT,
946 };
947 
948 
949 constexpr size_t MaxValues{6u};
950 
FloatValsByProp(ALenum prop)951 ALuint FloatValsByProp(ALenum prop)
952 {
953     switch(static_cast<SourceProp>(prop))
954     {
955     case AL_PITCH:
956     case AL_GAIN:
957     case AL_MIN_GAIN:
958     case AL_MAX_GAIN:
959     case AL_MAX_DISTANCE:
960     case AL_ROLLOFF_FACTOR:
961     case AL_DOPPLER_FACTOR:
962     case AL_CONE_OUTER_GAIN:
963     case AL_SEC_OFFSET:
964     case AL_SAMPLE_OFFSET:
965     case AL_BYTE_OFFSET:
966     case AL_CONE_INNER_ANGLE:
967     case AL_CONE_OUTER_ANGLE:
968     case AL_REFERENCE_DISTANCE:
969     case AL_CONE_OUTER_GAINHF:
970     case AL_AIR_ABSORPTION_FACTOR:
971     case AL_ROOM_ROLLOFF_FACTOR:
972     case AL_DIRECT_FILTER_GAINHF_AUTO:
973     case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
974     case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
975     case AL_DIRECT_CHANNELS_SOFT:
976     case AL_DISTANCE_MODEL:
977     case AL_SOURCE_RELATIVE:
978     case AL_LOOPING:
979     case AL_SOURCE_STATE:
980     case AL_BUFFERS_QUEUED:
981     case AL_BUFFERS_PROCESSED:
982     case AL_SOURCE_TYPE:
983     case AL_SOURCE_RADIUS:
984     case AL_SOURCE_RESAMPLER_SOFT:
985     case AL_SOURCE_SPATIALIZE_SOFT:
986         return 1;
987 
988     case AL_STEREO_ANGLES:
989         return 2;
990 
991     case AL_POSITION:
992     case AL_VELOCITY:
993     case AL_DIRECTION:
994         return 3;
995 
996     case AL_ORIENTATION:
997         return 6;
998 
999     case AL_SEC_OFFSET_LATENCY_SOFT:
1000     case AL_SEC_OFFSET_CLOCK_SOFT:
1001         break; /* Double only */
1002 
1003     case AL_BUFFER:
1004     case AL_DIRECT_FILTER:
1005     case AL_AUXILIARY_SEND_FILTER:
1006         break; /* i/i64 only */
1007     case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1008     case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1009         break; /* i64 only */
1010     }
1011     return 0;
1012 }
DoubleValsByProp(ALenum prop)1013 ALuint DoubleValsByProp(ALenum prop)
1014 {
1015     switch(static_cast<SourceProp>(prop))
1016     {
1017     case AL_PITCH:
1018     case AL_GAIN:
1019     case AL_MIN_GAIN:
1020     case AL_MAX_GAIN:
1021     case AL_MAX_DISTANCE:
1022     case AL_ROLLOFF_FACTOR:
1023     case AL_DOPPLER_FACTOR:
1024     case AL_CONE_OUTER_GAIN:
1025     case AL_SEC_OFFSET:
1026     case AL_SAMPLE_OFFSET:
1027     case AL_BYTE_OFFSET:
1028     case AL_CONE_INNER_ANGLE:
1029     case AL_CONE_OUTER_ANGLE:
1030     case AL_REFERENCE_DISTANCE:
1031     case AL_CONE_OUTER_GAINHF:
1032     case AL_AIR_ABSORPTION_FACTOR:
1033     case AL_ROOM_ROLLOFF_FACTOR:
1034     case AL_DIRECT_FILTER_GAINHF_AUTO:
1035     case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1036     case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1037     case AL_DIRECT_CHANNELS_SOFT:
1038     case AL_DISTANCE_MODEL:
1039     case AL_SOURCE_RELATIVE:
1040     case AL_LOOPING:
1041     case AL_SOURCE_STATE:
1042     case AL_BUFFERS_QUEUED:
1043     case AL_BUFFERS_PROCESSED:
1044     case AL_SOURCE_TYPE:
1045     case AL_SOURCE_RADIUS:
1046     case AL_SOURCE_RESAMPLER_SOFT:
1047     case AL_SOURCE_SPATIALIZE_SOFT:
1048         return 1;
1049 
1050     case AL_SEC_OFFSET_LATENCY_SOFT:
1051     case AL_SEC_OFFSET_CLOCK_SOFT:
1052     case AL_STEREO_ANGLES:
1053         return 2;
1054 
1055     case AL_POSITION:
1056     case AL_VELOCITY:
1057     case AL_DIRECTION:
1058         return 3;
1059 
1060     case AL_ORIENTATION:
1061         return 6;
1062 
1063     case AL_BUFFER:
1064     case AL_DIRECT_FILTER:
1065     case AL_AUXILIARY_SEND_FILTER:
1066         break; /* i/i64 only */
1067     case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1068     case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1069         break; /* i64 only */
1070     }
1071     return 0;
1072 }
1073 
1074 
1075 bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const float> values);
1076 bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int> values);
1077 bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int64_t> values);
1078 
1079 #define CHECKSIZE(v, s) do { \
1080     if LIKELY((v).size() == (s) || (v).size() == MaxValues) break;            \
1081     Context->setError(AL_INVALID_ENUM,                                        \
1082         "Property 0x%04x expects %d value(s), got %zu", prop, (s),            \
1083         (v).size());                                                          \
1084     return false;                                                             \
1085 } while(0)
1086 #define CHECKVAL(x) do {                                                      \
1087     if LIKELY(x) break;                                                       \
1088     Context->setError(AL_INVALID_VALUE, "Value out of range");                \
1089     return false;                                                             \
1090 } while(0)
1091 
UpdateSourceProps(ALsource * source,ALCcontext * context)1092 bool UpdateSourceProps(ALsource *source, ALCcontext *context)
1093 {
1094     Voice *voice;
1095     if(SourceShouldUpdate(source, context) && (voice=GetSourceVoice(source, context)) != nullptr)
1096         UpdateSourceProps(source, voice, context);
1097     else
1098         source->PropsClean.clear(std::memory_order_release);
1099     return true;
1100 }
1101 
SetSourcefv(ALsource * Source,ALCcontext * Context,SourceProp prop,const al::span<const float> values)1102 bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const float> values)
1103 {
1104     int ival;
1105 
1106     switch(prop)
1107     {
1108     case AL_SEC_OFFSET_LATENCY_SOFT:
1109     case AL_SEC_OFFSET_CLOCK_SOFT:
1110         /* Query only */
1111         SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1112             "Setting read-only source property 0x%04x", prop);
1113 
1114     case AL_PITCH:
1115         CHECKSIZE(values, 1);
1116         CHECKVAL(values[0] >= 0.0f);
1117 
1118         Source->Pitch = values[0];
1119         return UpdateSourceProps(Source, Context);
1120 
1121     case AL_CONE_INNER_ANGLE:
1122         CHECKSIZE(values, 1);
1123         CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
1124 
1125         Source->InnerAngle = values[0];
1126         return UpdateSourceProps(Source, Context);
1127 
1128     case AL_CONE_OUTER_ANGLE:
1129         CHECKSIZE(values, 1);
1130         CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
1131 
1132         Source->OuterAngle = values[0];
1133         return UpdateSourceProps(Source, Context);
1134 
1135     case AL_GAIN:
1136         CHECKSIZE(values, 1);
1137         CHECKVAL(values[0] >= 0.0f);
1138 
1139         Source->Gain = values[0];
1140         return UpdateSourceProps(Source, Context);
1141 
1142     case AL_MAX_DISTANCE:
1143         CHECKSIZE(values, 1);
1144         CHECKVAL(values[0] >= 0.0f);
1145 
1146         Source->MaxDistance = values[0];
1147         return UpdateSourceProps(Source, Context);
1148 
1149     case AL_ROLLOFF_FACTOR:
1150         CHECKSIZE(values, 1);
1151         CHECKVAL(values[0] >= 0.0f);
1152 
1153         Source->RolloffFactor = values[0];
1154         return UpdateSourceProps(Source, Context);
1155 
1156     case AL_REFERENCE_DISTANCE:
1157         CHECKSIZE(values, 1);
1158         CHECKVAL(values[0] >= 0.0f);
1159 
1160         Source->RefDistance = values[0];
1161         return UpdateSourceProps(Source, Context);
1162 
1163     case AL_MIN_GAIN:
1164         CHECKSIZE(values, 1);
1165         CHECKVAL(values[0] >= 0.0f);
1166 
1167         Source->MinGain = values[0];
1168         return UpdateSourceProps(Source, Context);
1169 
1170     case AL_MAX_GAIN:
1171         CHECKSIZE(values, 1);
1172         CHECKVAL(values[0] >= 0.0f);
1173 
1174         Source->MaxGain = values[0];
1175         return UpdateSourceProps(Source, Context);
1176 
1177     case AL_CONE_OUTER_GAIN:
1178         CHECKSIZE(values, 1);
1179         CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1180 
1181         Source->OuterGain = values[0];
1182         return UpdateSourceProps(Source, Context);
1183 
1184     case AL_CONE_OUTER_GAINHF:
1185         CHECKSIZE(values, 1);
1186         CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1187 
1188         Source->OuterGainHF = values[0];
1189         return UpdateSourceProps(Source, Context);
1190 
1191     case AL_AIR_ABSORPTION_FACTOR:
1192         CHECKSIZE(values, 1);
1193         CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
1194 
1195         Source->AirAbsorptionFactor = values[0];
1196         return UpdateSourceProps(Source, Context);
1197 
1198     case AL_ROOM_ROLLOFF_FACTOR:
1199         CHECKSIZE(values, 1);
1200         CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
1201 
1202         Source->RoomRolloffFactor = values[0];
1203         return UpdateSourceProps(Source, Context);
1204 
1205     case AL_DOPPLER_FACTOR:
1206         CHECKSIZE(values, 1);
1207         CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
1208 
1209         Source->DopplerFactor = values[0];
1210         return UpdateSourceProps(Source, Context);
1211 
1212     case AL_SEC_OFFSET:
1213     case AL_SAMPLE_OFFSET:
1214     case AL_BYTE_OFFSET:
1215         CHECKSIZE(values, 1);
1216         CHECKVAL(values[0] >= 0.0f);
1217 
1218         if(Voice *voice{GetSourceVoice(Source, Context)})
1219         {
1220             if((voice->mFlags&VoiceIsCallback))
1221                 SETERR_RETURN(Context, AL_INVALID_VALUE, false,
1222                     "Source offset for callback is invalid");
1223             auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
1224             if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid offset");
1225 
1226             if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mDevice.get()))
1227                 return true;
1228         }
1229         Source->OffsetType = prop;
1230         Source->Offset = values[0];
1231         return true;
1232 
1233     case AL_SOURCE_RADIUS:
1234         CHECKSIZE(values, 1);
1235         CHECKVAL(values[0] >= 0.0f && std::isfinite(values[0]));
1236 
1237         Source->Radius = values[0];
1238         return UpdateSourceProps(Source, Context);
1239 
1240     case AL_STEREO_ANGLES:
1241         CHECKSIZE(values, 2);
1242         CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]));
1243 
1244         Source->StereoPan[0] = values[0];
1245         Source->StereoPan[1] = values[1];
1246         return UpdateSourceProps(Source, Context);
1247 
1248 
1249     case AL_POSITION:
1250         CHECKSIZE(values, 3);
1251         CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1252 
1253         Source->Position[0] = values[0];
1254         Source->Position[1] = values[1];
1255         Source->Position[2] = values[2];
1256         return UpdateSourceProps(Source, Context);
1257 
1258     case AL_VELOCITY:
1259         CHECKSIZE(values, 3);
1260         CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1261 
1262         Source->Velocity[0] = values[0];
1263         Source->Velocity[1] = values[1];
1264         Source->Velocity[2] = values[2];
1265         return UpdateSourceProps(Source, Context);
1266 
1267     case AL_DIRECTION:
1268         CHECKSIZE(values, 3);
1269         CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1270 
1271         Source->Direction[0] = values[0];
1272         Source->Direction[1] = values[1];
1273         Source->Direction[2] = values[2];
1274         return UpdateSourceProps(Source, Context);
1275 
1276     case AL_ORIENTATION:
1277         CHECKSIZE(values, 6);
1278         CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2])
1279             && std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]));
1280 
1281         Source->OrientAt[0] = values[0];
1282         Source->OrientAt[1] = values[1];
1283         Source->OrientAt[2] = values[2];
1284         Source->OrientUp[0] = values[3];
1285         Source->OrientUp[1] = values[4];
1286         Source->OrientUp[2] = values[5];
1287         return UpdateSourceProps(Source, Context);
1288 
1289 
1290     case AL_SOURCE_RELATIVE:
1291     case AL_LOOPING:
1292     case AL_SOURCE_STATE:
1293     case AL_SOURCE_TYPE:
1294     case AL_DISTANCE_MODEL:
1295     case AL_DIRECT_FILTER_GAINHF_AUTO:
1296     case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1297     case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1298     case AL_DIRECT_CHANNELS_SOFT:
1299     case AL_SOURCE_RESAMPLER_SOFT:
1300     case AL_SOURCE_SPATIALIZE_SOFT:
1301         CHECKSIZE(values, 1);
1302         ival = static_cast<int>(values[0]);
1303         return SetSourceiv(Source, Context, prop, {&ival, 1u});
1304 
1305     case AL_BUFFERS_QUEUED:
1306     case AL_BUFFERS_PROCESSED:
1307         CHECKSIZE(values, 1);
1308         ival = static_cast<int>(static_cast<ALuint>(values[0]));
1309         return SetSourceiv(Source, Context, prop, {&ival, 1u});
1310 
1311     case AL_BUFFER:
1312     case AL_DIRECT_FILTER:
1313     case AL_AUXILIARY_SEND_FILTER:
1314     case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1315     case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1316         break;
1317     }
1318 
1319     ERR("Unexpected property: 0x%04x\n", prop);
1320     Context->setError(AL_INVALID_ENUM, "Invalid source float property 0x%04x", prop);
1321     return false;
1322 }
1323 
SetSourceiv(ALsource * Source,ALCcontext * Context,SourceProp prop,const al::span<const int> values)1324 bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int> values)
1325 {
1326     ALCdevice *device{Context->mDevice.get()};
1327     ALeffectslot *slot{nullptr};
1328     al::deque<ALbufferQueueItem> oldlist;
1329     std::unique_lock<std::mutex> slotlock;
1330     float fvals[6];
1331 
1332     switch(prop)
1333     {
1334     case AL_SOURCE_STATE:
1335     case AL_SOURCE_TYPE:
1336     case AL_BUFFERS_QUEUED:
1337     case AL_BUFFERS_PROCESSED:
1338         /* Query only */
1339         SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1340             "Setting read-only source property 0x%04x", prop);
1341 
1342     case AL_SOURCE_RELATIVE:
1343         CHECKSIZE(values, 1);
1344         CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1345 
1346         Source->HeadRelative = values[0] != AL_FALSE;
1347         return UpdateSourceProps(Source, Context);
1348 
1349     case AL_LOOPING:
1350         CHECKSIZE(values, 1);
1351         CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1352 
1353         Source->Looping = values[0] != AL_FALSE;
1354         if(IsPlayingOrPaused(Source))
1355         {
1356             if(Voice *voice{GetSourceVoice(Source, Context)})
1357             {
1358                 if(Source->Looping)
1359                     voice->mLoopBuffer.store(&Source->mQueue.front(), std::memory_order_release);
1360                 else
1361                     voice->mLoopBuffer.store(nullptr, std::memory_order_release);
1362 
1363                 /* If the source is playing, wait for the current mix to finish
1364                  * to ensure it isn't currently looping back or reaching the
1365                  * end.
1366                  */
1367                 device->waitForMix();
1368             }
1369         }
1370         return true;
1371 
1372     case AL_BUFFER:
1373         CHECKSIZE(values, 1);
1374         {
1375             const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
1376             if(state == AL_PLAYING || state == AL_PAUSED)
1377                 SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1378                     "Setting buffer on playing or paused source %u", Source->id);
1379         }
1380         if(values[0])
1381         {
1382             std::lock_guard<std::mutex> _{device->BufferLock};
1383             ALbuffer *buffer{LookupBuffer(device, static_cast<ALuint>(values[0]))};
1384             if(!buffer)
1385                 SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid buffer ID %u",
1386                     static_cast<ALuint>(values[0]));
1387             if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
1388                 SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1389                     "Setting non-persistently mapped buffer %u", buffer->id);
1390             if(buffer->mCallback && ReadRef(buffer->ref) != 0)
1391                 SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1392                     "Setting already-set callback buffer %u", buffer->id);
1393 
1394             /* Add the selected buffer to a one-item queue */
1395             al::deque<ALbufferQueueItem> newlist;
1396             newlist.emplace_back();
1397             newlist.back().mCallback = buffer->mCallback;
1398             newlist.back().mUserData = buffer->mUserData;
1399             newlist.back().mSampleLen = buffer->mSampleLen;
1400             newlist.back().mLoopStart = buffer->mLoopStart;
1401             newlist.back().mLoopEnd = buffer->mLoopEnd;
1402             newlist.back().mSamples = buffer->mData.data();
1403             newlist.back().mBuffer = buffer;
1404             IncrementRef(buffer->ref);
1405 
1406             /* Source is now Static */
1407             Source->SourceType = AL_STATIC;
1408             Source->mQueue.swap(oldlist);
1409             Source->mQueue.swap(newlist);
1410         }
1411         else
1412         {
1413             /* Source is now Undetermined */
1414             Source->SourceType = AL_UNDETERMINED;
1415             Source->mQueue.swap(oldlist);
1416         }
1417 
1418         /* Delete all elements in the previous queue */
1419         for(auto &item : oldlist)
1420         {
1421             if(ALbuffer *buffer{item.mBuffer})
1422                 DecrementRef(buffer->ref);
1423         }
1424         return true;
1425 
1426     case AL_SEC_OFFSET:
1427     case AL_SAMPLE_OFFSET:
1428     case AL_BYTE_OFFSET:
1429         CHECKSIZE(values, 1);
1430         CHECKVAL(values[0] >= 0);
1431 
1432         if(Voice *voice{GetSourceVoice(Source, Context)})
1433         {
1434             if((voice->mFlags&VoiceIsCallback))
1435                 SETERR_RETURN(Context, AL_INVALID_VALUE, false,
1436                     "Source offset for callback is invalid");
1437             auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
1438             if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid source offset");
1439 
1440             if(SetVoiceOffset(voice, *vpos, Source, Context, device))
1441                 return true;
1442         }
1443         Source->OffsetType = prop;
1444         Source->Offset = values[0];
1445         return true;
1446 
1447     case AL_DIRECT_FILTER:
1448         CHECKSIZE(values, 1);
1449         if(values[0])
1450         {
1451             std::lock_guard<std::mutex> _{device->FilterLock};
1452             ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[0]))};
1453             if(!filter)
1454                 SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid filter ID %u",
1455                     static_cast<ALuint>(values[0]));
1456             Source->Direct.Gain = filter->Gain;
1457             Source->Direct.GainHF = filter->GainHF;
1458             Source->Direct.HFReference = filter->HFReference;
1459             Source->Direct.GainLF = filter->GainLF;
1460             Source->Direct.LFReference = filter->LFReference;
1461         }
1462         else
1463         {
1464             Source->Direct.Gain = 1.0f;
1465             Source->Direct.GainHF = 1.0f;
1466             Source->Direct.HFReference = LOWPASSFREQREF;
1467             Source->Direct.GainLF = 1.0f;
1468             Source->Direct.LFReference = HIGHPASSFREQREF;
1469         }
1470         return UpdateSourceProps(Source, Context);
1471 
1472     case AL_DIRECT_FILTER_GAINHF_AUTO:
1473         CHECKSIZE(values, 1);
1474         CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1475 
1476         Source->DryGainHFAuto = values[0] != AL_FALSE;
1477         return UpdateSourceProps(Source, Context);
1478 
1479     case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1480         CHECKSIZE(values, 1);
1481         CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1482 
1483         Source->WetGainAuto = values[0] != AL_FALSE;
1484         return UpdateSourceProps(Source, Context);
1485 
1486     case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1487         CHECKSIZE(values, 1);
1488         CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1489 
1490         Source->WetGainHFAuto = values[0] != AL_FALSE;
1491         return UpdateSourceProps(Source, Context);
1492 
1493     case AL_DIRECT_CHANNELS_SOFT:
1494         CHECKSIZE(values, 1);
1495         if(auto mode = DirectModeFromEnum(values[0]))
1496         {
1497             Source->DirectChannels = *mode;
1498             return UpdateSourceProps(Source, Context);
1499         }
1500         Context->setError(AL_INVALID_VALUE, "Unsupported AL_DIRECT_CHANNELS_SOFT: 0x%04x\n",
1501             values[0]);
1502         return false;
1503 
1504     case AL_DISTANCE_MODEL:
1505         CHECKSIZE(values, 1);
1506         if(auto model = DistanceModelFromALenum(values[0]))
1507         {
1508             Source->mDistanceModel = *model;
1509             if(Context->mSourceDistanceModel)
1510                 return UpdateSourceProps(Source, Context);
1511             return true;
1512         }
1513         Context->setError(AL_INVALID_VALUE, "Distance model out of range: 0x%04x", values[0]);
1514         return false;
1515 
1516     case AL_SOURCE_RESAMPLER_SOFT:
1517         CHECKSIZE(values, 1);
1518         CHECKVAL(values[0] >= 0 && values[0] <= static_cast<int>(Resampler::Max));
1519 
1520         Source->mResampler = static_cast<Resampler>(values[0]);
1521         return UpdateSourceProps(Source, Context);
1522 
1523     case AL_SOURCE_SPATIALIZE_SOFT:
1524         CHECKSIZE(values, 1);
1525         if(auto mode = SpatializeModeFromEnum(values[0]))
1526         {
1527             Source->mSpatialize = *mode;
1528             return UpdateSourceProps(Source, Context);
1529         }
1530         Context->setError(AL_INVALID_VALUE, "Unsupported AL_SOURCE_SPATIALIZE_SOFT: 0x%04x\n",
1531             values[0]);
1532         return false;
1533 
1534     case AL_AUXILIARY_SEND_FILTER:
1535         CHECKSIZE(values, 3);
1536         slotlock = std::unique_lock<std::mutex>{Context->mEffectSlotLock};
1537         if(values[0] && (slot=LookupEffectSlot(Context, static_cast<ALuint>(values[0]))) == nullptr)
1538             SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid effect ID %u", values[0]);
1539         if(static_cast<ALuint>(values[1]) >= device->NumAuxSends)
1540             SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid send %u", values[1]);
1541 
1542         if(values[2])
1543         {
1544             std::lock_guard<std::mutex> _{device->FilterLock};
1545             ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[2]))};
1546             if(!filter)
1547                 SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid filter ID %u", values[2]);
1548 
1549             auto &send = Source->Send[static_cast<ALuint>(values[1])];
1550             send.Gain = filter->Gain;
1551             send.GainHF = filter->GainHF;
1552             send.HFReference = filter->HFReference;
1553             send.GainLF = filter->GainLF;
1554             send.LFReference = filter->LFReference;
1555         }
1556         else
1557         {
1558             /* Disable filter */
1559             auto &send = Source->Send[static_cast<ALuint>(values[1])];
1560             send.Gain = 1.0f;
1561             send.GainHF = 1.0f;
1562             send.HFReference = LOWPASSFREQREF;
1563             send.GainLF = 1.0f;
1564             send.LFReference = HIGHPASSFREQREF;
1565         }
1566 
1567         if(slot != Source->Send[static_cast<ALuint>(values[1])].Slot && IsPlayingOrPaused(Source))
1568         {
1569             /* Add refcount on the new slot, and release the previous slot */
1570             if(slot) IncrementRef(slot->ref);
1571             if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
1572                 DecrementRef(oldslot->ref);
1573             Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
1574 
1575             /* We must force an update if the auxiliary slot changed on an
1576              * active source, in case the slot is about to be deleted.
1577              */
1578             Voice *voice{GetSourceVoice(Source, Context)};
1579             if(voice) UpdateSourceProps(Source, voice, Context);
1580             else Source->PropsClean.clear(std::memory_order_release);
1581         }
1582         else
1583         {
1584             if(slot) IncrementRef(slot->ref);
1585             if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
1586                 DecrementRef(oldslot->ref);
1587             Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
1588             UpdateSourceProps(Source, Context);
1589         }
1590         return true;
1591 
1592 
1593     /* 1x float */
1594     case AL_CONE_INNER_ANGLE:
1595     case AL_CONE_OUTER_ANGLE:
1596     case AL_PITCH:
1597     case AL_GAIN:
1598     case AL_MIN_GAIN:
1599     case AL_MAX_GAIN:
1600     case AL_REFERENCE_DISTANCE:
1601     case AL_ROLLOFF_FACTOR:
1602     case AL_CONE_OUTER_GAIN:
1603     case AL_MAX_DISTANCE:
1604     case AL_DOPPLER_FACTOR:
1605     case AL_CONE_OUTER_GAINHF:
1606     case AL_AIR_ABSORPTION_FACTOR:
1607     case AL_ROOM_ROLLOFF_FACTOR:
1608     case AL_SOURCE_RADIUS:
1609         CHECKSIZE(values, 1);
1610         fvals[0] = static_cast<float>(values[0]);
1611         return SetSourcefv(Source, Context, prop, {fvals, 1u});
1612 
1613     /* 3x float */
1614     case AL_POSITION:
1615     case AL_VELOCITY:
1616     case AL_DIRECTION:
1617         CHECKSIZE(values, 3);
1618         fvals[0] = static_cast<float>(values[0]);
1619         fvals[1] = static_cast<float>(values[1]);
1620         fvals[2] = static_cast<float>(values[2]);
1621         return SetSourcefv(Source, Context, prop, {fvals, 3u});
1622 
1623     /* 6x float */
1624     case AL_ORIENTATION:
1625         CHECKSIZE(values, 6);
1626         fvals[0] = static_cast<float>(values[0]);
1627         fvals[1] = static_cast<float>(values[1]);
1628         fvals[2] = static_cast<float>(values[2]);
1629         fvals[3] = static_cast<float>(values[3]);
1630         fvals[4] = static_cast<float>(values[4]);
1631         fvals[5] = static_cast<float>(values[5]);
1632         return SetSourcefv(Source, Context, prop, {fvals, 6u});
1633 
1634     case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1635     case AL_SEC_OFFSET_LATENCY_SOFT:
1636     case AL_SEC_OFFSET_CLOCK_SOFT:
1637     case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1638     case AL_STEREO_ANGLES:
1639         break;
1640     }
1641 
1642     ERR("Unexpected property: 0x%04x\n", prop);
1643     Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
1644     return false;
1645 }
1646 
SetSourcei64v(ALsource * Source,ALCcontext * Context,SourceProp prop,const al::span<const int64_t> values)1647 bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int64_t> values)
1648 {
1649     float fvals[MaxValues];
1650     int   ivals[MaxValues];
1651 
1652     switch(prop)
1653     {
1654     case AL_SOURCE_TYPE:
1655     case AL_BUFFERS_QUEUED:
1656     case AL_BUFFERS_PROCESSED:
1657     case AL_SOURCE_STATE:
1658     case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1659     case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1660         /* Query only */
1661         SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1662             "Setting read-only source property 0x%04x", prop);
1663 
1664     /* 1x int */
1665     case AL_SOURCE_RELATIVE:
1666     case AL_LOOPING:
1667     case AL_SEC_OFFSET:
1668     case AL_SAMPLE_OFFSET:
1669     case AL_BYTE_OFFSET:
1670     case AL_DIRECT_FILTER_GAINHF_AUTO:
1671     case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1672     case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1673     case AL_DIRECT_CHANNELS_SOFT:
1674     case AL_DISTANCE_MODEL:
1675     case AL_SOURCE_RESAMPLER_SOFT:
1676     case AL_SOURCE_SPATIALIZE_SOFT:
1677         CHECKSIZE(values, 1);
1678         CHECKVAL(values[0] <= INT_MAX && values[0] >= INT_MIN);
1679 
1680         ivals[0] = static_cast<int>(values[0]);
1681         return SetSourceiv(Source, Context, prop, {ivals, 1u});
1682 
1683     /* 1x uint */
1684     case AL_BUFFER:
1685     case AL_DIRECT_FILTER:
1686         CHECKSIZE(values, 1);
1687         CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0);
1688 
1689         ivals[0] = static_cast<int>(values[0]);
1690         return SetSourceiv(Source, Context, prop, {ivals, 1u});
1691 
1692     /* 3x uint */
1693     case AL_AUXILIARY_SEND_FILTER:
1694         CHECKSIZE(values, 3);
1695         CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 && values[1] <= UINT_MAX && values[1] >= 0
1696             && values[2] <= UINT_MAX && values[2] >= 0);
1697 
1698         ivals[0] = static_cast<int>(values[0]);
1699         ivals[1] = static_cast<int>(values[1]);
1700         ivals[2] = static_cast<int>(values[2]);
1701         return SetSourceiv(Source, Context, prop, {ivals, 3u});
1702 
1703     /* 1x float */
1704     case AL_CONE_INNER_ANGLE:
1705     case AL_CONE_OUTER_ANGLE:
1706     case AL_PITCH:
1707     case AL_GAIN:
1708     case AL_MIN_GAIN:
1709     case AL_MAX_GAIN:
1710     case AL_REFERENCE_DISTANCE:
1711     case AL_ROLLOFF_FACTOR:
1712     case AL_CONE_OUTER_GAIN:
1713     case AL_MAX_DISTANCE:
1714     case AL_DOPPLER_FACTOR:
1715     case AL_CONE_OUTER_GAINHF:
1716     case AL_AIR_ABSORPTION_FACTOR:
1717     case AL_ROOM_ROLLOFF_FACTOR:
1718     case AL_SOURCE_RADIUS:
1719         CHECKSIZE(values, 1);
1720         fvals[0] = static_cast<float>(values[0]);
1721         return SetSourcefv(Source, Context, prop, {fvals, 1u});
1722 
1723     /* 3x float */
1724     case AL_POSITION:
1725     case AL_VELOCITY:
1726     case AL_DIRECTION:
1727         CHECKSIZE(values, 3);
1728         fvals[0] = static_cast<float>(values[0]);
1729         fvals[1] = static_cast<float>(values[1]);
1730         fvals[2] = static_cast<float>(values[2]);
1731         return SetSourcefv(Source, Context, prop, {fvals, 3u});
1732 
1733     /* 6x float */
1734     case AL_ORIENTATION:
1735         CHECKSIZE(values, 6);
1736         fvals[0] = static_cast<float>(values[0]);
1737         fvals[1] = static_cast<float>(values[1]);
1738         fvals[2] = static_cast<float>(values[2]);
1739         fvals[3] = static_cast<float>(values[3]);
1740         fvals[4] = static_cast<float>(values[4]);
1741         fvals[5] = static_cast<float>(values[5]);
1742         return SetSourcefv(Source, Context, prop, {fvals, 6u});
1743 
1744     case AL_SEC_OFFSET_LATENCY_SOFT:
1745     case AL_SEC_OFFSET_CLOCK_SOFT:
1746     case AL_STEREO_ANGLES:
1747         break;
1748     }
1749 
1750     ERR("Unexpected property: 0x%04x\n", prop);
1751     Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
1752     return false;
1753 }
1754 
1755 #undef CHECKVAL
1756 
1757 
1758 bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values);
1759 bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int> values);
1760 bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values);
1761 
GetSourcedv(ALsource * Source,ALCcontext * Context,SourceProp prop,const al::span<double> values)1762 bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values)
1763 {
1764     ALCdevice *device{Context->mDevice.get()};
1765     ClockLatency clocktime;
1766     nanoseconds srcclock;
1767     int ivals[MaxValues];
1768     bool err;
1769 
1770     switch(prop)
1771     {
1772     case AL_GAIN:
1773         CHECKSIZE(values, 1);
1774         values[0] = Source->Gain;
1775         return true;
1776 
1777     case AL_PITCH:
1778         CHECKSIZE(values, 1);
1779         values[0] = Source->Pitch;
1780         return true;
1781 
1782     case AL_MAX_DISTANCE:
1783         CHECKSIZE(values, 1);
1784         values[0] = Source->MaxDistance;
1785         return true;
1786 
1787     case AL_ROLLOFF_FACTOR:
1788         CHECKSIZE(values, 1);
1789         values[0] = Source->RolloffFactor;
1790         return true;
1791 
1792     case AL_REFERENCE_DISTANCE:
1793         CHECKSIZE(values, 1);
1794         values[0] = Source->RefDistance;
1795         return true;
1796 
1797     case AL_CONE_INNER_ANGLE:
1798         CHECKSIZE(values, 1);
1799         values[0] = Source->InnerAngle;
1800         return true;
1801 
1802     case AL_CONE_OUTER_ANGLE:
1803         CHECKSIZE(values, 1);
1804         values[0] = Source->OuterAngle;
1805         return true;
1806 
1807     case AL_MIN_GAIN:
1808         CHECKSIZE(values, 1);
1809         values[0] = Source->MinGain;
1810         return true;
1811 
1812     case AL_MAX_GAIN:
1813         CHECKSIZE(values, 1);
1814         values[0] = Source->MaxGain;
1815         return true;
1816 
1817     case AL_CONE_OUTER_GAIN:
1818         CHECKSIZE(values, 1);
1819         values[0] = Source->OuterGain;
1820         return true;
1821 
1822     case AL_SEC_OFFSET:
1823     case AL_SAMPLE_OFFSET:
1824     case AL_BYTE_OFFSET:
1825         CHECKSIZE(values, 1);
1826         values[0] = GetSourceOffset(Source, prop, Context);
1827         return true;
1828 
1829     case AL_CONE_OUTER_GAINHF:
1830         CHECKSIZE(values, 1);
1831         values[0] = Source->OuterGainHF;
1832         return true;
1833 
1834     case AL_AIR_ABSORPTION_FACTOR:
1835         CHECKSIZE(values, 1);
1836         values[0] = Source->AirAbsorptionFactor;
1837         return true;
1838 
1839     case AL_ROOM_ROLLOFF_FACTOR:
1840         CHECKSIZE(values, 1);
1841         values[0] = Source->RoomRolloffFactor;
1842         return true;
1843 
1844     case AL_DOPPLER_FACTOR:
1845         CHECKSIZE(values, 1);
1846         values[0] = Source->DopplerFactor;
1847         return true;
1848 
1849     case AL_SOURCE_RADIUS:
1850         CHECKSIZE(values, 1);
1851         values[0] = Source->Radius;
1852         return true;
1853 
1854     case AL_STEREO_ANGLES:
1855         CHECKSIZE(values, 2);
1856         values[0] = Source->StereoPan[0];
1857         values[1] = Source->StereoPan[1];
1858         return true;
1859 
1860     case AL_SEC_OFFSET_LATENCY_SOFT:
1861         CHECKSIZE(values, 2);
1862         /* Get the source offset with the clock time first. Then get the clock
1863          * time with the device latency. Order is important.
1864          */
1865         values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1866         {
1867             std::lock_guard<std::mutex> _{device->StateLock};
1868             clocktime = GetClockLatency(device);
1869         }
1870         if(srcclock == clocktime.ClockTime)
1871             values[1] = static_cast<double>(clocktime.Latency.count()) / 1000000000.0;
1872         else
1873         {
1874             /* If the clock time incremented, reduce the latency by that much
1875              * since it's that much closer to the source offset it got earlier.
1876              */
1877             const nanoseconds diff{clocktime.ClockTime - srcclock};
1878             const nanoseconds latency{clocktime.Latency - std::min(clocktime.Latency, diff)};
1879             values[1] = static_cast<double>(latency.count()) / 1000000000.0;
1880         }
1881         return true;
1882 
1883     case AL_SEC_OFFSET_CLOCK_SOFT:
1884         CHECKSIZE(values, 2);
1885         values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1886         values[1] = static_cast<double>(srcclock.count()) / 1000000000.0;
1887         return true;
1888 
1889     case AL_POSITION:
1890         CHECKSIZE(values, 3);
1891         values[0] = Source->Position[0];
1892         values[1] = Source->Position[1];
1893         values[2] = Source->Position[2];
1894         return true;
1895 
1896     case AL_VELOCITY:
1897         CHECKSIZE(values, 3);
1898         values[0] = Source->Velocity[0];
1899         values[1] = Source->Velocity[1];
1900         values[2] = Source->Velocity[2];
1901         return true;
1902 
1903     case AL_DIRECTION:
1904         CHECKSIZE(values, 3);
1905         values[0] = Source->Direction[0];
1906         values[1] = Source->Direction[1];
1907         values[2] = Source->Direction[2];
1908         return true;
1909 
1910     case AL_ORIENTATION:
1911         CHECKSIZE(values, 6);
1912         values[0] = Source->OrientAt[0];
1913         values[1] = Source->OrientAt[1];
1914         values[2] = Source->OrientAt[2];
1915         values[3] = Source->OrientUp[0];
1916         values[4] = Source->OrientUp[1];
1917         values[5] = Source->OrientUp[2];
1918         return true;
1919 
1920     /* 1x int */
1921     case AL_SOURCE_RELATIVE:
1922     case AL_LOOPING:
1923     case AL_SOURCE_STATE:
1924     case AL_BUFFERS_QUEUED:
1925     case AL_BUFFERS_PROCESSED:
1926     case AL_SOURCE_TYPE:
1927     case AL_DIRECT_FILTER_GAINHF_AUTO:
1928     case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1929     case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1930     case AL_DIRECT_CHANNELS_SOFT:
1931     case AL_DISTANCE_MODEL:
1932     case AL_SOURCE_RESAMPLER_SOFT:
1933     case AL_SOURCE_SPATIALIZE_SOFT:
1934         CHECKSIZE(values, 1);
1935         if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
1936             values[0] = static_cast<double>(ivals[0]);
1937         return err;
1938 
1939     case AL_BUFFER:
1940     case AL_DIRECT_FILTER:
1941     case AL_AUXILIARY_SEND_FILTER:
1942     case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1943     case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1944         break;
1945     }
1946 
1947     ERR("Unexpected property: 0x%04x\n", prop);
1948     Context->setError(AL_INVALID_ENUM, "Invalid source double property 0x%04x", prop);
1949     return false;
1950 }
1951 
GetSourceiv(ALsource * Source,ALCcontext * Context,SourceProp prop,const al::span<int> values)1952 bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int> values)
1953 {
1954     double dvals[MaxValues];
1955     bool err;
1956 
1957     switch(prop)
1958     {
1959     case AL_SOURCE_RELATIVE:
1960         CHECKSIZE(values, 1);
1961         values[0] = Source->HeadRelative;
1962         return true;
1963 
1964     case AL_LOOPING:
1965         CHECKSIZE(values, 1);
1966         values[0] = Source->Looping;
1967         return true;
1968 
1969     case AL_BUFFER:
1970         CHECKSIZE(values, 1);
1971         {
1972             ALbufferQueueItem *BufferList{(Source->SourceType == AL_STATIC)
1973                 ? &Source->mQueue.front() : nullptr};
1974             ALbuffer *buffer{BufferList ? BufferList->mBuffer : nullptr};
1975             values[0] = buffer ? static_cast<int>(buffer->id) : 0;
1976         }
1977         return true;
1978 
1979     case AL_SOURCE_STATE:
1980         CHECKSIZE(values, 1);
1981         values[0] = GetSourceState(Source, GetSourceVoice(Source, Context));
1982         return true;
1983 
1984     case AL_BUFFERS_QUEUED:
1985         CHECKSIZE(values, 1);
1986         values[0] = static_cast<int>(Source->mQueue.size());
1987         return true;
1988 
1989     case AL_BUFFERS_PROCESSED:
1990         CHECKSIZE(values, 1);
1991         if(Source->Looping || Source->SourceType != AL_STREAMING)
1992         {
1993             /* Buffers on a looping source are in a perpetual state of PENDING,
1994              * so don't report any as PROCESSED
1995              */
1996             values[0] = 0;
1997         }
1998         else
1999         {
2000             int played{0};
2001             if(Source->state != AL_INITIAL)
2002             {
2003                 const VoiceBufferItem *Current{nullptr};
2004                 if(Voice *voice{GetSourceVoice(Source, Context)})
2005                     Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
2006                 for(auto &item : Source->mQueue)
2007                 {
2008                     if(&item == Current)
2009                         break;
2010                     ++played;
2011                 }
2012             }
2013             values[0] = played;
2014         }
2015         return true;
2016 
2017     case AL_SOURCE_TYPE:
2018         CHECKSIZE(values, 1);
2019         values[0] = Source->SourceType;
2020         return true;
2021 
2022     case AL_DIRECT_FILTER_GAINHF_AUTO:
2023         CHECKSIZE(values, 1);
2024         values[0] = Source->DryGainHFAuto;
2025         return true;
2026 
2027     case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2028         CHECKSIZE(values, 1);
2029         values[0] = Source->WetGainAuto;
2030         return true;
2031 
2032     case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2033         CHECKSIZE(values, 1);
2034         values[0] = Source->WetGainHFAuto;
2035         return true;
2036 
2037     case AL_DIRECT_CHANNELS_SOFT:
2038         CHECKSIZE(values, 1);
2039         values[0] = EnumFromDirectMode(Source->DirectChannels);
2040         return true;
2041 
2042     case AL_DISTANCE_MODEL:
2043         CHECKSIZE(values, 1);
2044         values[0] = ALenumFromDistanceModel(Source->mDistanceModel);
2045         return true;
2046 
2047     case AL_SOURCE_RESAMPLER_SOFT:
2048         CHECKSIZE(values, 1);
2049         values[0] = static_cast<int>(Source->mResampler);
2050         return true;
2051 
2052     case AL_SOURCE_SPATIALIZE_SOFT:
2053         CHECKSIZE(values, 1);
2054         values[0] = EnumFromSpatializeMode(Source->mSpatialize);
2055         return true;
2056 
2057     /* 1x float/double */
2058     case AL_CONE_INNER_ANGLE:
2059     case AL_CONE_OUTER_ANGLE:
2060     case AL_PITCH:
2061     case AL_GAIN:
2062     case AL_MIN_GAIN:
2063     case AL_MAX_GAIN:
2064     case AL_REFERENCE_DISTANCE:
2065     case AL_ROLLOFF_FACTOR:
2066     case AL_CONE_OUTER_GAIN:
2067     case AL_MAX_DISTANCE:
2068     case AL_SEC_OFFSET:
2069     case AL_SAMPLE_OFFSET:
2070     case AL_BYTE_OFFSET:
2071     case AL_DOPPLER_FACTOR:
2072     case AL_AIR_ABSORPTION_FACTOR:
2073     case AL_ROOM_ROLLOFF_FACTOR:
2074     case AL_CONE_OUTER_GAINHF:
2075     case AL_SOURCE_RADIUS:
2076         CHECKSIZE(values, 1);
2077         if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
2078             values[0] = static_cast<int>(dvals[0]);
2079         return err;
2080 
2081     /* 3x float/double */
2082     case AL_POSITION:
2083     case AL_VELOCITY:
2084     case AL_DIRECTION:
2085         CHECKSIZE(values, 3);
2086         if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
2087         {
2088             values[0] = static_cast<int>(dvals[0]);
2089             values[1] = static_cast<int>(dvals[1]);
2090             values[2] = static_cast<int>(dvals[2]);
2091         }
2092         return err;
2093 
2094     /* 6x float/double */
2095     case AL_ORIENTATION:
2096         CHECKSIZE(values, 6);
2097         if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
2098         {
2099             values[0] = static_cast<int>(dvals[0]);
2100             values[1] = static_cast<int>(dvals[1]);
2101             values[2] = static_cast<int>(dvals[2]);
2102             values[3] = static_cast<int>(dvals[3]);
2103             values[4] = static_cast<int>(dvals[4]);
2104             values[5] = static_cast<int>(dvals[5]);
2105         }
2106         return err;
2107 
2108     case AL_SAMPLE_OFFSET_LATENCY_SOFT:
2109     case AL_SAMPLE_OFFSET_CLOCK_SOFT:
2110         break; /* i64 only */
2111     case AL_SEC_OFFSET_LATENCY_SOFT:
2112     case AL_SEC_OFFSET_CLOCK_SOFT:
2113         break; /* Double only */
2114     case AL_STEREO_ANGLES:
2115         break; /* Float/double only */
2116 
2117     case AL_DIRECT_FILTER:
2118     case AL_AUXILIARY_SEND_FILTER:
2119         break; /* ??? */
2120     }
2121 
2122     ERR("Unexpected property: 0x%04x\n", prop);
2123     Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
2124     return false;
2125 }
2126 
GetSourcei64v(ALsource * Source,ALCcontext * Context,SourceProp prop,const al::span<int64_t> values)2127 bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values)
2128 {
2129     ALCdevice *device = Context->mDevice.get();
2130     ClockLatency clocktime;
2131     nanoseconds srcclock;
2132     double dvals[MaxValues];
2133     int ivals[MaxValues];
2134     bool err;
2135 
2136     switch(prop)
2137     {
2138     case AL_SAMPLE_OFFSET_LATENCY_SOFT:
2139         CHECKSIZE(values, 2);
2140         /* Get the source offset with the clock time first. Then get the clock
2141          * time with the device latency. Order is important.
2142          */
2143         values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
2144         {
2145             std::lock_guard<std::mutex> _{device->StateLock};
2146             clocktime = GetClockLatency(device);
2147         }
2148         if(srcclock == clocktime.ClockTime)
2149             values[1] = clocktime.Latency.count();
2150         else
2151         {
2152             /* If the clock time incremented, reduce the latency by that much
2153              * since it's that much closer to the source offset it got earlier.
2154              */
2155             const nanoseconds diff{clocktime.ClockTime - srcclock};
2156             values[1] = nanoseconds{clocktime.Latency - std::min(clocktime.Latency, diff)}.count();
2157         }
2158         return true;
2159 
2160     case AL_SAMPLE_OFFSET_CLOCK_SOFT:
2161         CHECKSIZE(values, 2);
2162         values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
2163         values[1] = srcclock.count();
2164         return true;
2165 
2166     /* 1x float/double */
2167     case AL_CONE_INNER_ANGLE:
2168     case AL_CONE_OUTER_ANGLE:
2169     case AL_PITCH:
2170     case AL_GAIN:
2171     case AL_MIN_GAIN:
2172     case AL_MAX_GAIN:
2173     case AL_REFERENCE_DISTANCE:
2174     case AL_ROLLOFF_FACTOR:
2175     case AL_CONE_OUTER_GAIN:
2176     case AL_MAX_DISTANCE:
2177     case AL_SEC_OFFSET:
2178     case AL_SAMPLE_OFFSET:
2179     case AL_BYTE_OFFSET:
2180     case AL_DOPPLER_FACTOR:
2181     case AL_AIR_ABSORPTION_FACTOR:
2182     case AL_ROOM_ROLLOFF_FACTOR:
2183     case AL_CONE_OUTER_GAINHF:
2184     case AL_SOURCE_RADIUS:
2185         CHECKSIZE(values, 1);
2186         if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
2187             values[0] = static_cast<int64_t>(dvals[0]);
2188         return err;
2189 
2190     /* 3x float/double */
2191     case AL_POSITION:
2192     case AL_VELOCITY:
2193     case AL_DIRECTION:
2194         CHECKSIZE(values, 3);
2195         if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
2196         {
2197             values[0] = static_cast<int64_t>(dvals[0]);
2198             values[1] = static_cast<int64_t>(dvals[1]);
2199             values[2] = static_cast<int64_t>(dvals[2]);
2200         }
2201         return err;
2202 
2203     /* 6x float/double */
2204     case AL_ORIENTATION:
2205         CHECKSIZE(values, 6);
2206         if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
2207         {
2208             values[0] = static_cast<int64_t>(dvals[0]);
2209             values[1] = static_cast<int64_t>(dvals[1]);
2210             values[2] = static_cast<int64_t>(dvals[2]);
2211             values[3] = static_cast<int64_t>(dvals[3]);
2212             values[4] = static_cast<int64_t>(dvals[4]);
2213             values[5] = static_cast<int64_t>(dvals[5]);
2214         }
2215         return err;
2216 
2217     /* 1x int */
2218     case AL_SOURCE_RELATIVE:
2219     case AL_LOOPING:
2220     case AL_SOURCE_STATE:
2221     case AL_BUFFERS_QUEUED:
2222     case AL_BUFFERS_PROCESSED:
2223     case AL_SOURCE_TYPE:
2224     case AL_DIRECT_FILTER_GAINHF_AUTO:
2225     case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2226     case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2227     case AL_DIRECT_CHANNELS_SOFT:
2228     case AL_DISTANCE_MODEL:
2229     case AL_SOURCE_RESAMPLER_SOFT:
2230     case AL_SOURCE_SPATIALIZE_SOFT:
2231         CHECKSIZE(values, 1);
2232         if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2233             values[0] = ivals[0];
2234         return err;
2235 
2236     /* 1x uint */
2237     case AL_BUFFER:
2238     case AL_DIRECT_FILTER:
2239         CHECKSIZE(values, 1);
2240         if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2241             values[0] = static_cast<ALuint>(ivals[0]);
2242         return err;
2243 
2244     /* 3x uint */
2245     case AL_AUXILIARY_SEND_FILTER:
2246         CHECKSIZE(values, 3);
2247         if((err=GetSourceiv(Source, Context, prop, {ivals, 3u})) != false)
2248         {
2249             values[0] = static_cast<ALuint>(ivals[0]);
2250             values[1] = static_cast<ALuint>(ivals[1]);
2251             values[2] = static_cast<ALuint>(ivals[2]);
2252         }
2253         return err;
2254 
2255     case AL_SEC_OFFSET_LATENCY_SOFT:
2256     case AL_SEC_OFFSET_CLOCK_SOFT:
2257         break; /* Double only */
2258     case AL_STEREO_ANGLES:
2259         break; /* Float/double only */
2260     }
2261 
2262     ERR("Unexpected property: 0x%04x\n", prop);
2263     Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
2264     return false;
2265 }
2266 
2267 } // namespace
2268 
alGenSources(ALsizei n,ALuint * sources)2269 AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
2270 START_API_FUNC
2271 {
2272     ContextRef context{GetContextRef()};
2273     if UNLIKELY(!context) return;
2274 
2275     if UNLIKELY(n < 0)
2276         context->setError(AL_INVALID_VALUE, "Generating %d sources", n);
2277     if UNLIKELY(n <= 0) return;
2278 
2279     std::unique_lock<std::mutex> srclock{context->mSourceLock};
2280     ALCdevice *device{context->mDevice.get()};
2281     if(static_cast<ALuint>(n) > device->SourcesMax-context->mNumSources)
2282     {
2283         context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)",
2284             device->SourcesMax, context->mNumSources, n);
2285         return;
2286     }
2287     if(!EnsureSources(context.get(), static_cast<ALuint>(n)))
2288     {
2289         context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, (n==1)?"":"s");
2290         return;
2291     }
2292 
2293     if(n == 1)
2294     {
2295         ALsource *source{AllocSource(context.get())};
2296         sources[0] = source->id;
2297     }
2298     else
2299     {
2300         al::vector<ALuint> ids;
2301         ids.reserve(static_cast<ALuint>(n));
2302         do {
2303             ALsource *source{AllocSource(context.get())};
2304             ids.emplace_back(source->id);
2305         } while(--n);
2306         std::copy(ids.cbegin(), ids.cend(), sources);
2307     }
2308 }
2309 END_API_FUNC
2310 
alDeleteSources(ALsizei n,const ALuint * sources)2311 AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
2312 START_API_FUNC
2313 {
2314     ContextRef context{GetContextRef()};
2315     if UNLIKELY(!context) return;
2316 
2317     if UNLIKELY(n < 0)
2318         SETERR_RETURN(context, AL_INVALID_VALUE,, "Deleting %d sources", n);
2319 
2320     std::lock_guard<std::mutex> _{context->mSourceLock};
2321 
2322     /* Check that all Sources are valid */
2323     auto validate_source = [&context](const ALuint sid) -> bool
2324     { return LookupSource(context.get(), sid) != nullptr; };
2325 
2326     const ALuint *sources_end = sources + n;
2327     auto invsrc = std::find_if_not(sources, sources_end, validate_source);
2328     if UNLIKELY(invsrc != sources_end)
2329     {
2330         context->setError(AL_INVALID_NAME, "Invalid source ID %u", *invsrc);
2331         return;
2332     }
2333 
2334     /* All good. Delete source IDs. */
2335     auto delete_source = [&context](const ALuint sid) -> void
2336     {
2337         ALsource *src{LookupSource(context.get(), sid)};
2338         if(src) FreeSource(context.get(), src);
2339     };
2340     std::for_each(sources, sources_end, delete_source);
2341 }
2342 END_API_FUNC
2343 
alIsSource(ALuint source)2344 AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
2345 START_API_FUNC
2346 {
2347     ContextRef context{GetContextRef()};
2348     if LIKELY(context)
2349     {
2350         std::lock_guard<std::mutex> _{context->mSourceLock};
2351         if(LookupSource(context.get(), source) != nullptr)
2352             return AL_TRUE;
2353     }
2354     return AL_FALSE;
2355 }
2356 END_API_FUNC
2357 
2358 
alSourcef(ALuint source,ALenum param,ALfloat value)2359 AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
2360 START_API_FUNC
2361 {
2362     ContextRef context{GetContextRef()};
2363     if UNLIKELY(!context) return;
2364 
2365     std::lock_guard<std::mutex> _{context->mPropLock};
2366     std::lock_guard<std::mutex> __{context->mSourceLock};
2367     ALsource *Source = LookupSource(context.get(), source);
2368     if UNLIKELY(!Source)
2369         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2370     else
2371         SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2372 }
2373 END_API_FUNC
2374 
alSource3f(ALuint source,ALenum param,ALfloat value1,ALfloat value2,ALfloat value3)2375 AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
2376 START_API_FUNC
2377 {
2378     ContextRef context{GetContextRef()};
2379     if UNLIKELY(!context) return;
2380 
2381     std::lock_guard<std::mutex> _{context->mPropLock};
2382     std::lock_guard<std::mutex> __{context->mSourceLock};
2383     ALsource *Source = LookupSource(context.get(), source);
2384     if UNLIKELY(!Source)
2385         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2386     else
2387     {
2388         const float fvals[3]{ value1, value2, value3 };
2389         SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2390     }
2391 }
2392 END_API_FUNC
2393 
alSourcefv(ALuint source,ALenum param,const ALfloat * values)2394 AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
2395 START_API_FUNC
2396 {
2397     ContextRef context{GetContextRef()};
2398     if UNLIKELY(!context) return;
2399 
2400     std::lock_guard<std::mutex> _{context->mPropLock};
2401     std::lock_guard<std::mutex> __{context->mSourceLock};
2402     ALsource *Source = LookupSource(context.get(), source);
2403     if UNLIKELY(!Source)
2404         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2405     else if UNLIKELY(!values)
2406         context->setError(AL_INVALID_VALUE, "NULL pointer");
2407     else
2408         SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2409 }
2410 END_API_FUNC
2411 
2412 
alSourcedSOFT(ALuint source,ALenum param,ALdouble value)2413 AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
2414 START_API_FUNC
2415 {
2416     ContextRef context{GetContextRef()};
2417     if UNLIKELY(!context) return;
2418 
2419     std::lock_guard<std::mutex> _{context->mPropLock};
2420     std::lock_guard<std::mutex> __{context->mSourceLock};
2421     ALsource *Source = LookupSource(context.get(), source);
2422     if UNLIKELY(!Source)
2423         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2424     else
2425     {
2426         const float fval[1]{static_cast<float>(value)};
2427         SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fval);
2428     }
2429 }
2430 END_API_FUNC
2431 
alSource3dSOFT(ALuint source,ALenum param,ALdouble value1,ALdouble value2,ALdouble value3)2432 AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
2433 START_API_FUNC
2434 {
2435     ContextRef context{GetContextRef()};
2436     if UNLIKELY(!context) return;
2437 
2438     std::lock_guard<std::mutex> _{context->mPropLock};
2439     std::lock_guard<std::mutex> __{context->mSourceLock};
2440     ALsource *Source = LookupSource(context.get(), source);
2441     if UNLIKELY(!Source)
2442         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2443     else
2444     {
2445         const float fvals[3]{static_cast<float>(value1), static_cast<float>(value2),
2446             static_cast<float>(value3)};
2447         SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2448     }
2449 }
2450 END_API_FUNC
2451 
alSourcedvSOFT(ALuint source,ALenum param,const ALdouble * values)2452 AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
2453 START_API_FUNC
2454 {
2455     ContextRef context{GetContextRef()};
2456     if UNLIKELY(!context) return;
2457 
2458     std::lock_guard<std::mutex> _{context->mPropLock};
2459     std::lock_guard<std::mutex> __{context->mSourceLock};
2460     ALsource *Source = LookupSource(context.get(), source);
2461     if UNLIKELY(!Source)
2462         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2463     else if UNLIKELY(!values)
2464         context->setError(AL_INVALID_VALUE, "NULL pointer");
2465     else
2466     {
2467         const ALuint count{DoubleValsByProp(param)};
2468         float fvals[MaxValues];
2469         for(ALuint i{0};i < count;i++)
2470             fvals[i] = static_cast<float>(values[i]);
2471         SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {fvals, count});
2472     }
2473 }
2474 END_API_FUNC
2475 
2476 
alSourcei(ALuint source,ALenum param,ALint value)2477 AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
2478 START_API_FUNC
2479 {
2480     ContextRef context{GetContextRef()};
2481     if UNLIKELY(!context) return;
2482 
2483     std::lock_guard<std::mutex> _{context->mPropLock};
2484     std::lock_guard<std::mutex> __{context->mSourceLock};
2485     ALsource *Source = LookupSource(context.get(), source);
2486     if UNLIKELY(!Source)
2487         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2488     else
2489         SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2490 }
2491 END_API_FUNC
2492 
alSource3i(ALuint source,ALenum param,ALint value1,ALint value2,ALint value3)2493 AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
2494 START_API_FUNC
2495 {
2496     ContextRef context{GetContextRef()};
2497     if UNLIKELY(!context) return;
2498 
2499     std::lock_guard<std::mutex> _{context->mPropLock};
2500     std::lock_guard<std::mutex> __{context->mSourceLock};
2501     ALsource *Source = LookupSource(context.get(), source);
2502     if UNLIKELY(!Source)
2503         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2504     else
2505     {
2506         const int ivals[3]{ value1, value2, value3 };
2507         SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals);
2508     }
2509 }
2510 END_API_FUNC
2511 
alSourceiv(ALuint source,ALenum param,const ALint * values)2512 AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
2513 START_API_FUNC
2514 {
2515     ContextRef context{GetContextRef()};
2516     if UNLIKELY(!context) return;
2517 
2518     std::lock_guard<std::mutex> _{context->mPropLock};
2519     std::lock_guard<std::mutex> __{context->mSourceLock};
2520     ALsource *Source = LookupSource(context.get(), source);
2521     if UNLIKELY(!Source)
2522         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2523     else if UNLIKELY(!values)
2524         context->setError(AL_INVALID_VALUE, "NULL pointer");
2525     else
2526         SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2527 }
2528 END_API_FUNC
2529 
2530 
alSourcei64SOFT(ALuint source,ALenum param,ALint64SOFT value)2531 AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
2532 START_API_FUNC
2533 {
2534     ContextRef context{GetContextRef()};
2535     if UNLIKELY(!context) return;
2536 
2537     std::lock_guard<std::mutex> _{context->mPropLock};
2538     std::lock_guard<std::mutex> __{context->mSourceLock};
2539     ALsource *Source{LookupSource(context.get(), source)};
2540     if UNLIKELY(!Source)
2541         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2542     else
2543         SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2544 }
2545 END_API_FUNC
2546 
alSource3i64SOFT(ALuint source,ALenum param,ALint64SOFT value1,ALint64SOFT value2,ALint64SOFT value3)2547 AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
2548 START_API_FUNC
2549 {
2550     ContextRef context{GetContextRef()};
2551     if UNLIKELY(!context) return;
2552 
2553     std::lock_guard<std::mutex> _{context->mPropLock};
2554     std::lock_guard<std::mutex> __{context->mSourceLock};
2555     ALsource *Source{LookupSource(context.get(), source)};
2556     if UNLIKELY(!Source)
2557         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2558     else
2559     {
2560         const int64_t i64vals[3]{ value1, value2, value3 };
2561         SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals);
2562     }
2563 }
2564 END_API_FUNC
2565 
alSourcei64vSOFT(ALuint source,ALenum param,const ALint64SOFT * values)2566 AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
2567 START_API_FUNC
2568 {
2569     ContextRef context{GetContextRef()};
2570     if UNLIKELY(!context) return;
2571 
2572     std::lock_guard<std::mutex> _{context->mPropLock};
2573     std::lock_guard<std::mutex> __{context->mSourceLock};
2574     ALsource *Source{LookupSource(context.get(), source)};
2575     if UNLIKELY(!Source)
2576         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2577     else if UNLIKELY(!values)
2578         context->setError(AL_INVALID_VALUE, "NULL pointer");
2579     else
2580         SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2581 }
2582 END_API_FUNC
2583 
2584 
alGetSourcef(ALuint source,ALenum param,ALfloat * value)2585 AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
2586 START_API_FUNC
2587 {
2588     ContextRef context{GetContextRef()};
2589     if UNLIKELY(!context) return;
2590 
2591     std::lock_guard<std::mutex> _{context->mSourceLock};
2592     ALsource *Source{LookupSource(context.get(), source)};
2593     if UNLIKELY(!Source)
2594         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2595     else if UNLIKELY(!value)
2596         context->setError(AL_INVALID_VALUE, "NULL pointer");
2597     else
2598     {
2599         double dval[1];
2600         if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dval))
2601             *value = static_cast<float>(dval[0]);
2602     }
2603 }
2604 END_API_FUNC
2605 
alGetSource3f(ALuint source,ALenum param,ALfloat * value1,ALfloat * value2,ALfloat * value3)2606 AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
2607 START_API_FUNC
2608 {
2609     ContextRef context{GetContextRef()};
2610     if UNLIKELY(!context) return;
2611 
2612     std::lock_guard<std::mutex> _{context->mSourceLock};
2613     ALsource *Source{LookupSource(context.get(), source)};
2614     if UNLIKELY(!Source)
2615         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2616     else if UNLIKELY(!(value1 && value2 && value3))
2617         context->setError(AL_INVALID_VALUE, "NULL pointer");
2618     else
2619     {
2620         double dvals[3];
2621         if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2622         {
2623             *value1 = static_cast<float>(dvals[0]);
2624             *value2 = static_cast<float>(dvals[1]);
2625             *value3 = static_cast<float>(dvals[2]);
2626         }
2627     }
2628 }
2629 END_API_FUNC
2630 
alGetSourcefv(ALuint source,ALenum param,ALfloat * values)2631 AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
2632 START_API_FUNC
2633 {
2634     ContextRef context{GetContextRef()};
2635     if UNLIKELY(!context) return;
2636 
2637     std::lock_guard<std::mutex> _{context->mSourceLock};
2638     ALsource *Source{LookupSource(context.get(), source)};
2639     if UNLIKELY(!Source)
2640         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2641     else if UNLIKELY(!values)
2642         context->setError(AL_INVALID_VALUE, "NULL pointer");
2643     else
2644     {
2645         const ALuint count{FloatValsByProp(param)};
2646         double dvals[MaxValues];
2647         if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {dvals, count}))
2648         {
2649             for(ALuint i{0};i < count;i++)
2650                 values[i] = static_cast<float>(dvals[i]);
2651         }
2652     }
2653 }
2654 END_API_FUNC
2655 
2656 
alGetSourcedSOFT(ALuint source,ALenum param,ALdouble * value)2657 AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
2658 START_API_FUNC
2659 {
2660     ContextRef context{GetContextRef()};
2661     if UNLIKELY(!context) return;
2662 
2663     std::lock_guard<std::mutex> _{context->mSourceLock};
2664     ALsource *Source{LookupSource(context.get(), source)};
2665     if UNLIKELY(!Source)
2666         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2667     else if UNLIKELY(!value)
2668         context->setError(AL_INVALID_VALUE, "NULL pointer");
2669     else
2670         GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2671 }
2672 END_API_FUNC
2673 
alGetSource3dSOFT(ALuint source,ALenum param,ALdouble * value1,ALdouble * value2,ALdouble * value3)2674 AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
2675 START_API_FUNC
2676 {
2677     ContextRef context{GetContextRef()};
2678     if UNLIKELY(!context) return;
2679 
2680     std::lock_guard<std::mutex> _{context->mSourceLock};
2681     ALsource *Source{LookupSource(context.get(), source)};
2682     if UNLIKELY(!Source)
2683         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2684     else if UNLIKELY(!(value1 && value2 && value3))
2685         context->setError(AL_INVALID_VALUE, "NULL pointer");
2686     else
2687     {
2688         double dvals[3];
2689         if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2690         {
2691             *value1 = dvals[0];
2692             *value2 = dvals[1];
2693             *value3 = dvals[2];
2694         }
2695     }
2696 }
2697 END_API_FUNC
2698 
alGetSourcedvSOFT(ALuint source,ALenum param,ALdouble * values)2699 AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
2700 START_API_FUNC
2701 {
2702     ContextRef context{GetContextRef()};
2703     if UNLIKELY(!context) return;
2704 
2705     std::lock_guard<std::mutex> _{context->mSourceLock};
2706     ALsource *Source{LookupSource(context.get(), source)};
2707     if UNLIKELY(!Source)
2708         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2709     else if UNLIKELY(!values)
2710         context->setError(AL_INVALID_VALUE, "NULL pointer");
2711     else
2712         GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2713 }
2714 END_API_FUNC
2715 
2716 
alGetSourcei(ALuint source,ALenum param,ALint * value)2717 AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
2718 START_API_FUNC
2719 {
2720     ContextRef context{GetContextRef()};
2721     if UNLIKELY(!context) return;
2722 
2723     std::lock_guard<std::mutex> _{context->mSourceLock};
2724     ALsource *Source{LookupSource(context.get(), source)};
2725     if UNLIKELY(!Source)
2726         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2727     else if UNLIKELY(!value)
2728         context->setError(AL_INVALID_VALUE, "NULL pointer");
2729     else
2730         GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2731 }
2732 END_API_FUNC
2733 
alGetSource3i(ALuint source,ALenum param,ALint * value1,ALint * value2,ALint * value3)2734 AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
2735 START_API_FUNC
2736 {
2737     ContextRef context{GetContextRef()};
2738     if UNLIKELY(!context) return;
2739 
2740     std::lock_guard<std::mutex> _{context->mSourceLock};
2741     ALsource *Source{LookupSource(context.get(), source)};
2742     if UNLIKELY(!Source)
2743         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2744     else if UNLIKELY(!(value1 && value2 && value3))
2745         context->setError(AL_INVALID_VALUE, "NULL pointer");
2746     else
2747     {
2748         int ivals[3];
2749         if(GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals))
2750         {
2751             *value1 = ivals[0];
2752             *value2 = ivals[1];
2753             *value3 = ivals[2];
2754         }
2755     }
2756 }
2757 END_API_FUNC
2758 
alGetSourceiv(ALuint source,ALenum param,ALint * values)2759 AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
2760 START_API_FUNC
2761 {
2762     ContextRef context{GetContextRef()};
2763     if UNLIKELY(!context) return;
2764 
2765     std::lock_guard<std::mutex> _{context->mSourceLock};
2766     ALsource *Source{LookupSource(context.get(), source)};
2767     if UNLIKELY(!Source)
2768         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2769     else if UNLIKELY(!values)
2770         context->setError(AL_INVALID_VALUE, "NULL pointer");
2771     else
2772         GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2773 }
2774 END_API_FUNC
2775 
2776 
alGetSourcei64SOFT(ALuint source,ALenum param,ALint64SOFT * value)2777 AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
2778 START_API_FUNC
2779 {
2780     ContextRef context{GetContextRef()};
2781     if UNLIKELY(!context) return;
2782 
2783     std::lock_guard<std::mutex> _{context->mSourceLock};
2784     ALsource *Source{LookupSource(context.get(), source)};
2785     if UNLIKELY(!Source)
2786         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2787     else if UNLIKELY(!value)
2788         context->setError(AL_INVALID_VALUE, "NULL pointer");
2789     else
2790         GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2791 }
2792 END_API_FUNC
2793 
alGetSource3i64SOFT(ALuint source,ALenum param,ALint64SOFT * value1,ALint64SOFT * value2,ALint64SOFT * value3)2794 AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
2795 START_API_FUNC
2796 {
2797     ContextRef context{GetContextRef()};
2798     if UNLIKELY(!context) return;
2799 
2800     std::lock_guard<std::mutex> _{context->mSourceLock};
2801     ALsource *Source{LookupSource(context.get(), source)};
2802     if UNLIKELY(!Source)
2803         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2804     else if UNLIKELY(!(value1 && value2 && value3))
2805         context->setError(AL_INVALID_VALUE, "NULL pointer");
2806     else
2807     {
2808         int64_t i64vals[3];
2809         if(GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals))
2810         {
2811             *value1 = i64vals[0];
2812             *value2 = i64vals[1];
2813             *value3 = i64vals[2];
2814         }
2815     }
2816 }
2817 END_API_FUNC
2818 
alGetSourcei64vSOFT(ALuint source,ALenum param,ALint64SOFT * values)2819 AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
2820 START_API_FUNC
2821 {
2822     ContextRef context{GetContextRef()};
2823     if UNLIKELY(!context) return;
2824 
2825     std::lock_guard<std::mutex> _{context->mSourceLock};
2826     ALsource *Source{LookupSource(context.get(), source)};
2827     if UNLIKELY(!Source)
2828         context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2829     else if UNLIKELY(!values)
2830         context->setError(AL_INVALID_VALUE, "NULL pointer");
2831     else
2832         GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2833 }
2834 END_API_FUNC
2835 
2836 
alSourcePlay(ALuint source)2837 AL_API void AL_APIENTRY alSourcePlay(ALuint source)
2838 START_API_FUNC
2839 { alSourcePlayv(1, &source); }
2840 END_API_FUNC
2841 
alSourcePlayv(ALsizei n,const ALuint * sources)2842 AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
2843 START_API_FUNC
2844 {
2845     ContextRef context{GetContextRef()};
2846     if UNLIKELY(!context) return;
2847 
2848     if UNLIKELY(n < 0)
2849         context->setError(AL_INVALID_VALUE, "Playing %d sources", n);
2850     if UNLIKELY(n <= 0) return;
2851 
2852     al::vector<ALsource*> extra_sources;
2853     std::array<ALsource*,8> source_storage;
2854     al::span<ALsource*> srchandles;
2855     if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
2856         srchandles = {source_storage.data(), static_cast<ALuint>(n)};
2857     else
2858     {
2859         extra_sources.resize(static_cast<ALuint>(n));
2860         srchandles = {extra_sources.data(), extra_sources.size()};
2861     }
2862 
2863     std::lock_guard<std::mutex> _{context->mSourceLock};
2864     for(auto &srchdl : srchandles)
2865     {
2866         srchdl = LookupSource(context.get(), *sources);
2867         if(!srchdl)
2868             SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
2869         ++sources;
2870     }
2871 
2872     ALCdevice *device{context->mDevice.get()};
2873     /* If the device is disconnected, go right to stopped. */
2874     if UNLIKELY(!device->Connected.load(std::memory_order_acquire))
2875     {
2876         /* TODO: Send state change event? */
2877         for(ALsource *source : srchandles)
2878         {
2879             source->Offset = 0.0;
2880             source->OffsetType = AL_NONE;
2881             source->state = AL_STOPPED;
2882         }
2883         return;
2884     }
2885 
2886     /* Count the number of reusable voices. */
2887     auto voicelist = context->getVoicesSpan();
2888     size_t free_voices{0};
2889     for(const Voice *voice : voicelist)
2890     {
2891         free_voices += (voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
2892             && voice->mSourceID.load(std::memory_order_relaxed) == 0u
2893             && voice->mPendingChange.load(std::memory_order_relaxed) == false);
2894         if(free_voices == srchandles.size())
2895             break;
2896     }
2897     if UNLIKELY(srchandles.size() != free_voices)
2898     {
2899         const size_t inc_amount{srchandles.size() - free_voices};
2900         auto &allvoices = *context->mVoices.load(std::memory_order_relaxed);
2901         if(inc_amount > allvoices.size() - voicelist.size())
2902         {
2903             /* Increase the number of voices to handle the request. */
2904             context->allocVoices(inc_amount - (allvoices.size() - voicelist.size()));
2905         }
2906         context->mActiveVoiceCount.fetch_add(inc_amount, std::memory_order_release);
2907         voicelist = context->getVoicesSpan();
2908     }
2909 
2910     auto voiceiter = voicelist.begin();
2911     ALuint vidx{0};
2912     VoiceChange *tail{}, *cur{};
2913     for(ALsource *source : srchandles)
2914     {
2915         /* Check that there is a queue containing at least one valid, non zero
2916          * length buffer.
2917          */
2918         auto BufferList = source->mQueue.begin();
2919         for(;BufferList != source->mQueue.end();++BufferList)
2920         {
2921             if(BufferList->mSampleLen != 0 || BufferList->mCallback)
2922                 break;
2923         }
2924 
2925         /* If there's nothing to play, go right to stopped. */
2926         if UNLIKELY(BufferList == source->mQueue.end())
2927         {
2928             /* NOTE: A source without any playable buffers should not have a
2929              * Voice since it shouldn't be in a playing or paused state. So
2930              * there's no need to look up its voice and clear the source.
2931              */
2932             source->Offset = 0.0;
2933             source->OffsetType = AL_NONE;
2934             source->state = AL_STOPPED;
2935             continue;
2936         }
2937 
2938         if(!cur)
2939             cur = tail = GetVoiceChanger(context.get());
2940         else
2941         {
2942             cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
2943             cur = cur->mNext.load(std::memory_order_relaxed);
2944         }
2945         Voice *voice{GetSourceVoice(source, context.get())};
2946         switch(GetSourceState(source, voice))
2947         {
2948         case AL_PAUSED:
2949             /* A source that's paused simply resumes. If there's no voice, it
2950              * was lost from a disconnect, so just start over with a new one.
2951              */
2952             cur->mOldVoice = nullptr;
2953             if(!voice) break;
2954             cur->mVoice = voice;
2955             cur->mSourceID = source->id;
2956             cur->mState = VChangeState::Play;
2957             source->state = AL_PLAYING;
2958             continue;
2959 
2960         case AL_PLAYING:
2961             /* A source that's already playing is restarted from the beginning.
2962              * Stop the current voice and start a new one so it properly cross-
2963              * fades back to the beginning.
2964              */
2965             if(voice)
2966                 voice->mPendingChange.store(true, std::memory_order_relaxed);
2967             cur->mOldVoice = voice;
2968             voice = nullptr;
2969             break;
2970 
2971         default:
2972             assert(voice == nullptr);
2973             cur->mOldVoice = nullptr;
2974             break;
2975         }
2976 
2977         /* Find the next unused voice to play this source with. */
2978         for(;voiceiter != voicelist.end();++voiceiter,++vidx)
2979         {
2980             Voice *v{*voiceiter};
2981             if(v->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
2982                 && v->mSourceID.load(std::memory_order_relaxed) == 0u
2983                 && v->mPendingChange.load(std::memory_order_relaxed) == false)
2984             {
2985                 voice = v;
2986                 break;
2987             }
2988         }
2989 
2990         voice->mPosition.store(0u, std::memory_order_relaxed);
2991         voice->mPositionFrac.store(0, std::memory_order_relaxed);
2992         voice->mCurrentBuffer.store(&source->mQueue.front(), std::memory_order_relaxed);
2993         voice->mFlags = 0;
2994         /* A source that's not playing or paused has any offset applied when it
2995          * starts playing.
2996          */
2997         if(const ALenum offsettype{source->OffsetType})
2998         {
2999             const double offset{source->Offset};
3000             source->OffsetType = AL_NONE;
3001             source->Offset = 0.0;
3002             if(auto vpos = GetSampleOffset(source->mQueue, offsettype, offset))
3003             {
3004                 voice->mPosition.store(vpos->pos, std::memory_order_relaxed);
3005                 voice->mPositionFrac.store(vpos->frac, std::memory_order_relaxed);
3006                 voice->mCurrentBuffer.store(vpos->bufferitem, std::memory_order_relaxed);
3007                 if(vpos->pos!=0 || vpos->frac!=0 || vpos->bufferitem!=&source->mQueue.front())
3008                     voice->mFlags |= VoiceIsFading;
3009             }
3010         }
3011         InitVoice(voice, source, std::addressof(*BufferList), context.get(), device);
3012 
3013         source->VoiceIdx = vidx;
3014         source->state = AL_PLAYING;
3015 
3016         cur->mVoice = voice;
3017         cur->mSourceID = source->id;
3018         cur->mState = VChangeState::Play;
3019     }
3020     if LIKELY(tail)
3021         SendVoiceChanges(context.get(), tail);
3022 }
3023 END_API_FUNC
3024 
3025 
alSourcePause(ALuint source)3026 AL_API void AL_APIENTRY alSourcePause(ALuint source)
3027 START_API_FUNC
3028 { alSourcePausev(1, &source); }
3029 END_API_FUNC
3030 
alSourcePausev(ALsizei n,const ALuint * sources)3031 AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
3032 START_API_FUNC
3033 {
3034     ContextRef context{GetContextRef()};
3035     if UNLIKELY(!context) return;
3036 
3037     if UNLIKELY(n < 0)
3038         context->setError(AL_INVALID_VALUE, "Pausing %d sources", n);
3039     if UNLIKELY(n <= 0) return;
3040 
3041     al::vector<ALsource*> extra_sources;
3042     std::array<ALsource*,8> source_storage;
3043     al::span<ALsource*> srchandles;
3044     if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3045         srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3046     else
3047     {
3048         extra_sources.resize(static_cast<ALuint>(n));
3049         srchandles = {extra_sources.data(), extra_sources.size()};
3050     }
3051 
3052     std::lock_guard<std::mutex> _{context->mSourceLock};
3053     for(auto &srchdl : srchandles)
3054     {
3055         srchdl = LookupSource(context.get(), *sources);
3056         if(!srchdl)
3057             SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3058         ++sources;
3059     }
3060 
3061     /* Pausing has to be done in two steps. First, for each source that's
3062      * detected to be playing, chamge the voice (asynchronously) to
3063      * stopping/paused.
3064      */
3065     VoiceChange *tail{}, *cur{};
3066     for(ALsource *source : srchandles)
3067     {
3068         Voice *voice{GetSourceVoice(source, context.get())};
3069         if(GetSourceState(source, voice) == AL_PLAYING)
3070         {
3071             if(!cur)
3072                 cur = tail = GetVoiceChanger(context.get());
3073             else
3074             {
3075                 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3076                 cur = cur->mNext.load(std::memory_order_relaxed);
3077             }
3078             cur->mVoice = voice;
3079             cur->mSourceID = source->id;
3080             cur->mState = VChangeState::Pause;
3081         }
3082     }
3083     if LIKELY(tail)
3084     {
3085         SendVoiceChanges(context.get(), tail);
3086         /* Second, now that the voice changes have been sent, because it's
3087          * possible that the voice stopped after it was detected playing and
3088          * before the voice got paused, recheck that the source is still
3089          * considered playing and set it to paused if so.
3090          */
3091         for(ALsource *source : srchandles)
3092         {
3093             Voice *voice{GetSourceVoice(source, context.get())};
3094             if(GetSourceState(source, voice) == AL_PLAYING)
3095                 source->state = AL_PAUSED;
3096         }
3097     }
3098 }
3099 END_API_FUNC
3100 
3101 
alSourceStop(ALuint source)3102 AL_API void AL_APIENTRY alSourceStop(ALuint source)
3103 START_API_FUNC
3104 { alSourceStopv(1, &source); }
3105 END_API_FUNC
3106 
alSourceStopv(ALsizei n,const ALuint * sources)3107 AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
3108 START_API_FUNC
3109 {
3110     ContextRef context{GetContextRef()};
3111     if UNLIKELY(!context) return;
3112 
3113     if UNLIKELY(n < 0)
3114         context->setError(AL_INVALID_VALUE, "Stopping %d sources", n);
3115     if UNLIKELY(n <= 0) return;
3116 
3117     al::vector<ALsource*> extra_sources;
3118     std::array<ALsource*,8> source_storage;
3119     al::span<ALsource*> srchandles;
3120     if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3121         srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3122     else
3123     {
3124         extra_sources.resize(static_cast<ALuint>(n));
3125         srchandles = {extra_sources.data(), extra_sources.size()};
3126     }
3127 
3128     std::lock_guard<std::mutex> _{context->mSourceLock};
3129     for(auto &srchdl : srchandles)
3130     {
3131         srchdl = LookupSource(context.get(), *sources);
3132         if(!srchdl)
3133             SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3134         ++sources;
3135     }
3136 
3137     VoiceChange *tail{}, *cur{};
3138     for(ALsource *source : srchandles)
3139     {
3140         if(Voice *voice{GetSourceVoice(source, context.get())})
3141         {
3142             if(!cur)
3143                 cur = tail = GetVoiceChanger(context.get());
3144             else
3145             {
3146                 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3147                 cur = cur->mNext.load(std::memory_order_relaxed);
3148             }
3149             voice->mPendingChange.store(true, std::memory_order_relaxed);
3150             cur->mVoice = voice;
3151             cur->mSourceID = source->id;
3152             cur->mState = VChangeState::Stop;
3153             source->state = AL_STOPPED;
3154         }
3155         source->Offset = 0.0;
3156         source->OffsetType = AL_NONE;
3157         source->VoiceIdx = INVALID_VOICE_IDX;
3158     }
3159     if LIKELY(tail)
3160         SendVoiceChanges(context.get(), tail);
3161 }
3162 END_API_FUNC
3163 
3164 
alSourceRewind(ALuint source)3165 AL_API void AL_APIENTRY alSourceRewind(ALuint source)
3166 START_API_FUNC
3167 { alSourceRewindv(1, &source); }
3168 END_API_FUNC
3169 
alSourceRewindv(ALsizei n,const ALuint * sources)3170 AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
3171 START_API_FUNC
3172 {
3173     ContextRef context{GetContextRef()};
3174     if UNLIKELY(!context) return;
3175 
3176     if UNLIKELY(n < 0)
3177         context->setError(AL_INVALID_VALUE, "Rewinding %d sources", n);
3178     if UNLIKELY(n <= 0) return;
3179 
3180     al::vector<ALsource*> extra_sources;
3181     std::array<ALsource*,8> source_storage;
3182     al::span<ALsource*> srchandles;
3183     if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3184         srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3185     else
3186     {
3187         extra_sources.resize(static_cast<ALuint>(n));
3188         srchandles = {extra_sources.data(), extra_sources.size()};
3189     }
3190 
3191     std::lock_guard<std::mutex> _{context->mSourceLock};
3192     for(auto &srchdl : srchandles)
3193     {
3194         srchdl = LookupSource(context.get(), *sources);
3195         if(!srchdl)
3196             SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3197         ++sources;
3198     }
3199 
3200     VoiceChange *tail{}, *cur{};
3201     for(ALsource *source : srchandles)
3202     {
3203         Voice *voice{GetSourceVoice(source, context.get())};
3204         if(source->state != AL_INITIAL)
3205         {
3206             if(!cur)
3207                 cur = tail = GetVoiceChanger(context.get());
3208             else
3209             {
3210                 cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
3211                 cur = cur->mNext.load(std::memory_order_relaxed);
3212             }
3213             if(voice)
3214                 voice->mPendingChange.store(true, std::memory_order_relaxed);
3215             cur->mVoice = voice;
3216             cur->mSourceID = source->id;
3217             cur->mState = VChangeState::Reset;
3218             source->state = AL_INITIAL;
3219         }
3220         source->Offset = 0.0;
3221         source->OffsetType = AL_NONE;
3222         source->VoiceIdx = INVALID_VOICE_IDX;
3223     }
3224     if LIKELY(tail)
3225         SendVoiceChanges(context.get(), tail);
3226 }
3227 END_API_FUNC
3228 
3229 
alSourceQueueBuffers(ALuint src,ALsizei nb,const ALuint * buffers)3230 AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
3231 START_API_FUNC
3232 {
3233     ContextRef context{GetContextRef()};
3234     if UNLIKELY(!context) return;
3235 
3236     if UNLIKELY(nb < 0)
3237         context->setError(AL_INVALID_VALUE, "Queueing %d buffers", nb);
3238     if UNLIKELY(nb <= 0) return;
3239 
3240     std::lock_guard<std::mutex> _{context->mSourceLock};
3241     ALsource *source{LookupSource(context.get(),src)};
3242     if UNLIKELY(!source)
3243         SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
3244 
3245     /* Can't queue on a Static Source */
3246     if UNLIKELY(source->SourceType == AL_STATIC)
3247         SETERR_RETURN(context, AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
3248 
3249     /* Check for a valid Buffer, for its frequency and format */
3250     ALCdevice *device{context->mDevice.get()};
3251     ALbuffer *BufferFmt{nullptr};
3252     for(auto &item : source->mQueue)
3253     {
3254         BufferFmt = item.mBuffer;
3255         if(BufferFmt) break;
3256     }
3257 
3258     std::unique_lock<std::mutex> buflock{device->BufferLock};
3259     const size_t NewListStart{source->mQueue.size()};
3260     ALbufferQueueItem *BufferList{nullptr};
3261     for(ALsizei i{0};i < nb;i++)
3262     {
3263         bool fmt_mismatch{false};
3264         ALbuffer *buffer{nullptr};
3265         if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr)
3266         {
3267             context->setError(AL_INVALID_NAME, "Queueing invalid buffer ID %u", buffers[i]);
3268             goto buffer_error;
3269         }
3270         if(buffer && buffer->mCallback)
3271         {
3272             context->setError(AL_INVALID_OPERATION, "Queueing callback buffer %u", buffers[i]);
3273             goto buffer_error;
3274         }
3275 
3276         source->mQueue.emplace_back();
3277         if(!BufferList)
3278             BufferList = &source->mQueue.back();
3279         else
3280         {
3281             auto &item = source->mQueue.back();
3282             BufferList->mNext.store(&item, std::memory_order_relaxed);
3283             BufferList = &item;
3284         }
3285         if(!buffer) continue;
3286         BufferList->mSampleLen = buffer->mSampleLen;
3287         BufferList->mLoopEnd = buffer->mSampleLen;
3288         BufferList->mSamples = buffer->mData.data();
3289         BufferList->mBuffer = buffer;
3290         IncrementRef(buffer->ref);
3291 
3292         if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
3293         {
3294             context->setError(AL_INVALID_OPERATION, "Queueing non-persistently mapped buffer %u",
3295                 buffer->id);
3296             goto buffer_error;
3297         }
3298 
3299         if(BufferFmt == nullptr)
3300             BufferFmt = buffer;
3301         else
3302         {
3303             fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate;
3304             fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels;
3305             if(BufferFmt->isBFormat())
3306             {
3307                 fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout;
3308                 fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling;
3309             }
3310             fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder;
3311             fmt_mismatch |= BufferFmt->OriginalType != buffer->OriginalType;
3312         }
3313         if UNLIKELY(fmt_mismatch)
3314         {
3315             context->setError(AL_INVALID_OPERATION, "Queueing buffer with mismatched format");
3316 
3317         buffer_error:
3318             /* A buffer failed (invalid ID or format), so unlock and release
3319              * each buffer we had.
3320              */
3321             auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart);
3322             for(;iter != source->mQueue.end();++iter)
3323             {
3324                 if(ALbuffer *buf{iter->mBuffer})
3325                     DecrementRef(buf->ref);
3326             }
3327             source->mQueue.resize(NewListStart);
3328             return;
3329         }
3330     }
3331     /* All buffers good. */
3332     buflock.unlock();
3333 
3334     /* Source is now streaming */
3335     source->SourceType = AL_STREAMING;
3336 
3337     if(NewListStart != 0)
3338     {
3339         auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart);
3340         (iter-1)->mNext.store(std::addressof(*iter), std::memory_order_release);
3341     }
3342 }
3343 END_API_FUNC
3344 
alSourceUnqueueBuffers(ALuint src,ALsizei nb,ALuint * buffers)3345 AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
3346 START_API_FUNC
3347 {
3348     ContextRef context{GetContextRef()};
3349     if UNLIKELY(!context) return;
3350 
3351     if UNLIKELY(nb < 0)
3352         context->setError(AL_INVALID_VALUE, "Unqueueing %d buffers", nb);
3353     if UNLIKELY(nb <= 0) return;
3354 
3355     std::lock_guard<std::mutex> _{context->mSourceLock};
3356     ALsource *source{LookupSource(context.get(),src)};
3357     if UNLIKELY(!source)
3358         SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
3359 
3360     if UNLIKELY(source->SourceType != AL_STREAMING)
3361         SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from a non-streaming source %u",
3362             src);
3363     if UNLIKELY(source->Looping)
3364         SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from looping source %u", src);
3365 
3366     /* Make sure enough buffers have been processed to unqueue. */
3367     uint processed{0u};
3368     if LIKELY(source->state != AL_INITIAL)
3369     {
3370         VoiceBufferItem *Current{nullptr};
3371         if(Voice *voice{GetSourceVoice(source, context.get())})
3372             Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
3373         for(auto &item : source->mQueue)
3374         {
3375             if(&item == Current)
3376                 break;
3377             ++processed;
3378         }
3379     }
3380     if UNLIKELY(processed < static_cast<ALuint>(nb))
3381         SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing %d buffer%s (only %u processed)",
3382             nb, (nb==1)?"":"s", processed);
3383 
3384     do {
3385         auto &head = source->mQueue.front();
3386         if(ALbuffer *buffer{head.mBuffer})
3387         {
3388             *(buffers++) = buffer->id;
3389             DecrementRef(buffer->ref);
3390         }
3391         else
3392             *(buffers++) = 0;
3393         source->mQueue.pop_front();
3394     } while(--nb);
3395 }
3396 END_API_FUNC
3397 
3398 
ALsource()3399 ALsource::ALsource()
3400 {
3401     Direct.Gain = 1.0f;
3402     Direct.GainHF = 1.0f;
3403     Direct.HFReference = LOWPASSFREQREF;
3404     Direct.GainLF = 1.0f;
3405     Direct.LFReference = HIGHPASSFREQREF;
3406     for(auto &send : Send)
3407     {
3408         send.Slot = nullptr;
3409         send.Gain = 1.0f;
3410         send.GainHF = 1.0f;
3411         send.HFReference = LOWPASSFREQREF;
3412         send.GainLF = 1.0f;
3413         send.LFReference = HIGHPASSFREQREF;
3414     }
3415 
3416     PropsClean.test_and_set(std::memory_order_relaxed);
3417 }
3418 
~ALsource()3419 ALsource::~ALsource()
3420 {
3421     for(auto &item : mQueue)
3422     {
3423         if(ALbuffer *buffer{item.mBuffer})
3424             DecrementRef(buffer->ref);
3425     }
3426 
3427     auto clear_send = [](ALsource::SendData &send) -> void
3428     { if(send.Slot) DecrementRef(send.Slot->ref); };
3429     std::for_each(Send.begin(), Send.end(), clear_send);
3430 }
3431 
UpdateAllSourceProps(ALCcontext * context)3432 void UpdateAllSourceProps(ALCcontext *context)
3433 {
3434     std::lock_guard<std::mutex> _{context->mSourceLock};
3435     auto voicelist = context->getVoicesSpan();
3436     ALuint vidx{0u};
3437     for(Voice *voice : voicelist)
3438     {
3439         ALuint sid{voice->mSourceID.load(std::memory_order_acquire)};
3440         ALsource *source = sid ? LookupSource(context, sid) : nullptr;
3441         if(source && source->VoiceIdx == vidx)
3442         {
3443             if(!source->PropsClean.test_and_set(std::memory_order_acq_rel))
3444                 UpdateSourceProps(source, voice, context);
3445         }
3446         ++vidx;
3447     }
3448 }
3449 
~SourceSubList()3450 SourceSubList::~SourceSubList()
3451 {
3452     uint64_t usemask{~FreeMask};
3453     while(usemask)
3454     {
3455         const int idx{al::countr_zero(usemask)};
3456         al::destroy_at(Sources+idx);
3457         usemask &= ~(1_u64 << idx);
3458     }
3459     FreeMask = ~usemask;
3460     al_free(Sources);
3461     Sources = nullptr;
3462 }
3463