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