Fix reverse iterators for spans
[openal-soft.git] / al / source.cpp
blob75d4a8037666225c430a2e259f54444b69fbf291
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.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include "source.h"
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>
43 #include "AL/al.h"
44 #include "AL/alc.h"
45 #include "AL/alext.h"
46 #include "AL/efx.h"
48 #include "alcmain.h"
49 #include "alcontext.h"
50 #include "alexcpt.h"
51 #include "almalloc.h"
52 #include "alnumeric.h"
53 #include "aloptional.h"
54 #include "alspan.h"
55 #include "alu.h"
56 #include "ambidefs.h"
57 #include "atomic.h"
58 #include "auxeffectslot.h"
59 #include "backends/base.h"
60 #include "bformatdec.h"
61 #include "buffer.h"
62 #include "event.h"
63 #include "filter.h"
64 #include "filters/nfc.h"
65 #include "filters/splitter.h"
66 #include "inprogext.h"
67 #include "logging.h"
68 #include "math_defs.h"
69 #include "opthelpers.h"
70 #include "ringbuffer.h"
71 #include "threads.h"
74 namespace {
76 using namespace std::placeholders;
77 using std::chrono::nanoseconds;
79 ALvoice *GetSourceVoice(ALsource *source, ALCcontext *context)
81 ALuint idx{source->VoiceIdx};
82 if(idx < context->mVoices.size())
84 ALuint sid{source->id};
85 ALvoice &voice = context->mVoices[idx];
86 if(voice.mSourceID.load(std::memory_order_acquire) == sid)
87 return &voice;
89 source->VoiceIdx = INVALID_VOICE_IDX;
90 return nullptr;
93 void UpdateSourceProps(const ALsource *source, ALvoice *voice, ALCcontext *context)
95 /* Get an unused property container, or allocate a new one as needed. */
96 ALvoiceProps *props{context->mFreeVoiceProps.load(std::memory_order_acquire)};
97 if(!props)
98 props = new ALvoiceProps{};
99 else
101 ALvoiceProps *next;
102 do {
103 next = props->next.load(std::memory_order_relaxed);
104 } while(context->mFreeVoiceProps.compare_exchange_weak(props, next,
105 std::memory_order_acq_rel, std::memory_order_acquire) == 0);
108 /* Copy in current property values. */
109 props->Pitch = source->Pitch;
110 props->Gain = source->Gain;
111 props->OuterGain = source->OuterGain;
112 props->MinGain = source->MinGain;
113 props->MaxGain = source->MaxGain;
114 props->InnerAngle = source->InnerAngle;
115 props->OuterAngle = source->OuterAngle;
116 props->RefDistance = source->RefDistance;
117 props->MaxDistance = source->MaxDistance;
118 props->RolloffFactor = source->RolloffFactor;
119 props->Position = source->Position;
120 props->Velocity = source->Velocity;
121 props->Direction = source->Direction;
122 props->OrientAt = source->OrientAt;
123 props->OrientUp = source->OrientUp;
124 props->HeadRelative = source->HeadRelative;
125 props->mDistanceModel = source->mDistanceModel;
126 props->mResampler = source->mResampler;
127 props->DirectChannels = source->DirectChannels;
128 props->mSpatializeMode = source->mSpatialize;
130 props->DryGainHFAuto = source->DryGainHFAuto;
131 props->WetGainAuto = source->WetGainAuto;
132 props->WetGainHFAuto = source->WetGainHFAuto;
133 props->OuterGainHF = source->OuterGainHF;
135 props->AirAbsorptionFactor = source->AirAbsorptionFactor;
136 props->RoomRolloffFactor = source->RoomRolloffFactor;
137 props->DopplerFactor = source->DopplerFactor;
139 props->StereoPan = source->StereoPan;
141 props->Radius = source->Radius;
143 props->Direct.Gain = source->Direct.Gain;
144 props->Direct.GainHF = source->Direct.GainHF;
145 props->Direct.HFReference = source->Direct.HFReference;
146 props->Direct.GainLF = source->Direct.GainLF;
147 props->Direct.LFReference = source->Direct.LFReference;
149 auto copy_send = [](const ALsource::SendData &srcsend) noexcept -> ALvoicePropsBase::SendData
151 ALvoicePropsBase::SendData ret;
152 ret.Slot = srcsend.Slot;
153 ret.Gain = srcsend.Gain;
154 ret.GainHF = srcsend.GainHF;
155 ret.HFReference = srcsend.HFReference;
156 ret.GainLF = srcsend.GainLF;
157 ret.LFReference = srcsend.LFReference;
158 return ret;
160 std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send);
162 /* Set the new container for updating internal parameters. */
163 props = voice->mUpdate.exchange(props, std::memory_order_acq_rel);
164 if(props)
166 /* If there was an unused update container, put it back in the
167 * freelist.
169 AtomicReplaceHead(context->mFreeVoiceProps, props);
173 /* GetSourceSampleOffset
175 * Gets the current read offset for the given Source, in 32.32 fixed-point
176 * samples. The offset is relative to the start of the queue (not the start of
177 * the current buffer).
179 int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
181 ALCdevice *device{context->mDevice.get()};
182 const ALbufferlistitem *Current;
183 uint64_t readPos;
184 ALuint refcount;
185 ALvoice *voice;
187 do {
188 Current = nullptr;
189 readPos = 0;
190 while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
191 std::this_thread::yield();
192 *clocktime = GetDeviceClockTime(device);
194 voice = GetSourceVoice(Source, context);
195 if(voice)
197 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
199 readPos = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << 32;
200 readPos |= uint64_t{voice->mPositionFrac.load(std::memory_order_relaxed)} <<
201 (32-FRACTIONBITS);
203 std::atomic_thread_fence(std::memory_order_acquire);
204 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
206 if(voice)
208 const ALbufferlistitem *BufferList{Source->queue};
209 while(BufferList && BufferList != Current)
211 readPos += uint64_t{BufferList->mSampleLen} << 32;
212 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
214 readPos = minu64(readPos, 0x7fffffffffffffff_u64);
217 return static_cast<int64_t>(readPos);
220 /* GetSourceSecOffset
222 * Gets the current read offset for the given Source, in seconds. The offset is
223 * relative to the start of the queue (not the start of the current buffer).
225 ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
227 ALCdevice *device{context->mDevice.get()};
228 const ALbufferlistitem *Current;
229 uint64_t readPos;
230 ALuint refcount;
231 ALvoice *voice;
233 do {
234 Current = nullptr;
235 readPos = 0;
236 while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
237 std::this_thread::yield();
238 *clocktime = GetDeviceClockTime(device);
240 voice = GetSourceVoice(Source, context);
241 if(voice)
243 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
245 readPos = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << FRACTIONBITS;
246 readPos |= voice->mPositionFrac.load(std::memory_order_relaxed);
248 std::atomic_thread_fence(std::memory_order_acquire);
249 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
251 ALdouble offset{0.0};
252 if(voice)
254 const ALbufferlistitem *BufferList{Source->queue};
255 const ALbuffer *BufferFmt{nullptr};
256 while(BufferList && BufferList != Current)
258 if(!BufferFmt) BufferFmt = BufferList->mBuffer;
259 readPos += uint64_t{BufferList->mSampleLen} << FRACTIONBITS;
260 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
263 while(BufferList && !BufferFmt)
265 BufferFmt = BufferList->mBuffer;
266 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
268 assert(BufferFmt != nullptr);
270 offset = static_cast<ALdouble>(readPos) / ALdouble{FRACTIONONE} / BufferFmt->Frequency;
273 return offset;
276 /* GetSourceOffset
278 * Gets the current read offset for the given Source, in the appropriate format
279 * (Bytes, Samples or Seconds). The offset is relative to the start of the
280 * queue (not the start of the current buffer).
282 ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
284 ALCdevice *device{context->mDevice.get()};
285 const ALbufferlistitem *Current;
286 ALuint readPos;
287 ALuint readPosFrac;
288 ALuint refcount;
289 ALvoice *voice;
291 do {
292 Current = nullptr;
293 readPos = readPosFrac = 0;
294 while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
295 std::this_thread::yield();
296 voice = GetSourceVoice(Source, context);
297 if(voice)
299 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
301 readPos = voice->mPosition.load(std::memory_order_relaxed);
302 readPosFrac = voice->mPositionFrac.load(std::memory_order_relaxed);
304 std::atomic_thread_fence(std::memory_order_acquire);
305 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
307 ALdouble offset{0.0};
308 if(!voice) return offset;
310 const ALbufferlistitem *BufferList{Source->queue};
311 const ALbuffer *BufferFmt{nullptr};
312 ALuint totalBufferLen{0u};
313 bool readFin{false};
315 while(BufferList)
317 if(!BufferFmt) BufferFmt = BufferList->mBuffer;
319 readFin |= (BufferList == Current);
320 totalBufferLen += BufferList->mSampleLen;
321 if(!readFin) readPos += BufferList->mSampleLen;
323 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
325 assert(BufferFmt != nullptr);
327 if(Source->Looping)
328 readPos %= totalBufferLen;
329 else
331 /* Wrap back to 0 */
332 if(readPos >= totalBufferLen)
333 readPos = readPosFrac = 0;
336 switch(name)
338 case AL_SEC_OFFSET:
339 offset = (readPos + static_cast<ALdouble>(readPosFrac)/FRACTIONONE) / BufferFmt->Frequency;
340 break;
342 case AL_SAMPLE_OFFSET:
343 offset = readPos + static_cast<ALdouble>(readPosFrac)/FRACTIONONE;
344 break;
346 case AL_BYTE_OFFSET:
347 if(BufferFmt->OriginalType == UserFmtIMA4)
349 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
350 ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
351 ALuint BlockSize{align * ChannelsFromFmt(BufferFmt->mFmtChannels)};
353 /* Round down to nearest ADPCM block */
354 offset = static_cast<ALdouble>(readPos / FrameBlockSize * BlockSize);
356 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
358 ALuint FrameBlockSize{BufferFmt->OriginalAlign};
359 ALuint align{(FrameBlockSize-2)/2 + 7};
360 ALuint BlockSize{align * ChannelsFromFmt(BufferFmt->mFmtChannels)};
362 /* Round down to nearest ADPCM block */
363 offset = static_cast<ALdouble>(readPos / FrameBlockSize * BlockSize);
365 else
367 const ALuint FrameSize{FrameSizeFromFmt(BufferFmt->mFmtChannels, BufferFmt->mFmtType)};
368 offset = static_cast<ALdouble>(readPos * FrameSize);
370 break;
373 return offset;
377 struct VoicePos {
378 ALuint pos, frac;
379 ALbufferlistitem *bufferitem;
383 * GetSampleOffset
385 * Retrieves the voice position, fixed-point fraction, and bufferlist item
386 * using the source's stored offset and offset type. If the source has no
387 * stored offset, or the offset is out of range, returns an empty optional.
389 al::optional<VoicePos> GetSampleOffset(ALsource *Source)
391 al::optional<VoicePos> ret;
393 /* Find the first valid Buffer in the Queue */
394 const ALbuffer *BufferFmt{nullptr};
395 ALbufferlistitem *BufferList{Source->queue};
396 while(BufferList)
398 if((BufferFmt=BufferList->mBuffer) != nullptr) break;
399 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
401 if(!BufferList)
403 Source->OffsetType = AL_NONE;
404 Source->Offset = 0.0;
405 return ret;
408 /* Get sample frame offset */
409 ALuint offset{0u}, frac{0u};
410 ALdouble dbloff, dblfrac;
411 switch(Source->OffsetType)
413 case AL_BYTE_OFFSET:
414 /* Determine the ByteOffset (and ensure it is block aligned) */
415 offset = static_cast<ALuint>(Source->Offset);
416 if(BufferFmt->OriginalType == UserFmtIMA4)
418 const ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
419 offset /= align * ChannelsFromFmt(BufferFmt->mFmtChannels);
420 offset *= BufferFmt->OriginalAlign;
422 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
424 const ALuint align{(BufferFmt->OriginalAlign-2)/2 + 7};
425 offset /= align * ChannelsFromFmt(BufferFmt->mFmtChannels);
426 offset *= BufferFmt->OriginalAlign;
428 else
429 offset /= FrameSizeFromFmt(BufferFmt->mFmtChannels, BufferFmt->mFmtType);
430 frac = 0;
431 break;
433 case AL_SAMPLE_OFFSET:
434 dblfrac = std::modf(Source->Offset, &dbloff);
435 offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
436 frac = static_cast<ALuint>(mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0));
437 break;
439 case AL_SEC_OFFSET:
440 dblfrac = std::modf(Source->Offset*BufferFmt->Frequency, &dbloff);
441 offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
442 frac = static_cast<ALuint>(mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0));
443 break;
445 Source->OffsetType = AL_NONE;
446 Source->Offset = 0.0;
448 /* Find the bufferlist item this offset belongs to. */
449 ALuint totalBufferLen{0u};
450 while(BufferList && totalBufferLen <= offset)
452 if(BufferList->mSampleLen > offset-totalBufferLen)
454 /* Offset is in this buffer */
455 ret = {offset-totalBufferLen, frac, BufferList};
456 return ret;
458 totalBufferLen += BufferList->mSampleLen;
460 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
463 /* Offset is out of range of the queue */
464 return ret;
469 * Returns if the last known state for the source was playing or paused. Does
470 * not sync with the mixer voice.
472 inline bool IsPlayingOrPaused(ALsource *source)
473 { return source->state == AL_PLAYING || source->state == AL_PAUSED; }
476 * Returns an updated source state using the matching voice's status (or lack
477 * thereof).
479 inline ALenum GetSourceState(ALsource *source, ALvoice *voice)
481 if(!voice && source->state == AL_PLAYING)
482 source->state = AL_STOPPED;
483 return source->state;
487 * Returns if the source should specify an update, given the context's
488 * deferring state and the source's last known state.
490 inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context)
492 return !context->mDeferUpdates.load(std::memory_order_acquire) &&
493 IsPlayingOrPaused(source);
497 bool EnsureSources(ALCcontext *context, size_t needed)
499 size_t count{std::accumulate(context->mSourceList.cbegin(), context->mSourceList.cend(),
500 size_t{0},
501 [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
502 { return cur + static_cast<ALuint>(POPCNT64(sublist.FreeMask)); }
505 while(needed > count)
507 if UNLIKELY(context->mSourceList.size() >= 1<<25)
508 return false;
510 context->mSourceList.emplace_back();
511 auto sublist = context->mSourceList.end() - 1;
512 sublist->FreeMask = ~0_u64;
513 sublist->Sources = static_cast<ALsource*>(al_calloc(alignof(ALsource), sizeof(ALsource)*64));
514 if UNLIKELY(!sublist->Sources)
516 context->mSourceList.pop_back();
517 return false;
519 count += 64;
521 return true;
524 ALsource *AllocSource(ALCcontext *context, ALuint num_sends)
526 auto sublist = std::find_if(context->mSourceList.begin(), context->mSourceList.end(),
527 [](const SourceSubList &entry) noexcept -> bool
528 { return entry.FreeMask != 0; }
530 auto lidx = static_cast<ALuint>(std::distance(context->mSourceList.begin(), sublist));
531 auto slidx = static_cast<ALuint>(CTZ64(sublist->FreeMask));
533 ALsource *source{::new (sublist->Sources + slidx) ALsource{num_sends}};
535 /* Add 1 to avoid source ID 0. */
536 source->id = ((lidx<<6) | slidx) + 1;
538 context->mNumSources += 1;
539 sublist->FreeMask &= ~(1_u64 << slidx);
541 return source;
544 void FreeSource(ALCcontext *context, ALsource *source)
546 const ALuint id{source->id - 1};
547 const size_t lidx{id >> 6};
548 const ALuint slidx{id & 0x3f};
550 if(IsPlayingOrPaused(source))
552 ALCdevice *device{context->mDevice.get()};
553 BackendLockGuard _{*device->Backend};
554 if(ALvoice *voice{GetSourceVoice(source, context)})
556 voice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
557 voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
558 voice->mSourceID.store(0u, std::memory_order_relaxed);
559 std::atomic_thread_fence(std::memory_order_release);
560 /* Don't set the voice to stopping if it was already stopped or
561 * stopping.
563 ALvoice::State oldvstate{ALvoice::Playing};
564 voice->mPlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping,
565 std::memory_order_acq_rel, std::memory_order_acquire);
569 al::destroy_at(source);
571 context->mSourceList[lidx].FreeMask |= 1_u64 << slidx;
572 context->mNumSources--;
576 inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept
578 const size_t lidx{(id-1) >> 6};
579 const ALuint slidx{(id-1) & 0x3f};
581 if UNLIKELY(lidx >= context->mSourceList.size())
582 return nullptr;
583 SourceSubList &sublist{context->mSourceList[lidx]};
584 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
585 return nullptr;
586 return sublist.Sources + slidx;
589 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
591 const size_t lidx{(id-1) >> 6};
592 const ALuint slidx{(id-1) & 0x3f};
594 if UNLIKELY(lidx >= device->BufferList.size())
595 return nullptr;
596 BufferSubList &sublist = device->BufferList[lidx];
597 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
598 return nullptr;
599 return sublist.Buffers + slidx;
602 inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept
604 const size_t lidx{(id-1) >> 6};
605 const ALuint slidx{(id-1) & 0x3f};
607 if UNLIKELY(lidx >= device->FilterList.size())
608 return nullptr;
609 FilterSubList &sublist = device->FilterList[lidx];
610 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
611 return nullptr;
612 return sublist.Filters + slidx;
615 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
617 const size_t lidx{(id-1) >> 6};
618 const ALuint slidx{(id-1) & 0x3f};
620 if UNLIKELY(lidx >= context->mEffectSlotList.size())
621 return nullptr;
622 EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
623 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
624 return nullptr;
625 return sublist.EffectSlots + slidx;
629 enum SourceProp : ALenum {
630 srcPitch = AL_PITCH,
631 srcGain = AL_GAIN,
632 srcMinGain = AL_MIN_GAIN,
633 srcMaxGain = AL_MAX_GAIN,
634 srcMaxDistance = AL_MAX_DISTANCE,
635 srcRolloffFactor = AL_ROLLOFF_FACTOR,
636 srcDopplerFactor = AL_DOPPLER_FACTOR,
637 srcConeOuterGain = AL_CONE_OUTER_GAIN,
638 srcSecOffset = AL_SEC_OFFSET,
639 srcSampleOffset = AL_SAMPLE_OFFSET,
640 srcByteOffset = AL_BYTE_OFFSET,
641 srcConeInnerAngle = AL_CONE_INNER_ANGLE,
642 srcConeOuterAngle = AL_CONE_OUTER_ANGLE,
643 srcRefDistance = AL_REFERENCE_DISTANCE,
645 srcPosition = AL_POSITION,
646 srcVelocity = AL_VELOCITY,
647 srcDirection = AL_DIRECTION,
649 srcSourceRelative = AL_SOURCE_RELATIVE,
650 srcLooping = AL_LOOPING,
651 srcBuffer = AL_BUFFER,
652 srcSourceState = AL_SOURCE_STATE,
653 srcBuffersQueued = AL_BUFFERS_QUEUED,
654 srcBuffersProcessed = AL_BUFFERS_PROCESSED,
655 srcSourceType = AL_SOURCE_TYPE,
657 /* ALC_EXT_EFX */
658 srcConeOuterGainHF = AL_CONE_OUTER_GAINHF,
659 srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR,
660 srcRoomRolloffFactor = AL_ROOM_ROLLOFF_FACTOR,
661 srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO,
662 srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO,
663 srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO,
664 srcDirectFilter = AL_DIRECT_FILTER,
665 srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER,
667 /* AL_SOFT_direct_channels */
668 srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT,
670 /* AL_EXT_source_distance_model */
671 srcDistanceModel = AL_DISTANCE_MODEL,
673 /* AL_SOFT_source_latency */
674 srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT,
675 srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT,
677 /* AL_EXT_STEREO_ANGLES */
678 srcAngles = AL_STEREO_ANGLES,
680 /* AL_EXT_SOURCE_RADIUS */
681 srcRadius = AL_SOURCE_RADIUS,
683 /* AL_EXT_BFORMAT */
684 srcOrientation = AL_ORIENTATION,
686 /* AL_SOFT_source_resampler */
687 srcResampler = AL_SOURCE_RESAMPLER_SOFT,
689 /* AL_SOFT_source_spatialize */
690 srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT,
692 /* ALC_SOFT_device_clock */
693 srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT,
694 srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT,
698 /** Can only be called while the mixer is locked! */
699 void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state)
701 ALbitfieldSOFT enabledevt{context->mEnabledEvts.load(std::memory_order_acquire)};
702 if(!(enabledevt&EventType_SourceStateChange)) return;
704 /* The mixer may have queued a state change that's not yet been processed,
705 * and we don't want state change messages to occur out of order, so send
706 * it through the async queue to ensure proper ordering.
708 RingBuffer *ring{context->mAsyncEvents.get()};
709 auto evt_vec = ring->getWriteVector();
710 if(evt_vec.first.len < 1) return;
712 AsyncEvent *evt{::new (evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}};
713 evt->u.srcstate.id = id;
714 evt->u.srcstate.state = state;
715 ring->writeAdvance(1);
716 context->mEventSem.post();
720 constexpr size_t MaxValues{6u};
722 ALuint FloatValsByProp(ALenum prop)
724 switch(static_cast<SourceProp>(prop))
726 case AL_PITCH:
727 case AL_GAIN:
728 case AL_MIN_GAIN:
729 case AL_MAX_GAIN:
730 case AL_MAX_DISTANCE:
731 case AL_ROLLOFF_FACTOR:
732 case AL_DOPPLER_FACTOR:
733 case AL_CONE_OUTER_GAIN:
734 case AL_SEC_OFFSET:
735 case AL_SAMPLE_OFFSET:
736 case AL_BYTE_OFFSET:
737 case AL_CONE_INNER_ANGLE:
738 case AL_CONE_OUTER_ANGLE:
739 case AL_REFERENCE_DISTANCE:
740 case AL_CONE_OUTER_GAINHF:
741 case AL_AIR_ABSORPTION_FACTOR:
742 case AL_ROOM_ROLLOFF_FACTOR:
743 case AL_DIRECT_FILTER_GAINHF_AUTO:
744 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
745 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
746 case AL_DIRECT_CHANNELS_SOFT:
747 case AL_DISTANCE_MODEL:
748 case AL_SOURCE_RELATIVE:
749 case AL_LOOPING:
750 case AL_SOURCE_STATE:
751 case AL_BUFFERS_QUEUED:
752 case AL_BUFFERS_PROCESSED:
753 case AL_SOURCE_TYPE:
754 case AL_SOURCE_RADIUS:
755 case AL_SOURCE_RESAMPLER_SOFT:
756 case AL_SOURCE_SPATIALIZE_SOFT:
757 return 1;
759 case AL_STEREO_ANGLES:
760 return 2;
762 case AL_POSITION:
763 case AL_VELOCITY:
764 case AL_DIRECTION:
765 return 3;
767 case AL_ORIENTATION:
768 return 6;
770 case AL_SEC_OFFSET_LATENCY_SOFT:
771 case AL_SEC_OFFSET_CLOCK_SOFT:
772 break; /* Double only */
774 case AL_BUFFER:
775 case AL_DIRECT_FILTER:
776 case AL_AUXILIARY_SEND_FILTER:
777 break; /* i/i64 only */
778 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
779 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
780 break; /* i64 only */
782 return 0;
784 ALuint DoubleValsByProp(ALenum prop)
786 switch(static_cast<SourceProp>(prop))
788 case AL_PITCH:
789 case AL_GAIN:
790 case AL_MIN_GAIN:
791 case AL_MAX_GAIN:
792 case AL_MAX_DISTANCE:
793 case AL_ROLLOFF_FACTOR:
794 case AL_DOPPLER_FACTOR:
795 case AL_CONE_OUTER_GAIN:
796 case AL_SEC_OFFSET:
797 case AL_SAMPLE_OFFSET:
798 case AL_BYTE_OFFSET:
799 case AL_CONE_INNER_ANGLE:
800 case AL_CONE_OUTER_ANGLE:
801 case AL_REFERENCE_DISTANCE:
802 case AL_CONE_OUTER_GAINHF:
803 case AL_AIR_ABSORPTION_FACTOR:
804 case AL_ROOM_ROLLOFF_FACTOR:
805 case AL_DIRECT_FILTER_GAINHF_AUTO:
806 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
807 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
808 case AL_DIRECT_CHANNELS_SOFT:
809 case AL_DISTANCE_MODEL:
810 case AL_SOURCE_RELATIVE:
811 case AL_LOOPING:
812 case AL_SOURCE_STATE:
813 case AL_BUFFERS_QUEUED:
814 case AL_BUFFERS_PROCESSED:
815 case AL_SOURCE_TYPE:
816 case AL_SOURCE_RADIUS:
817 case AL_SOURCE_RESAMPLER_SOFT:
818 case AL_SOURCE_SPATIALIZE_SOFT:
819 return 1;
821 case AL_SEC_OFFSET_LATENCY_SOFT:
822 case AL_SEC_OFFSET_CLOCK_SOFT:
823 case AL_STEREO_ANGLES:
824 return 2;
826 case AL_POSITION:
827 case AL_VELOCITY:
828 case AL_DIRECTION:
829 return 3;
831 case AL_ORIENTATION:
832 return 6;
834 case AL_BUFFER:
835 case AL_DIRECT_FILTER:
836 case AL_AUXILIARY_SEND_FILTER:
837 break; /* i/i64 only */
838 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
839 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
840 break; /* i64 only */
842 return 0;
846 bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const ALfloat> values);
847 bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const ALint> values);
848 bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const ALint64SOFT> values);
850 #define CHECKSIZE(v, s) do { \
851 if LIKELY((v).size() == (s) || (v).size() == MaxValues) break; \
852 Context->setError(AL_INVALID_ENUM, \
853 "Property 0x%04x expects %d value(s), got %zu", prop, (s), \
854 (v).size()); \
855 return false; \
856 } while(0)
857 #define CHECKVAL(x) do { \
858 if LIKELY(x) break; \
859 Context->setError(AL_INVALID_VALUE, "Value out of range"); \
860 return false; \
861 } while(0)
863 bool UpdateSourceProps(ALsource *source, ALCcontext *context)
865 ALvoice *voice;
866 if(SourceShouldUpdate(source, context) && (voice=GetSourceVoice(source, context)) != nullptr)
867 UpdateSourceProps(source, voice, context);
868 else
869 source->PropsClean.clear(std::memory_order_release);
870 return true;
873 bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const ALfloat> values)
875 ALint ival;
877 switch(prop)
879 case AL_SEC_OFFSET_LATENCY_SOFT:
880 case AL_SEC_OFFSET_CLOCK_SOFT:
881 /* Query only */
882 SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
883 "Setting read-only source property 0x%04x", prop);
885 case AL_PITCH:
886 CHECKSIZE(values, 1);
887 CHECKVAL(values[0] >= 0.0f);
889 Source->Pitch = values[0];
890 return UpdateSourceProps(Source, Context);
892 case AL_CONE_INNER_ANGLE:
893 CHECKSIZE(values, 1);
894 CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
896 Source->InnerAngle = values[0];
897 return UpdateSourceProps(Source, Context);
899 case AL_CONE_OUTER_ANGLE:
900 CHECKSIZE(values, 1);
901 CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
903 Source->OuterAngle = values[0];
904 return UpdateSourceProps(Source, Context);
906 case AL_GAIN:
907 CHECKSIZE(values, 1);
908 CHECKVAL(values[0] >= 0.0f);
910 Source->Gain = values[0];
911 return UpdateSourceProps(Source, Context);
913 case AL_MAX_DISTANCE:
914 CHECKSIZE(values, 1);
915 CHECKVAL(values[0] >= 0.0f);
917 Source->MaxDistance = values[0];
918 return UpdateSourceProps(Source, Context);
920 case AL_ROLLOFF_FACTOR:
921 CHECKSIZE(values, 1);
922 CHECKVAL(values[0] >= 0.0f);
924 Source->RolloffFactor = values[0];
925 return UpdateSourceProps(Source, Context);
927 case AL_REFERENCE_DISTANCE:
928 CHECKSIZE(values, 1);
929 CHECKVAL(values[0] >= 0.0f);
931 Source->RefDistance = values[0];
932 return UpdateSourceProps(Source, Context);
934 case AL_MIN_GAIN:
935 CHECKSIZE(values, 1);
936 CHECKVAL(values[0] >= 0.0f);
938 Source->MinGain = values[0];
939 return UpdateSourceProps(Source, Context);
941 case AL_MAX_GAIN:
942 CHECKSIZE(values, 1);
943 CHECKVAL(values[0] >= 0.0f);
945 Source->MaxGain = values[0];
946 return UpdateSourceProps(Source, Context);
948 case AL_CONE_OUTER_GAIN:
949 CHECKSIZE(values, 1);
950 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
952 Source->OuterGain = values[0];
953 return UpdateSourceProps(Source, Context);
955 case AL_CONE_OUTER_GAINHF:
956 CHECKSIZE(values, 1);
957 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
959 Source->OuterGainHF = values[0];
960 return UpdateSourceProps(Source, Context);
962 case AL_AIR_ABSORPTION_FACTOR:
963 CHECKSIZE(values, 1);
964 CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
966 Source->AirAbsorptionFactor = values[0];
967 return UpdateSourceProps(Source, Context);
969 case AL_ROOM_ROLLOFF_FACTOR:
970 CHECKSIZE(values, 1);
971 CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
973 Source->RoomRolloffFactor = values[0];
974 return UpdateSourceProps(Source, Context);
976 case AL_DOPPLER_FACTOR:
977 CHECKSIZE(values, 1);
978 CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
980 Source->DopplerFactor = values[0];
981 return UpdateSourceProps(Source, Context);
983 case AL_SEC_OFFSET:
984 case AL_SAMPLE_OFFSET:
985 case AL_BYTE_OFFSET:
986 CHECKSIZE(values, 1);
987 CHECKVAL(values[0] >= 0.0f);
989 Source->OffsetType = prop;
990 Source->Offset = values[0];
992 if(IsPlayingOrPaused(Source))
994 ALCdevice *device{Context->mDevice.get()};
995 BackendLockGuard _{*device->Backend};
996 /* Double-check that the source is still playing while we have the
997 * lock.
999 if(ALvoice *voice{GetSourceVoice(Source, Context)})
1001 auto vpos = GetSampleOffset(Source);
1002 if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid offset");
1004 voice->mPosition.store(vpos->pos, std::memory_order_relaxed);
1005 voice->mPositionFrac.store(vpos->frac, std::memory_order_relaxed);
1006 voice->mCurrentBuffer.store(vpos->bufferitem, std::memory_order_release);
1009 return true;
1011 case AL_SOURCE_RADIUS:
1012 CHECKSIZE(values, 1);
1013 CHECKVAL(values[0] >= 0.0f && std::isfinite(values[0]));
1015 Source->Radius = values[0];
1016 return UpdateSourceProps(Source, Context);
1018 case AL_STEREO_ANGLES:
1019 CHECKSIZE(values, 2);
1020 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]));
1022 Source->StereoPan[0] = values[0];
1023 Source->StereoPan[1] = values[1];
1024 return UpdateSourceProps(Source, Context);
1027 case AL_POSITION:
1028 CHECKSIZE(values, 3);
1029 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1031 Source->Position[0] = values[0];
1032 Source->Position[1] = values[1];
1033 Source->Position[2] = values[2];
1034 return UpdateSourceProps(Source, Context);
1036 case AL_VELOCITY:
1037 CHECKSIZE(values, 3);
1038 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1040 Source->Velocity[0] = values[0];
1041 Source->Velocity[1] = values[1];
1042 Source->Velocity[2] = values[2];
1043 return UpdateSourceProps(Source, Context);
1045 case AL_DIRECTION:
1046 CHECKSIZE(values, 3);
1047 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1049 Source->Direction[0] = values[0];
1050 Source->Direction[1] = values[1];
1051 Source->Direction[2] = values[2];
1052 return UpdateSourceProps(Source, Context);
1054 case AL_ORIENTATION:
1055 CHECKSIZE(values, 6);
1056 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2])
1057 && std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]));
1059 Source->OrientAt[0] = values[0];
1060 Source->OrientAt[1] = values[1];
1061 Source->OrientAt[2] = values[2];
1062 Source->OrientUp[0] = values[3];
1063 Source->OrientUp[1] = values[4];
1064 Source->OrientUp[2] = values[5];
1065 return UpdateSourceProps(Source, Context);
1068 case AL_SOURCE_RELATIVE:
1069 case AL_LOOPING:
1070 case AL_SOURCE_STATE:
1071 case AL_SOURCE_TYPE:
1072 case AL_DISTANCE_MODEL:
1073 case AL_DIRECT_FILTER_GAINHF_AUTO:
1074 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1075 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1076 case AL_DIRECT_CHANNELS_SOFT:
1077 case AL_SOURCE_RESAMPLER_SOFT:
1078 case AL_SOURCE_SPATIALIZE_SOFT:
1079 CHECKSIZE(values, 1);
1080 ival = static_cast<ALint>(values[0]);
1081 return SetSourceiv(Source, Context, prop, {&ival, 1u});
1083 case AL_BUFFERS_QUEUED:
1084 case AL_BUFFERS_PROCESSED:
1085 CHECKSIZE(values, 1);
1086 ival = static_cast<ALint>(static_cast<ALuint>(values[0]));
1087 return SetSourceiv(Source, Context, prop, {&ival, 1u});
1089 case AL_BUFFER:
1090 case AL_DIRECT_FILTER:
1091 case AL_AUXILIARY_SEND_FILTER:
1092 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1093 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1094 break;
1097 ERR("Unexpected property: 0x%04x\n", prop);
1098 Context->setError(AL_INVALID_ENUM, "Invalid source float property 0x%04x", prop);
1099 return false;
1102 bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const ALint> values)
1104 ALCdevice *device{Context->mDevice.get()};
1105 ALbuffer *buffer{nullptr};
1106 ALfilter *filter{nullptr};
1107 ALeffectslot *slot{nullptr};
1108 ALbufferlistitem *oldlist{nullptr};
1109 std::unique_lock<std::mutex> slotlock;
1110 std::unique_lock<std::mutex> filtlock;
1111 std::unique_lock<std::mutex> buflock;
1112 ALfloat fvals[6];
1114 switch(prop)
1116 case AL_SOURCE_STATE:
1117 case AL_SOURCE_TYPE:
1118 case AL_BUFFERS_QUEUED:
1119 case AL_BUFFERS_PROCESSED:
1120 /* Query only */
1121 SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1122 "Setting read-only source property 0x%04x", prop);
1124 case AL_SOURCE_RELATIVE:
1125 CHECKSIZE(values, 1);
1126 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1128 Source->HeadRelative = values[0] != AL_FALSE;
1129 return UpdateSourceProps(Source, Context);
1131 case AL_LOOPING:
1132 CHECKSIZE(values, 1);
1133 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1135 Source->Looping = values[0] != AL_FALSE;
1136 if(IsPlayingOrPaused(Source))
1138 if(ALvoice *voice{GetSourceVoice(Source, Context)})
1140 if(Source->Looping)
1141 voice->mLoopBuffer.store(Source->queue, std::memory_order_release);
1142 else
1143 voice->mLoopBuffer.store(nullptr, std::memory_order_release);
1145 /* If the source is playing, wait for the current mix to finish
1146 * to ensure it isn't currently looping back or reaching the
1147 * end.
1149 while((device->MixCount.load(std::memory_order_acquire)&1))
1150 std::this_thread::yield();
1153 return true;
1155 case AL_BUFFER:
1156 CHECKSIZE(values, 1);
1157 buflock = std::unique_lock<std::mutex>{device->BufferLock};
1158 if(values[0] && (buffer=LookupBuffer(device, static_cast<ALuint>(values[0]))) == nullptr)
1159 SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid buffer ID %u",
1160 static_cast<ALuint>(values[0]));
1162 if(buffer && buffer->MappedAccess != 0 &&
1163 !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
1164 SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1165 "Setting non-persistently mapped buffer %u", buffer->id);
1166 else
1168 ALenum state = GetSourceState(Source, GetSourceVoice(Source, Context));
1169 if(state == AL_PLAYING || state == AL_PAUSED)
1170 SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1171 "Setting buffer on playing or paused source %u", Source->id);
1174 oldlist = Source->queue;
1175 if(buffer != nullptr)
1177 /* Add the selected buffer to a one-item queue */
1178 auto newlist = new ALbufferlistitem{};
1179 newlist->mSampleLen = buffer->SampleLen;
1180 newlist->mBuffer = buffer;
1181 IncrementRef(buffer->ref);
1183 /* Source is now Static */
1184 Source->SourceType = AL_STATIC;
1185 Source->queue = newlist;
1187 else
1189 /* Source is now Undetermined */
1190 Source->SourceType = AL_UNDETERMINED;
1191 Source->queue = nullptr;
1193 buflock.unlock();
1195 /* Delete all elements in the previous queue */
1196 while(oldlist != nullptr)
1198 std::unique_ptr<ALbufferlistitem> temp{oldlist};
1199 oldlist = temp->mNext.load(std::memory_order_relaxed);
1201 if((buffer=temp->mBuffer) != nullptr)
1202 DecrementRef(buffer->ref);
1204 return true;
1206 case AL_SEC_OFFSET:
1207 case AL_SAMPLE_OFFSET:
1208 case AL_BYTE_OFFSET:
1209 CHECKSIZE(values, 1);
1210 CHECKVAL(values[0] >= 0);
1212 Source->OffsetType = prop;
1213 Source->Offset = values[0];
1215 if(IsPlayingOrPaused(Source))
1217 BackendLockGuard _{*device->Backend};
1218 if(ALvoice *voice{GetSourceVoice(Source, Context)})
1220 auto vpos = GetSampleOffset(Source);
1221 if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid source offset");
1223 voice->mPosition.store(vpos->pos, std::memory_order_relaxed);
1224 voice->mPositionFrac.store(vpos->frac, std::memory_order_relaxed);
1225 voice->mCurrentBuffer.store(vpos->bufferitem, std::memory_order_release);
1228 return true;
1230 case AL_DIRECT_FILTER:
1231 CHECKSIZE(values, 1);
1232 filtlock = std::unique_lock<std::mutex>{device->FilterLock};
1233 if(values[0] && (filter=LookupFilter(device, static_cast<ALuint>(values[0]))) == nullptr)
1234 SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid filter ID %u",
1235 static_cast<ALuint>(values[0]));
1237 if(!filter)
1239 Source->Direct.Gain = 1.0f;
1240 Source->Direct.GainHF = 1.0f;
1241 Source->Direct.HFReference = LOWPASSFREQREF;
1242 Source->Direct.GainLF = 1.0f;
1243 Source->Direct.LFReference = HIGHPASSFREQREF;
1245 else
1247 Source->Direct.Gain = filter->Gain;
1248 Source->Direct.GainHF = filter->GainHF;
1249 Source->Direct.HFReference = filter->HFReference;
1250 Source->Direct.GainLF = filter->GainLF;
1251 Source->Direct.LFReference = filter->LFReference;
1253 filtlock.unlock();
1254 return UpdateSourceProps(Source, Context);
1256 case AL_DIRECT_FILTER_GAINHF_AUTO:
1257 CHECKSIZE(values, 1);
1258 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1260 Source->DryGainHFAuto = values[0] != AL_FALSE;
1261 return UpdateSourceProps(Source, Context);
1263 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1264 CHECKSIZE(values, 1);
1265 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1267 Source->WetGainAuto = values[0] != AL_FALSE;
1268 return UpdateSourceProps(Source, Context);
1270 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1271 CHECKSIZE(values, 1);
1272 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1274 Source->WetGainHFAuto = values[0] != AL_FALSE;
1275 return UpdateSourceProps(Source, Context);
1277 case AL_DIRECT_CHANNELS_SOFT:
1278 CHECKSIZE(values, 1);
1279 CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
1281 Source->DirectChannels = values[0] != AL_FALSE;
1282 return UpdateSourceProps(Source, Context);
1284 case AL_DISTANCE_MODEL:
1285 CHECKSIZE(values, 1);
1286 CHECKVAL(values[0] == AL_NONE ||
1287 values[0] == AL_INVERSE_DISTANCE || values[0] == AL_INVERSE_DISTANCE_CLAMPED ||
1288 values[0] == AL_LINEAR_DISTANCE || values[0] == AL_LINEAR_DISTANCE_CLAMPED ||
1289 values[0] == AL_EXPONENT_DISTANCE || values[0] == AL_EXPONENT_DISTANCE_CLAMPED);
1291 Source->mDistanceModel = static_cast<DistanceModel>(values[0]);
1292 if(Context->mSourceDistanceModel)
1293 return UpdateSourceProps(Source, Context);
1294 return true;
1296 case AL_SOURCE_RESAMPLER_SOFT:
1297 CHECKSIZE(values, 1);
1298 CHECKVAL(values[0] >= 0 && values[0] <= static_cast<int>(Resampler::Max));
1300 Source->mResampler = static_cast<Resampler>(values[0]);
1301 return UpdateSourceProps(Source, Context);
1303 case AL_SOURCE_SPATIALIZE_SOFT:
1304 CHECKSIZE(values, 1);
1305 CHECKVAL(values[0] >= AL_FALSE && values[0] <= AL_AUTO_SOFT);
1307 Source->mSpatialize = static_cast<SpatializeMode>(values[0]);
1308 return UpdateSourceProps(Source, Context);
1311 case AL_AUXILIARY_SEND_FILTER:
1312 CHECKSIZE(values, 3);
1313 slotlock = std::unique_lock<std::mutex>{Context->mEffectSlotLock};
1314 if(values[0] && (slot=LookupEffectSlot(Context, static_cast<ALuint>(values[0]))) == nullptr)
1315 SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid effect ID %u", values[0]);
1316 if(static_cast<ALuint>(values[1]) >= device->NumAuxSends)
1317 SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid send %u", values[1]);
1319 filtlock = std::unique_lock<std::mutex>{device->FilterLock};
1320 if(values[2] && (filter=LookupFilter(device, static_cast<ALuint>(values[2]))) == nullptr)
1321 SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid filter ID %u", values[2]);
1323 if(!filter)
1325 /* Disable filter */
1326 auto &send = Source->Send[static_cast<ALuint>(values[1])];
1327 send.Gain = 1.0f;
1328 send.GainHF = 1.0f;
1329 send.HFReference = LOWPASSFREQREF;
1330 send.GainLF = 1.0f;
1331 send.LFReference = HIGHPASSFREQREF;
1333 else
1335 auto &send = Source->Send[static_cast<ALuint>(values[1])];
1336 send.Gain = filter->Gain;
1337 send.GainHF = filter->GainHF;
1338 send.HFReference = filter->HFReference;
1339 send.GainLF = filter->GainLF;
1340 send.LFReference = filter->LFReference;
1342 filtlock.unlock();
1344 if(slot != Source->Send[static_cast<ALuint>(values[1])].Slot && IsPlayingOrPaused(Source))
1346 /* Add refcount on the new slot, and release the previous slot */
1347 if(slot) IncrementRef(slot->ref);
1348 if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
1349 DecrementRef(oldslot->ref);
1350 Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
1352 /* We must force an update if the auxiliary slot changed on an
1353 * active source, in case the slot is about to be deleted.
1355 ALvoice *voice{GetSourceVoice(Source, Context)};
1356 if(voice) UpdateSourceProps(Source, voice, Context);
1357 else Source->PropsClean.clear(std::memory_order_release);
1359 else
1361 if(slot) IncrementRef(slot->ref);
1362 if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
1363 DecrementRef(oldslot->ref);
1364 Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
1365 UpdateSourceProps(Source, Context);
1367 return true;
1370 /* 1x float */
1371 case AL_CONE_INNER_ANGLE:
1372 case AL_CONE_OUTER_ANGLE:
1373 case AL_PITCH:
1374 case AL_GAIN:
1375 case AL_MIN_GAIN:
1376 case AL_MAX_GAIN:
1377 case AL_REFERENCE_DISTANCE:
1378 case AL_ROLLOFF_FACTOR:
1379 case AL_CONE_OUTER_GAIN:
1380 case AL_MAX_DISTANCE:
1381 case AL_DOPPLER_FACTOR:
1382 case AL_CONE_OUTER_GAINHF:
1383 case AL_AIR_ABSORPTION_FACTOR:
1384 case AL_ROOM_ROLLOFF_FACTOR:
1385 case AL_SOURCE_RADIUS:
1386 CHECKSIZE(values, 1);
1387 fvals[0] = static_cast<ALfloat>(values[0]);
1388 return SetSourcefv(Source, Context, prop, {fvals, 1u});
1390 /* 3x float */
1391 case AL_POSITION:
1392 case AL_VELOCITY:
1393 case AL_DIRECTION:
1394 CHECKSIZE(values, 3);
1395 fvals[0] = static_cast<ALfloat>(values[0]);
1396 fvals[1] = static_cast<ALfloat>(values[1]);
1397 fvals[2] = static_cast<ALfloat>(values[2]);
1398 return SetSourcefv(Source, Context, prop, {fvals, 3u});
1400 /* 6x float */
1401 case AL_ORIENTATION:
1402 CHECKSIZE(values, 6);
1403 fvals[0] = static_cast<ALfloat>(values[0]);
1404 fvals[1] = static_cast<ALfloat>(values[1]);
1405 fvals[2] = static_cast<ALfloat>(values[2]);
1406 fvals[3] = static_cast<ALfloat>(values[3]);
1407 fvals[4] = static_cast<ALfloat>(values[4]);
1408 fvals[5] = static_cast<ALfloat>(values[5]);
1409 return SetSourcefv(Source, Context, prop, {fvals, 6u});
1411 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1412 case AL_SEC_OFFSET_LATENCY_SOFT:
1413 case AL_SEC_OFFSET_CLOCK_SOFT:
1414 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1415 case AL_STEREO_ANGLES:
1416 break;
1419 ERR("Unexpected property: 0x%04x\n", prop);
1420 Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
1421 return false;
1424 bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const ALint64SOFT> values)
1426 ALfloat fvals[MaxValues];
1427 ALint ivals[MaxValues];
1429 switch(prop)
1431 case AL_SOURCE_TYPE:
1432 case AL_BUFFERS_QUEUED:
1433 case AL_BUFFERS_PROCESSED:
1434 case AL_SOURCE_STATE:
1435 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1436 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1437 /* Query only */
1438 SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
1439 "Setting read-only source property 0x%04x", prop);
1441 /* 1x int */
1442 case AL_SOURCE_RELATIVE:
1443 case AL_LOOPING:
1444 case AL_SEC_OFFSET:
1445 case AL_SAMPLE_OFFSET:
1446 case AL_BYTE_OFFSET:
1447 case AL_DIRECT_FILTER_GAINHF_AUTO:
1448 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1449 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1450 case AL_DIRECT_CHANNELS_SOFT:
1451 case AL_DISTANCE_MODEL:
1452 case AL_SOURCE_RESAMPLER_SOFT:
1453 case AL_SOURCE_SPATIALIZE_SOFT:
1454 CHECKSIZE(values, 1);
1455 CHECKVAL(values[0] <= INT_MAX && values[0] >= INT_MIN);
1457 ivals[0] = static_cast<ALint>(values[0]);
1458 return SetSourceiv(Source, Context, prop, {ivals, 1u});
1460 /* 1x uint */
1461 case AL_BUFFER:
1462 case AL_DIRECT_FILTER:
1463 CHECKSIZE(values, 1);
1464 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0);
1466 ivals[0] = static_cast<ALint>(values[0]);
1467 return SetSourceiv(Source, Context, prop, {ivals, 1u});
1469 /* 3x uint */
1470 case AL_AUXILIARY_SEND_FILTER:
1471 CHECKSIZE(values, 3);
1472 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 && values[1] <= UINT_MAX && values[1] >= 0
1473 && values[2] <= UINT_MAX && values[2] >= 0);
1475 ivals[0] = static_cast<ALint>(values[0]);
1476 ivals[1] = static_cast<ALint>(values[1]);
1477 ivals[2] = static_cast<ALint>(values[2]);
1478 return SetSourceiv(Source, Context, prop, {ivals, 3u});
1480 /* 1x float */
1481 case AL_CONE_INNER_ANGLE:
1482 case AL_CONE_OUTER_ANGLE:
1483 case AL_PITCH:
1484 case AL_GAIN:
1485 case AL_MIN_GAIN:
1486 case AL_MAX_GAIN:
1487 case AL_REFERENCE_DISTANCE:
1488 case AL_ROLLOFF_FACTOR:
1489 case AL_CONE_OUTER_GAIN:
1490 case AL_MAX_DISTANCE:
1491 case AL_DOPPLER_FACTOR:
1492 case AL_CONE_OUTER_GAINHF:
1493 case AL_AIR_ABSORPTION_FACTOR:
1494 case AL_ROOM_ROLLOFF_FACTOR:
1495 case AL_SOURCE_RADIUS:
1496 CHECKSIZE(values, 1);
1497 fvals[0] = static_cast<ALfloat>(values[0]);
1498 return SetSourcefv(Source, Context, prop, {fvals, 1u});
1500 /* 3x float */
1501 case AL_POSITION:
1502 case AL_VELOCITY:
1503 case AL_DIRECTION:
1504 CHECKSIZE(values, 3);
1505 fvals[0] = static_cast<ALfloat>(values[0]);
1506 fvals[1] = static_cast<ALfloat>(values[1]);
1507 fvals[2] = static_cast<ALfloat>(values[2]);
1508 return SetSourcefv(Source, Context, prop, {fvals, 3u});
1510 /* 6x float */
1511 case AL_ORIENTATION:
1512 CHECKSIZE(values, 6);
1513 fvals[0] = static_cast<ALfloat>(values[0]);
1514 fvals[1] = static_cast<ALfloat>(values[1]);
1515 fvals[2] = static_cast<ALfloat>(values[2]);
1516 fvals[3] = static_cast<ALfloat>(values[3]);
1517 fvals[4] = static_cast<ALfloat>(values[4]);
1518 fvals[5] = static_cast<ALfloat>(values[5]);
1519 return SetSourcefv(Source, Context, prop, {fvals, 6u});
1521 case AL_SEC_OFFSET_LATENCY_SOFT:
1522 case AL_SEC_OFFSET_CLOCK_SOFT:
1523 case AL_STEREO_ANGLES:
1524 break;
1527 ERR("Unexpected property: 0x%04x\n", prop);
1528 Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
1529 return false;
1532 #undef CHECKVAL
1535 bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<ALdouble> values);
1536 bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<ALint> values);
1537 bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<ALint64SOFT> values);
1539 bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<ALdouble> values)
1541 ALCdevice *device{Context->mDevice.get()};
1542 ClockLatency clocktime;
1543 nanoseconds srcclock;
1544 ALint ivals[MaxValues];
1545 bool err;
1547 switch(prop)
1549 case AL_GAIN:
1550 CHECKSIZE(values, 1);
1551 values[0] = Source->Gain;
1552 return true;
1554 case AL_PITCH:
1555 CHECKSIZE(values, 1);
1556 values[0] = Source->Pitch;
1557 return true;
1559 case AL_MAX_DISTANCE:
1560 CHECKSIZE(values, 1);
1561 values[0] = Source->MaxDistance;
1562 return true;
1564 case AL_ROLLOFF_FACTOR:
1565 CHECKSIZE(values, 1);
1566 values[0] = Source->RolloffFactor;
1567 return true;
1569 case AL_REFERENCE_DISTANCE:
1570 CHECKSIZE(values, 1);
1571 values[0] = Source->RefDistance;
1572 return true;
1574 case AL_CONE_INNER_ANGLE:
1575 CHECKSIZE(values, 1);
1576 values[0] = Source->InnerAngle;
1577 return true;
1579 case AL_CONE_OUTER_ANGLE:
1580 CHECKSIZE(values, 1);
1581 values[0] = Source->OuterAngle;
1582 return true;
1584 case AL_MIN_GAIN:
1585 CHECKSIZE(values, 1);
1586 values[0] = Source->MinGain;
1587 return true;
1589 case AL_MAX_GAIN:
1590 CHECKSIZE(values, 1);
1591 values[0] = Source->MaxGain;
1592 return true;
1594 case AL_CONE_OUTER_GAIN:
1595 CHECKSIZE(values, 1);
1596 values[0] = Source->OuterGain;
1597 return true;
1599 case AL_SEC_OFFSET:
1600 case AL_SAMPLE_OFFSET:
1601 case AL_BYTE_OFFSET:
1602 CHECKSIZE(values, 1);
1603 values[0] = GetSourceOffset(Source, prop, Context);
1604 return true;
1606 case AL_CONE_OUTER_GAINHF:
1607 CHECKSIZE(values, 1);
1608 values[0] = Source->OuterGainHF;
1609 return true;
1611 case AL_AIR_ABSORPTION_FACTOR:
1612 CHECKSIZE(values, 1);
1613 values[0] = Source->AirAbsorptionFactor;
1614 return true;
1616 case AL_ROOM_ROLLOFF_FACTOR:
1617 CHECKSIZE(values, 1);
1618 values[0] = Source->RoomRolloffFactor;
1619 return true;
1621 case AL_DOPPLER_FACTOR:
1622 CHECKSIZE(values, 1);
1623 values[0] = Source->DopplerFactor;
1624 return true;
1626 case AL_SOURCE_RADIUS:
1627 CHECKSIZE(values, 1);
1628 values[0] = Source->Radius;
1629 return true;
1631 case AL_STEREO_ANGLES:
1632 CHECKSIZE(values, 2);
1633 values[0] = Source->StereoPan[0];
1634 values[1] = Source->StereoPan[1];
1635 return true;
1637 case AL_SEC_OFFSET_LATENCY_SOFT:
1638 CHECKSIZE(values, 2);
1639 /* Get the source offset with the clock time first. Then get the clock
1640 * time with the device latency. Order is important.
1642 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1644 std::lock_guard<std::mutex> _{device->StateLock};
1645 clocktime = GetClockLatency(device);
1647 if(srcclock == clocktime.ClockTime)
1648 values[1] = static_cast<ALdouble>(clocktime.Latency.count()) / 1000000000.0;
1649 else
1651 /* If the clock time incremented, reduce the latency by that much
1652 * since it's that much closer to the source offset it got earlier.
1654 const nanoseconds diff{clocktime.ClockTime - srcclock};
1655 const nanoseconds latency{clocktime.Latency - std::min(clocktime.Latency, diff)};
1656 values[1] = static_cast<ALdouble>(latency.count()) / 1000000000.0;
1658 return true;
1660 case AL_SEC_OFFSET_CLOCK_SOFT:
1661 CHECKSIZE(values, 2);
1662 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1663 values[1] = static_cast<ALdouble>(srcclock.count()) / 1000000000.0;
1664 return true;
1666 case AL_POSITION:
1667 CHECKSIZE(values, 3);
1668 values[0] = Source->Position[0];
1669 values[1] = Source->Position[1];
1670 values[2] = Source->Position[2];
1671 return true;
1673 case AL_VELOCITY:
1674 CHECKSIZE(values, 3);
1675 values[0] = Source->Velocity[0];
1676 values[1] = Source->Velocity[1];
1677 values[2] = Source->Velocity[2];
1678 return true;
1680 case AL_DIRECTION:
1681 CHECKSIZE(values, 3);
1682 values[0] = Source->Direction[0];
1683 values[1] = Source->Direction[1];
1684 values[2] = Source->Direction[2];
1685 return true;
1687 case AL_ORIENTATION:
1688 CHECKSIZE(values, 6);
1689 values[0] = Source->OrientAt[0];
1690 values[1] = Source->OrientAt[1];
1691 values[2] = Source->OrientAt[2];
1692 values[3] = Source->OrientUp[0];
1693 values[4] = Source->OrientUp[1];
1694 values[5] = Source->OrientUp[2];
1695 return true;
1697 /* 1x int */
1698 case AL_SOURCE_RELATIVE:
1699 case AL_LOOPING:
1700 case AL_SOURCE_STATE:
1701 case AL_BUFFERS_QUEUED:
1702 case AL_BUFFERS_PROCESSED:
1703 case AL_SOURCE_TYPE:
1704 case AL_DIRECT_FILTER_GAINHF_AUTO:
1705 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1706 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1707 case AL_DIRECT_CHANNELS_SOFT:
1708 case AL_DISTANCE_MODEL:
1709 case AL_SOURCE_RESAMPLER_SOFT:
1710 case AL_SOURCE_SPATIALIZE_SOFT:
1711 CHECKSIZE(values, 1);
1712 if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
1713 values[0] = static_cast<ALdouble>(ivals[0]);
1714 return err;
1716 case AL_BUFFER:
1717 case AL_DIRECT_FILTER:
1718 case AL_AUXILIARY_SEND_FILTER:
1719 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1720 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1721 break;
1724 ERR("Unexpected property: 0x%04x\n", prop);
1725 Context->setError(AL_INVALID_ENUM, "Invalid source double property 0x%04x", prop);
1726 return false;
1729 bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<ALint> values)
1731 ALdouble dvals[MaxValues];
1732 bool err;
1734 switch(prop)
1736 case AL_SOURCE_RELATIVE:
1737 CHECKSIZE(values, 1);
1738 values[0] = Source->HeadRelative;
1739 return true;
1741 case AL_LOOPING:
1742 CHECKSIZE(values, 1);
1743 values[0] = Source->Looping;
1744 return true;
1746 case AL_BUFFER:
1747 CHECKSIZE(values, 1);
1749 ALbufferlistitem *BufferList{nullptr};
1750 if(Source->SourceType == AL_STATIC) BufferList = Source->queue;
1751 ALbuffer *buffer{nullptr};
1752 if(BufferList) buffer = BufferList->mBuffer;
1753 values[0] = buffer ? static_cast<ALint>(buffer->id) : 0;
1755 return true;
1757 case AL_SOURCE_STATE:
1758 CHECKSIZE(values, 1);
1759 values[0] = GetSourceState(Source, GetSourceVoice(Source, Context));
1760 return true;
1762 case AL_BUFFERS_QUEUED:
1763 CHECKSIZE(values, 1);
1764 if(ALbufferlistitem *BufferList{Source->queue})
1766 ALsizei count{0};
1767 do {
1768 ++count;
1769 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
1770 } while(BufferList != nullptr);
1771 values[0] = count;
1773 else
1774 values[0] = 0;
1775 return true;
1777 case AL_BUFFERS_PROCESSED:
1778 CHECKSIZE(values, 1);
1779 if(Source->Looping || Source->SourceType != AL_STREAMING)
1781 /* Buffers on a looping source are in a perpetual state of PENDING,
1782 * so don't report any as PROCESSED
1784 values[0] = 0;
1786 else
1788 const ALbufferlistitem *BufferList{Source->queue};
1789 const ALbufferlistitem *Current{nullptr};
1790 ALsizei played{0};
1792 ALvoice *voice{GetSourceVoice(Source, Context)};
1793 if(voice != nullptr)
1794 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
1795 else if(Source->state == AL_INITIAL)
1796 Current = BufferList;
1798 while(BufferList && BufferList != Current)
1800 ++played;
1801 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
1803 values[0] = played;
1805 return true;
1807 case AL_SOURCE_TYPE:
1808 CHECKSIZE(values, 1);
1809 values[0] = Source->SourceType;
1810 return true;
1812 case AL_DIRECT_FILTER_GAINHF_AUTO:
1813 CHECKSIZE(values, 1);
1814 values[0] = Source->DryGainHFAuto;
1815 return true;
1817 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1818 CHECKSIZE(values, 1);
1819 values[0] = Source->WetGainAuto;
1820 return true;
1822 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1823 CHECKSIZE(values, 1);
1824 values[0] = Source->WetGainHFAuto;
1825 return true;
1827 case AL_DIRECT_CHANNELS_SOFT:
1828 CHECKSIZE(values, 1);
1829 values[0] = Source->DirectChannels;
1830 return true;
1832 case AL_DISTANCE_MODEL:
1833 CHECKSIZE(values, 1);
1834 values[0] = static_cast<int>(Source->mDistanceModel);
1835 return true;
1837 case AL_SOURCE_RESAMPLER_SOFT:
1838 CHECKSIZE(values, 1);
1839 values[0] = static_cast<int>(Source->mResampler);
1840 return true;
1842 case AL_SOURCE_SPATIALIZE_SOFT:
1843 CHECKSIZE(values, 1);
1844 values[0] = Source->mSpatialize;
1845 return true;
1847 /* 1x float/double */
1848 case AL_CONE_INNER_ANGLE:
1849 case AL_CONE_OUTER_ANGLE:
1850 case AL_PITCH:
1851 case AL_GAIN:
1852 case AL_MIN_GAIN:
1853 case AL_MAX_GAIN:
1854 case AL_REFERENCE_DISTANCE:
1855 case AL_ROLLOFF_FACTOR:
1856 case AL_CONE_OUTER_GAIN:
1857 case AL_MAX_DISTANCE:
1858 case AL_SEC_OFFSET:
1859 case AL_SAMPLE_OFFSET:
1860 case AL_BYTE_OFFSET:
1861 case AL_DOPPLER_FACTOR:
1862 case AL_AIR_ABSORPTION_FACTOR:
1863 case AL_ROOM_ROLLOFF_FACTOR:
1864 case AL_CONE_OUTER_GAINHF:
1865 case AL_SOURCE_RADIUS:
1866 CHECKSIZE(values, 1);
1867 if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
1868 values[0] = static_cast<ALint>(dvals[0]);
1869 return err;
1871 /* 3x float/double */
1872 case AL_POSITION:
1873 case AL_VELOCITY:
1874 case AL_DIRECTION:
1875 CHECKSIZE(values, 3);
1876 if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
1878 values[0] = static_cast<ALint>(dvals[0]);
1879 values[1] = static_cast<ALint>(dvals[1]);
1880 values[2] = static_cast<ALint>(dvals[2]);
1882 return err;
1884 /* 6x float/double */
1885 case AL_ORIENTATION:
1886 CHECKSIZE(values, 6);
1887 if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
1889 values[0] = static_cast<ALint>(dvals[0]);
1890 values[1] = static_cast<ALint>(dvals[1]);
1891 values[2] = static_cast<ALint>(dvals[2]);
1892 values[3] = static_cast<ALint>(dvals[3]);
1893 values[4] = static_cast<ALint>(dvals[4]);
1894 values[5] = static_cast<ALint>(dvals[5]);
1896 return err;
1898 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1899 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1900 break; /* i64 only */
1901 case AL_SEC_OFFSET_LATENCY_SOFT:
1902 case AL_SEC_OFFSET_CLOCK_SOFT:
1903 break; /* Double only */
1904 case AL_STEREO_ANGLES:
1905 break; /* Float/double only */
1907 case AL_DIRECT_FILTER:
1908 case AL_AUXILIARY_SEND_FILTER:
1909 break; /* ??? */
1912 ERR("Unexpected property: 0x%04x\n", prop);
1913 Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
1914 return false;
1917 bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<ALint64SOFT> values)
1919 ALCdevice *device = Context->mDevice.get();
1920 ClockLatency clocktime;
1921 nanoseconds srcclock;
1922 ALdouble dvals[MaxValues];
1923 ALint ivals[MaxValues];
1924 bool err;
1926 switch(prop)
1928 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1929 CHECKSIZE(values, 2);
1930 /* Get the source offset with the clock time first. Then get the clock
1931 * time with the device latency. Order is important.
1933 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
1935 std::lock_guard<std::mutex> _{device->StateLock};
1936 clocktime = GetClockLatency(device);
1938 if(srcclock == clocktime.ClockTime)
1939 values[1] = clocktime.Latency.count();
1940 else
1942 /* If the clock time incremented, reduce the latency by that much
1943 * since it's that much closer to the source offset it got earlier.
1945 const nanoseconds diff{clocktime.ClockTime - srcclock};
1946 values[1] = nanoseconds{clocktime.Latency - std::min(clocktime.Latency, diff)}.count();
1948 return true;
1950 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1951 CHECKSIZE(values, 2);
1952 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
1953 values[1] = srcclock.count();
1954 return true;
1956 /* 1x float/double */
1957 case AL_CONE_INNER_ANGLE:
1958 case AL_CONE_OUTER_ANGLE:
1959 case AL_PITCH:
1960 case AL_GAIN:
1961 case AL_MIN_GAIN:
1962 case AL_MAX_GAIN:
1963 case AL_REFERENCE_DISTANCE:
1964 case AL_ROLLOFF_FACTOR:
1965 case AL_CONE_OUTER_GAIN:
1966 case AL_MAX_DISTANCE:
1967 case AL_SEC_OFFSET:
1968 case AL_SAMPLE_OFFSET:
1969 case AL_BYTE_OFFSET:
1970 case AL_DOPPLER_FACTOR:
1971 case AL_AIR_ABSORPTION_FACTOR:
1972 case AL_ROOM_ROLLOFF_FACTOR:
1973 case AL_CONE_OUTER_GAINHF:
1974 case AL_SOURCE_RADIUS:
1975 CHECKSIZE(values, 1);
1976 if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
1977 values[0] = static_cast<int64_t>(dvals[0]);
1978 return err;
1980 /* 3x float/double */
1981 case AL_POSITION:
1982 case AL_VELOCITY:
1983 case AL_DIRECTION:
1984 CHECKSIZE(values, 3);
1985 if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
1987 values[0] = static_cast<int64_t>(dvals[0]);
1988 values[1] = static_cast<int64_t>(dvals[1]);
1989 values[2] = static_cast<int64_t>(dvals[2]);
1991 return err;
1993 /* 6x float/double */
1994 case AL_ORIENTATION:
1995 CHECKSIZE(values, 6);
1996 if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
1998 values[0] = static_cast<int64_t>(dvals[0]);
1999 values[1] = static_cast<int64_t>(dvals[1]);
2000 values[2] = static_cast<int64_t>(dvals[2]);
2001 values[3] = static_cast<int64_t>(dvals[3]);
2002 values[4] = static_cast<int64_t>(dvals[4]);
2003 values[5] = static_cast<int64_t>(dvals[5]);
2005 return err;
2007 /* 1x int */
2008 case AL_SOURCE_RELATIVE:
2009 case AL_LOOPING:
2010 case AL_SOURCE_STATE:
2011 case AL_BUFFERS_QUEUED:
2012 case AL_BUFFERS_PROCESSED:
2013 case AL_SOURCE_TYPE:
2014 case AL_DIRECT_FILTER_GAINHF_AUTO:
2015 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2016 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2017 case AL_DIRECT_CHANNELS_SOFT:
2018 case AL_DISTANCE_MODEL:
2019 case AL_SOURCE_RESAMPLER_SOFT:
2020 case AL_SOURCE_SPATIALIZE_SOFT:
2021 CHECKSIZE(values, 1);
2022 if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2023 values[0] = ivals[0];
2024 return err;
2026 /* 1x uint */
2027 case AL_BUFFER:
2028 case AL_DIRECT_FILTER:
2029 CHECKSIZE(values, 1);
2030 if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
2031 values[0] = static_cast<ALuint>(ivals[0]);
2032 return err;
2034 /* 3x uint */
2035 case AL_AUXILIARY_SEND_FILTER:
2036 CHECKSIZE(values, 3);
2037 if((err=GetSourceiv(Source, Context, prop, {ivals, 3u})) != false)
2039 values[0] = static_cast<ALuint>(ivals[0]);
2040 values[1] = static_cast<ALuint>(ivals[1]);
2041 values[2] = static_cast<ALuint>(ivals[2]);
2043 return err;
2045 case AL_SEC_OFFSET_LATENCY_SOFT:
2046 case AL_SEC_OFFSET_CLOCK_SOFT:
2047 break; /* Double only */
2048 case AL_STEREO_ANGLES:
2049 break; /* Float/double only */
2052 ERR("Unexpected property: 0x%04x\n", prop);
2053 Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
2054 return false;
2057 } // namespace
2059 AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
2060 START_API_FUNC
2062 ContextRef context{GetContextRef()};
2063 if UNLIKELY(!context) return;
2065 if UNLIKELY(n < 0)
2066 context->setError(AL_INVALID_VALUE, "Generating %d sources", n);
2067 if UNLIKELY(n <= 0) return;
2069 std::unique_lock<std::mutex> srclock{context->mSourceLock};
2070 ALCdevice *device{context->mDevice.get()};
2071 if(static_cast<ALuint>(n) > device->SourcesMax-context->mNumSources)
2073 context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)",
2074 device->SourcesMax, context->mNumSources, n);
2075 return;
2077 if(!EnsureSources(context.get(), static_cast<ALuint>(n)))
2079 context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, (n==1)?"":"s");
2080 return;
2083 if(n == 1)
2085 ALsource *source{AllocSource(context.get(), device->NumAuxSends)};
2086 sources[0] = source->id;
2088 else
2090 const ALuint num_sends{device->NumAuxSends};
2091 al::vector<ALuint> ids;
2092 ids.reserve(static_cast<ALuint>(n));
2093 do {
2094 ALsource *source{AllocSource(context.get(), num_sends)};
2095 ids.emplace_back(source->id);
2096 } while(--n);
2097 std::copy(ids.cbegin(), ids.cend(), sources);
2100 END_API_FUNC
2102 AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
2103 START_API_FUNC
2105 ContextRef context{GetContextRef()};
2106 if UNLIKELY(!context) return;
2108 if UNLIKELY(n < 0)
2109 SETERR_RETURN(context, AL_INVALID_VALUE,, "Deleting %d sources", n);
2111 std::lock_guard<std::mutex> _{context->mSourceLock};
2113 /* Check that all Sources are valid */
2114 auto validate_source = [&context](const ALuint sid) -> bool
2115 { return LookupSource(context.get(), sid) != nullptr; };
2117 const ALuint *sources_end = sources + n;
2118 auto invsrc = std::find_if_not(sources, sources_end, validate_source);
2119 if UNLIKELY(invsrc != sources_end)
2121 context->setError(AL_INVALID_NAME, "Invalid source ID %u", *invsrc);
2122 return;
2125 /* All good. Delete source IDs. */
2126 auto delete_source = [&context](const ALuint sid) -> void
2128 ALsource *src{LookupSource(context.get(), sid)};
2129 if(src) FreeSource(context.get(), src);
2131 std::for_each(sources, sources_end, delete_source);
2133 END_API_FUNC
2135 AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
2136 START_API_FUNC
2138 ContextRef context{GetContextRef()};
2139 if LIKELY(context)
2141 std::lock_guard<std::mutex> _{context->mSourceLock};
2142 if(LookupSource(context.get(), source) != nullptr)
2143 return AL_TRUE;
2145 return AL_FALSE;
2147 END_API_FUNC
2150 AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
2151 START_API_FUNC
2153 ContextRef context{GetContextRef()};
2154 if UNLIKELY(!context) return;
2156 std::lock_guard<std::mutex> _{context->mPropLock};
2157 std::lock_guard<std::mutex> __{context->mSourceLock};
2158 ALsource *Source = LookupSource(context.get(), source);
2159 if UNLIKELY(!Source)
2160 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2161 else
2162 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2164 END_API_FUNC
2166 AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
2167 START_API_FUNC
2169 ContextRef context{GetContextRef()};
2170 if UNLIKELY(!context) return;
2172 std::lock_guard<std::mutex> _{context->mPropLock};
2173 std::lock_guard<std::mutex> __{context->mSourceLock};
2174 ALsource *Source = LookupSource(context.get(), source);
2175 if UNLIKELY(!Source)
2176 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2177 else
2179 const ALfloat fvals[3]{ value1, value2, value3 };
2180 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2183 END_API_FUNC
2185 AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
2186 START_API_FUNC
2188 ContextRef context{GetContextRef()};
2189 if UNLIKELY(!context) return;
2191 std::lock_guard<std::mutex> _{context->mPropLock};
2192 std::lock_guard<std::mutex> __{context->mSourceLock};
2193 ALsource *Source = LookupSource(context.get(), source);
2194 if UNLIKELY(!Source)
2195 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2196 else if UNLIKELY(!values)
2197 context->setError(AL_INVALID_VALUE, "NULL pointer");
2198 else
2199 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2201 END_API_FUNC
2204 AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
2205 START_API_FUNC
2207 ContextRef context{GetContextRef()};
2208 if UNLIKELY(!context) return;
2210 std::lock_guard<std::mutex> _{context->mPropLock};
2211 std::lock_guard<std::mutex> __{context->mSourceLock};
2212 ALsource *Source = LookupSource(context.get(), source);
2213 if UNLIKELY(!Source)
2214 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2215 else
2217 const ALfloat fval[1]{static_cast<ALfloat>(value)};
2218 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fval);
2221 END_API_FUNC
2223 AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
2224 START_API_FUNC
2226 ContextRef context{GetContextRef()};
2227 if UNLIKELY(!context) return;
2229 std::lock_guard<std::mutex> _{context->mPropLock};
2230 std::lock_guard<std::mutex> __{context->mSourceLock};
2231 ALsource *Source = LookupSource(context.get(), source);
2232 if UNLIKELY(!Source)
2233 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2234 else
2236 const ALfloat fvals[3]{static_cast<ALfloat>(value1), static_cast<ALfloat>(value2),
2237 static_cast<ALfloat>(value3)};
2238 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2241 END_API_FUNC
2243 AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
2244 START_API_FUNC
2246 ContextRef context{GetContextRef()};
2247 if UNLIKELY(!context) return;
2249 std::lock_guard<std::mutex> _{context->mPropLock};
2250 std::lock_guard<std::mutex> __{context->mSourceLock};
2251 ALsource *Source = LookupSource(context.get(), source);
2252 if UNLIKELY(!Source)
2253 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2254 else if UNLIKELY(!values)
2255 context->setError(AL_INVALID_VALUE, "NULL pointer");
2256 else
2258 const ALuint count{DoubleValsByProp(param)};
2259 ALfloat fvals[MaxValues];
2260 for(ALuint i{0};i < count;i++)
2261 fvals[i] = static_cast<ALfloat>(values[i]);
2262 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {fvals, count});
2265 END_API_FUNC
2268 AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
2269 START_API_FUNC
2271 ContextRef context{GetContextRef()};
2272 if UNLIKELY(!context) return;
2274 std::lock_guard<std::mutex> _{context->mPropLock};
2275 std::lock_guard<std::mutex> __{context->mSourceLock};
2276 ALsource *Source = LookupSource(context.get(), source);
2277 if UNLIKELY(!Source)
2278 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2279 else
2280 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2282 END_API_FUNC
2284 AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
2285 START_API_FUNC
2287 ContextRef context{GetContextRef()};
2288 if UNLIKELY(!context) return;
2290 std::lock_guard<std::mutex> _{context->mPropLock};
2291 std::lock_guard<std::mutex> __{context->mSourceLock};
2292 ALsource *Source = LookupSource(context.get(), source);
2293 if UNLIKELY(!Source)
2294 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2295 else
2297 const ALint ivals[3]{ value1, value2, value3 };
2298 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals);
2301 END_API_FUNC
2303 AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
2304 START_API_FUNC
2306 ContextRef context{GetContextRef()};
2307 if UNLIKELY(!context) return;
2309 std::lock_guard<std::mutex> _{context->mPropLock};
2310 std::lock_guard<std::mutex> __{context->mSourceLock};
2311 ALsource *Source = LookupSource(context.get(), source);
2312 if UNLIKELY(!Source)
2313 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2314 else if UNLIKELY(!values)
2315 context->setError(AL_INVALID_VALUE, "NULL pointer");
2316 else
2317 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2319 END_API_FUNC
2322 AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
2323 START_API_FUNC
2325 ContextRef context{GetContextRef()};
2326 if UNLIKELY(!context) return;
2328 std::lock_guard<std::mutex> _{context->mPropLock};
2329 std::lock_guard<std::mutex> __{context->mSourceLock};
2330 ALsource *Source{LookupSource(context.get(), source)};
2331 if UNLIKELY(!Source)
2332 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2333 else
2334 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
2336 END_API_FUNC
2338 AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
2339 START_API_FUNC
2341 ContextRef context{GetContextRef()};
2342 if UNLIKELY(!context) return;
2344 std::lock_guard<std::mutex> _{context->mPropLock};
2345 std::lock_guard<std::mutex> __{context->mSourceLock};
2346 ALsource *Source{LookupSource(context.get(), source)};
2347 if UNLIKELY(!Source)
2348 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2349 else
2351 const ALint64SOFT i64vals[3]{ value1, value2, value3 };
2352 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals);
2355 END_API_FUNC
2357 AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
2358 START_API_FUNC
2360 ContextRef context{GetContextRef()};
2361 if UNLIKELY(!context) return;
2363 std::lock_guard<std::mutex> _{context->mPropLock};
2364 std::lock_guard<std::mutex> __{context->mSourceLock};
2365 ALsource *Source{LookupSource(context.get(), source)};
2366 if UNLIKELY(!Source)
2367 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2368 else if UNLIKELY(!values)
2369 context->setError(AL_INVALID_VALUE, "NULL pointer");
2370 else
2371 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2373 END_API_FUNC
2376 AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
2377 START_API_FUNC
2379 ContextRef context{GetContextRef()};
2380 if UNLIKELY(!context) return;
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 if UNLIKELY(!value)
2387 context->setError(AL_INVALID_VALUE, "NULL pointer");
2388 else
2390 ALdouble dval[1];
2391 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dval))
2392 *value = static_cast<ALfloat>(dval[0]);
2395 END_API_FUNC
2397 AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
2398 START_API_FUNC
2400 ContextRef context{GetContextRef()};
2401 if UNLIKELY(!context) return;
2403 std::lock_guard<std::mutex> _{context->mSourceLock};
2404 ALsource *Source{LookupSource(context.get(), source)};
2405 if UNLIKELY(!Source)
2406 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2407 else if UNLIKELY(!(value1 && value2 && value3))
2408 context->setError(AL_INVALID_VALUE, "NULL pointer");
2409 else
2411 ALdouble dvals[3];
2412 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2414 *value1 = static_cast<ALfloat>(dvals[0]);
2415 *value2 = static_cast<ALfloat>(dvals[1]);
2416 *value3 = static_cast<ALfloat>(dvals[2]);
2420 END_API_FUNC
2422 AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
2423 START_API_FUNC
2425 ContextRef context{GetContextRef()};
2426 if UNLIKELY(!context) return;
2428 std::lock_guard<std::mutex> _{context->mSourceLock};
2429 ALsource *Source{LookupSource(context.get(), source)};
2430 if UNLIKELY(!Source)
2431 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2432 else if UNLIKELY(!values)
2433 context->setError(AL_INVALID_VALUE, "NULL pointer");
2434 else
2436 const ALuint count{FloatValsByProp(param)};
2437 ALdouble dvals[MaxValues];
2438 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {dvals, count}))
2440 for(ALuint i{0};i < count;i++)
2441 values[i] = static_cast<ALfloat>(dvals[i]);
2445 END_API_FUNC
2448 AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
2449 START_API_FUNC
2451 ContextRef context{GetContextRef()};
2452 if UNLIKELY(!context) return;
2454 std::lock_guard<std::mutex> _{context->mSourceLock};
2455 ALsource *Source{LookupSource(context.get(), source)};
2456 if UNLIKELY(!Source)
2457 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2458 else if UNLIKELY(!value)
2459 context->setError(AL_INVALID_VALUE, "NULL pointer");
2460 else
2461 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2463 END_API_FUNC
2465 AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
2466 START_API_FUNC
2468 ContextRef context{GetContextRef()};
2469 if UNLIKELY(!context) return;
2471 std::lock_guard<std::mutex> _{context->mSourceLock};
2472 ALsource *Source{LookupSource(context.get(), source)};
2473 if UNLIKELY(!Source)
2474 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2475 else if UNLIKELY(!(value1 && value2 && value3))
2476 context->setError(AL_INVALID_VALUE, "NULL pointer");
2477 else
2479 ALdouble dvals[3];
2480 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2482 *value1 = dvals[0];
2483 *value2 = dvals[1];
2484 *value3 = dvals[2];
2488 END_API_FUNC
2490 AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
2491 START_API_FUNC
2493 ContextRef context{GetContextRef()};
2494 if UNLIKELY(!context) return;
2496 std::lock_guard<std::mutex> _{context->mSourceLock};
2497 ALsource *Source{LookupSource(context.get(), source)};
2498 if UNLIKELY(!Source)
2499 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2500 else if UNLIKELY(!values)
2501 context->setError(AL_INVALID_VALUE, "NULL pointer");
2502 else
2503 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2505 END_API_FUNC
2508 AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
2509 START_API_FUNC
2511 ContextRef context{GetContextRef()};
2512 if UNLIKELY(!context) return;
2514 std::lock_guard<std::mutex> _{context->mSourceLock};
2515 ALsource *Source{LookupSource(context.get(), source)};
2516 if UNLIKELY(!Source)
2517 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2518 else if UNLIKELY(!value)
2519 context->setError(AL_INVALID_VALUE, "NULL pointer");
2520 else
2521 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2523 END_API_FUNC
2525 AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
2526 START_API_FUNC
2528 ContextRef context{GetContextRef()};
2529 if UNLIKELY(!context) return;
2531 std::lock_guard<std::mutex> _{context->mSourceLock};
2532 ALsource *Source{LookupSource(context.get(), source)};
2533 if UNLIKELY(!Source)
2534 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2535 else if UNLIKELY(!(value1 && value2 && value3))
2536 context->setError(AL_INVALID_VALUE, "NULL pointer");
2537 else
2539 ALint ivals[3];
2540 if(GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals))
2542 *value1 = ivals[0];
2543 *value2 = ivals[1];
2544 *value3 = ivals[2];
2548 END_API_FUNC
2550 AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
2551 START_API_FUNC
2553 ContextRef context{GetContextRef()};
2554 if UNLIKELY(!context) return;
2556 std::lock_guard<std::mutex> _{context->mSourceLock};
2557 ALsource *Source{LookupSource(context.get(), source)};
2558 if UNLIKELY(!Source)
2559 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2560 else if UNLIKELY(!values)
2561 context->setError(AL_INVALID_VALUE, "NULL pointer");
2562 else
2563 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2565 END_API_FUNC
2568 AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
2569 START_API_FUNC
2571 ContextRef context{GetContextRef()};
2572 if UNLIKELY(!context) return;
2574 std::lock_guard<std::mutex> _{context->mSourceLock};
2575 ALsource *Source{LookupSource(context.get(), source)};
2576 if UNLIKELY(!Source)
2577 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2578 else if UNLIKELY(!value)
2579 context->setError(AL_INVALID_VALUE, "NULL pointer");
2580 else
2581 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
2583 END_API_FUNC
2585 AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
2586 START_API_FUNC
2588 ContextRef context{GetContextRef()};
2589 if UNLIKELY(!context) return;
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(!(value1 && value2 && value3))
2596 context->setError(AL_INVALID_VALUE, "NULL pointer");
2597 else
2599 ALint64SOFT i64vals[3];
2600 if(GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals))
2602 *value1 = i64vals[0];
2603 *value2 = i64vals[1];
2604 *value3 = i64vals[2];
2608 END_API_FUNC
2610 AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
2611 START_API_FUNC
2613 ContextRef context{GetContextRef()};
2614 if UNLIKELY(!context) return;
2616 std::lock_guard<std::mutex> _{context->mSourceLock};
2617 ALsource *Source{LookupSource(context.get(), source)};
2618 if UNLIKELY(!Source)
2619 context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
2620 else if UNLIKELY(!values)
2621 context->setError(AL_INVALID_VALUE, "NULL pointer");
2622 else
2623 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
2625 END_API_FUNC
2628 AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source)
2629 START_API_FUNC
2630 { alSourcePlayv(1, &source); }
2631 END_API_FUNC
2633 AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
2634 START_API_FUNC
2636 ContextRef context{GetContextRef()};
2637 if UNLIKELY(!context) return;
2639 if UNLIKELY(n < 0)
2640 context->setError(AL_INVALID_VALUE, "Playing %d sources", n);
2641 if UNLIKELY(n <= 0) return;
2643 al::vector<ALsource*> extra_sources;
2644 std::array<ALsource*,8> source_storage;
2645 al::span<ALsource*> srchandles;
2646 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
2647 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
2648 else
2650 extra_sources.resize(static_cast<ALuint>(n));
2651 srchandles = {extra_sources.data(), extra_sources.size()};
2654 std::lock_guard<std::mutex> _{context->mSourceLock};
2655 for(auto &srchdl : srchandles)
2657 srchdl = LookupSource(context.get(), *sources);
2658 if(!srchdl)
2659 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
2660 ++sources;
2663 ALCdevice *device{context->mDevice.get()};
2664 BackendLockGuard __{*device->Backend};
2665 /* If the device is disconnected, go right to stopped. */
2666 if UNLIKELY(!device->Connected.load(std::memory_order_acquire))
2668 /* TODO: Send state change event? */
2669 std::for_each(srchandles.begin(), srchandles.end(),
2670 [](ALsource *source) -> void
2672 source->OffsetType = AL_NONE;
2673 source->Offset = 0.0;
2674 source->state = AL_STOPPED;
2677 return;
2680 /* Count the number of reusable voices. */
2681 auto count_free_voices = [](const ALuint count, const ALvoice &voice) noexcept -> ALuint
2683 if(voice.mPlayState.load(std::memory_order_acquire) == ALvoice::Stopped
2684 && voice.mSourceID.load(std::memory_order_relaxed) == 0u)
2685 return count + 1;
2686 return count;
2688 auto free_voices = std::accumulate(context->mVoices.begin(), context->mVoices.end(),
2689 ALuint{0}, count_free_voices);
2690 if UNLIKELY(srchandles.size() > free_voices)
2692 /* Increase the number of voices to handle the request. */
2693 const size_t need_voices{srchandles.size() - free_voices};
2694 context->mVoices.resize(context->mVoices.size() + need_voices);
2697 auto start_source = [&context,device](ALsource *source) -> void
2699 /* Check that there is a queue containing at least one valid, non zero
2700 * length buffer.
2702 ALbufferlistitem *BufferList{source->queue};
2703 while(BufferList && BufferList->mSampleLen == 0)
2704 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
2706 /* If there's nothing to play, go right to stopped. */
2707 if UNLIKELY(!BufferList)
2709 /* NOTE: A source without any playable buffers should not have an
2710 * ALvoice since it shouldn't be in a playing or paused state. So
2711 * there's no need to look up its voice and clear the source.
2713 ALenum oldstate{GetSourceState(source, nullptr)};
2714 source->OffsetType = AL_NONE;
2715 source->Offset = 0.0;
2716 if(oldstate != AL_STOPPED)
2718 source->state = AL_STOPPED;
2719 SendStateChangeEvent(context.get(), source->id, AL_STOPPED);
2721 return;
2724 ALvoice *voice{GetSourceVoice(source, context.get())};
2725 switch(GetSourceState(source, voice))
2727 case AL_PAUSED:
2728 assert(voice != nullptr);
2729 /* A source that's paused simply resumes. */
2730 voice->mPlayState.store(ALvoice::Playing, std::memory_order_release);
2731 source->state = AL_PLAYING;
2732 SendStateChangeEvent(context.get(), source->id, AL_PLAYING);
2733 return;
2735 case AL_PLAYING:
2736 assert(voice != nullptr);
2737 /* A source that's already playing is restarted from the beginning.
2738 * Stop the current voice and start a new one so it properly cross-
2739 * fades back to the beginning.
2741 voice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
2742 voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
2743 voice->mSourceID.store(0u, std::memory_order_release);
2744 voice->mPlayState.store(ALvoice::Stopping, std::memory_order_release);
2745 voice = nullptr;
2746 break;
2748 default:
2749 assert(voice == nullptr);
2750 break;
2753 /* Look for an unused voice to play this source with. */
2754 auto find_voice = [](const ALvoice &v) noexcept -> bool
2756 return v.mPlayState.load(std::memory_order_acquire) == ALvoice::Stopped
2757 && v.mSourceID.load(std::memory_order_relaxed) == 0u;
2759 auto voices_end = context->mVoices.data() + context->mVoices.size();
2760 voice = std::find_if(context->mVoices.data(), voices_end, find_voice);
2761 assert(voice != voices_end);
2763 auto vidx = static_cast<ALuint>(std::distance(context->mVoices.data(), voice));
2764 voice->mPlayState.store(ALvoice::Stopped, std::memory_order_release);
2766 source->PropsClean.test_and_set(std::memory_order_acquire);
2767 UpdateSourceProps(source, voice, context.get());
2769 /* A source that's not playing or paused has any offset applied when it
2770 * starts playing.
2772 if(source->Looping)
2773 voice->mLoopBuffer.store(source->queue, std::memory_order_relaxed);
2774 else
2775 voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
2776 voice->mCurrentBuffer.store(BufferList, std::memory_order_relaxed);
2777 voice->mPosition.store(0u, std::memory_order_relaxed);
2778 voice->mPositionFrac.store(0, std::memory_order_relaxed);
2779 bool start_fading{false};
2780 if(auto vpos = GetSampleOffset(source))
2782 start_fading = vpos->pos != 0 || vpos->frac != 0 || vpos->bufferitem != BufferList;
2783 voice->mPosition.store(vpos->pos, std::memory_order_relaxed);
2784 voice->mPositionFrac.store(vpos->frac, std::memory_order_relaxed);
2785 voice->mCurrentBuffer.store(vpos->bufferitem, std::memory_order_relaxed);
2788 ALbuffer *buffer{BufferList->mBuffer};
2789 voice->mFrequency = buffer->Frequency;
2790 voice->mFmtChannels = buffer->mFmtChannels;
2791 voice->mNumChannels = ChannelsFromFmt(buffer->mFmtChannels);
2792 voice->mSampleSize = BytesFromFmt(buffer->mFmtType);
2793 voice->mAmbiLayout = static_cast<AmbiLayout>(buffer->AmbiLayout);
2794 voice->mAmbiScaling = static_cast<AmbiNorm>(buffer->AmbiScaling);
2795 voice->mAmbiOrder = 1;
2797 /* Clear the stepping value so the mixer knows not to mix this until
2798 * the update gets applied.
2800 voice->mStep = 0;
2802 voice->mFlags = start_fading ? VOICE_IS_FADING : 0;
2803 if(source->SourceType == AL_STATIC) voice->mFlags |= VOICE_IS_STATIC;
2805 /* Don't need to set the VOICE_IS_AMBISONIC flag if the device is not
2806 * higher order than the voice. No HF scaling is necessary to mix it.
2808 if((voice->mFmtChannels == FmtBFormat2D || voice->mFmtChannels == FmtBFormat3D)
2809 && device->mAmbiOrder > voice->mAmbiOrder)
2811 const ALuint *OrderFromChan;
2812 if(voice->mFmtChannels == FmtBFormat2D)
2814 static const ALuint Order2DFromChan[MAX_AMBI2D_CHANNELS]{
2815 0, 1,1, 2,2, 3,3,};
2816 OrderFromChan = Order2DFromChan;
2818 else
2820 static const ALuint Order3DFromChan[MAX_AMBI_CHANNELS]{
2821 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3,};
2822 OrderFromChan = Order3DFromChan;
2825 const BandSplitter splitter{400.0f / static_cast<float>(device->Frequency)};
2827 const auto scales = BFormatDec::GetHFOrderScales(voice->mAmbiOrder,
2828 device->mAmbiOrder);
2829 auto init_ambi = [device,&scales,&OrderFromChan,splitter](ALvoice::ChannelData &chandata) -> void
2831 chandata.mPrevSamples.fill(0.0f);
2832 chandata.mAmbiScale = scales[*(OrderFromChan++)];
2833 chandata.mAmbiSplitter = splitter;
2834 chandata.mDryParams = DirectParams{};
2835 std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
2837 std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
2838 init_ambi);
2840 voice->mFlags |= VOICE_IS_AMBISONIC;
2842 else
2844 /* Clear previous samples. */
2845 auto clear_prevs = [device](ALvoice::ChannelData &chandata) -> void
2847 chandata.mPrevSamples.fill(0.0f);
2848 chandata.mDryParams = DirectParams{};
2849 std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
2851 std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
2852 clear_prevs);
2855 if(device->AvgSpeakerDist > 0.0f)
2857 const ALfloat w1{SPEEDOFSOUNDMETRESPERSEC /
2858 (device->AvgSpeakerDist * static_cast<float>(device->Frequency))};
2859 auto init_nfc = [w1](ALvoice::ChannelData &chandata) -> void
2860 { chandata.mDryParams.NFCtrlFilter.init(w1); };
2861 std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
2862 init_nfc);
2865 voice->mSourceID.store(source->id, std::memory_order_relaxed);
2866 voice->mPlayState.store(ALvoice::Playing, std::memory_order_release);
2867 source->VoiceIdx = vidx;
2869 if(source->state != AL_PLAYING)
2871 source->state = AL_PLAYING;
2872 SendStateChangeEvent(context.get(), source->id, AL_PLAYING);
2875 std::for_each(srchandles.begin(), srchandles.end(), start_source);
2877 END_API_FUNC
2880 AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source)
2881 START_API_FUNC
2882 { alSourcePausev(1, &source); }
2883 END_API_FUNC
2885 AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
2886 START_API_FUNC
2888 ContextRef context{GetContextRef()};
2889 if UNLIKELY(!context) return;
2891 if UNLIKELY(n < 0)
2892 context->setError(AL_INVALID_VALUE, "Pausing %d sources", n);
2893 if UNLIKELY(n <= 0) return;
2895 al::vector<ALsource*> extra_sources;
2896 std::array<ALsource*,8> source_storage;
2897 al::span<ALsource*> srchandles;
2898 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
2899 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
2900 else
2902 extra_sources.resize(static_cast<ALuint>(n));
2903 srchandles = {extra_sources.data(), extra_sources.size()};
2906 std::lock_guard<std::mutex> _{context->mSourceLock};
2907 for(auto &srchdl : srchandles)
2909 srchdl = LookupSource(context.get(), *sources);
2910 if(!srchdl)
2911 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
2912 ++sources;
2915 ALCdevice *device{context->mDevice.get()};
2916 BackendLockGuard __{*device->Backend};
2917 auto pause_source = [&context](ALsource *source) -> void
2919 ALvoice *voice{GetSourceVoice(source, context.get())};
2920 if(voice)
2922 std::atomic_thread_fence(std::memory_order_release);
2923 ALvoice::State oldvstate{ALvoice::Playing};
2924 voice->mPlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping,
2925 std::memory_order_acq_rel, std::memory_order_acquire);
2927 if(GetSourceState(source, voice) == AL_PLAYING)
2929 source->state = AL_PAUSED;
2930 SendStateChangeEvent(context.get(), source->id, AL_PAUSED);
2933 std::for_each(srchandles.begin(), srchandles.end(), pause_source);
2935 END_API_FUNC
2938 AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source)
2939 START_API_FUNC
2940 { alSourceStopv(1, &source); }
2941 END_API_FUNC
2943 AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
2944 START_API_FUNC
2946 ContextRef context{GetContextRef()};
2947 if UNLIKELY(!context) return;
2949 if UNLIKELY(n < 0)
2950 context->setError(AL_INVALID_VALUE, "Stopping %d sources", n);
2951 if UNLIKELY(n <= 0) return;
2953 al::vector<ALsource*> extra_sources;
2954 std::array<ALsource*,8> source_storage;
2955 al::span<ALsource*> srchandles;
2956 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
2957 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
2958 else
2960 extra_sources.resize(static_cast<ALuint>(n));
2961 srchandles = {extra_sources.data(), extra_sources.size()};
2964 std::lock_guard<std::mutex> _{context->mSourceLock};
2965 for(auto &srchdl : srchandles)
2967 srchdl = LookupSource(context.get(), *sources);
2968 if(!srchdl)
2969 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
2970 ++sources;
2973 ALCdevice *device{context->mDevice.get()};
2974 BackendLockGuard __{*device->Backend};
2975 auto stop_source = [&context](ALsource *source) -> void
2977 /* Get the source state before clearing from the voice, so we know what
2978 * state the source+voice was actually in.
2980 ALvoice *voice{GetSourceVoice(source, context.get())};
2981 const ALenum oldstate{GetSourceState(source, voice)};
2982 if(voice != nullptr)
2984 voice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
2985 voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
2986 voice->mSourceID.store(0u, std::memory_order_relaxed);
2987 std::atomic_thread_fence(std::memory_order_release);
2988 ALvoice::State oldvstate{ALvoice::Playing};
2989 voice->mPlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping,
2990 std::memory_order_acq_rel, std::memory_order_acquire);
2991 voice = nullptr;
2993 if(oldstate != AL_INITIAL && oldstate != AL_STOPPED)
2995 source->state = AL_STOPPED;
2996 SendStateChangeEvent(context.get(), source->id, AL_STOPPED);
2998 source->OffsetType = AL_NONE;
2999 source->Offset = 0.0;
3001 std::for_each(srchandles.begin(), srchandles.end(), stop_source);
3003 END_API_FUNC
3006 AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source)
3007 START_API_FUNC
3008 { alSourceRewindv(1, &source); }
3009 END_API_FUNC
3011 AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
3012 START_API_FUNC
3014 ContextRef context{GetContextRef()};
3015 if UNLIKELY(!context) return;
3017 if UNLIKELY(n < 0)
3018 context->setError(AL_INVALID_VALUE, "Rewinding %d sources", n);
3019 if UNLIKELY(n <= 0) return;
3021 al::vector<ALsource*> extra_sources;
3022 std::array<ALsource*,8> source_storage;
3023 al::span<ALsource*> srchandles;
3024 if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
3025 srchandles = {source_storage.data(), static_cast<ALuint>(n)};
3026 else
3028 extra_sources.resize(static_cast<ALuint>(n));
3029 srchandles = {extra_sources.data(), extra_sources.size()};
3032 std::lock_guard<std::mutex> _{context->mSourceLock};
3033 for(auto &srchdl : srchandles)
3035 srchdl = LookupSource(context.get(), *sources);
3036 if(!srchdl)
3037 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
3038 ++sources;
3041 ALCdevice *device{context->mDevice.get()};
3042 BackendLockGuard __{*device->Backend};
3043 auto rewind_source = [&context](ALsource *source) -> void
3045 ALvoice *voice{GetSourceVoice(source, context.get())};
3046 if(voice != nullptr)
3048 voice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
3049 voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
3050 voice->mSourceID.store(0u, std::memory_order_relaxed);
3051 std::atomic_thread_fence(std::memory_order_release);
3052 ALvoice::State oldvstate{ALvoice::Playing};
3053 voice->mPlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping,
3054 std::memory_order_acq_rel, std::memory_order_acquire);
3055 voice = nullptr;
3057 if(source->state != AL_INITIAL)
3059 source->state = AL_INITIAL;
3060 SendStateChangeEvent(context.get(), source->id, AL_INITIAL);
3062 source->OffsetType = AL_NONE;
3063 source->Offset = 0.0;
3065 std::for_each(srchandles.begin(), srchandles.end(), rewind_source);
3067 END_API_FUNC
3070 AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
3071 START_API_FUNC
3073 ContextRef context{GetContextRef()};
3074 if UNLIKELY(!context) return;
3076 if UNLIKELY(nb < 0)
3077 context->setError(AL_INVALID_VALUE, "Queueing %d buffers", nb);
3078 if UNLIKELY(nb <= 0) return;
3080 std::lock_guard<std::mutex> _{context->mSourceLock};
3081 ALsource *source{LookupSource(context.get(),src)};
3082 if UNLIKELY(!source)
3083 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
3085 /* Can't queue on a Static Source */
3086 if UNLIKELY(source->SourceType == AL_STATIC)
3087 SETERR_RETURN(context, AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
3089 /* Check for a valid Buffer, for its frequency and format */
3090 ALCdevice *device{context->mDevice.get()};
3091 ALbuffer *BufferFmt{nullptr};
3092 ALbufferlistitem *BufferList{source->queue};
3093 while(BufferList && !BufferFmt)
3095 BufferFmt = BufferList->mBuffer;
3096 BufferList = BufferList->mNext.load(std::memory_order_relaxed);
3099 std::unique_lock<std::mutex> buflock{device->BufferLock};
3100 ALbufferlistitem *BufferListStart{nullptr};
3101 BufferList = nullptr;
3102 for(ALsizei i{0};i < nb;i++)
3104 ALbuffer *buffer{nullptr};
3105 if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr)
3107 context->setError(AL_INVALID_NAME, "Queueing invalid buffer ID %u", buffers[i]);
3108 goto buffer_error;
3111 if(!BufferListStart)
3113 BufferListStart = new ALbufferlistitem{};
3114 BufferList = BufferListStart;
3116 else
3118 auto item = new ALbufferlistitem{};
3119 BufferList->mNext.store(item, std::memory_order_relaxed);
3120 BufferList = item;
3122 BufferList->mNext.store(nullptr, std::memory_order_relaxed);
3123 BufferList->mSampleLen = buffer ? buffer->SampleLen : 0;
3124 BufferList->mBuffer = buffer;
3125 if(!buffer) continue;
3127 IncrementRef(buffer->ref);
3129 if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
3131 context->setError(AL_INVALID_OPERATION, "Queueing non-persistently mapped buffer %u",
3132 buffer->id);
3133 goto buffer_error;
3136 if(BufferFmt == nullptr)
3137 BufferFmt = buffer;
3138 else if(BufferFmt->Frequency != buffer->Frequency ||
3139 BufferFmt->mFmtChannels != buffer->mFmtChannels ||
3140 ((BufferFmt->mFmtChannels == FmtBFormat2D ||
3141 BufferFmt->mFmtChannels == FmtBFormat3D) &&
3142 (BufferFmt->AmbiLayout != buffer->AmbiLayout ||
3143 BufferFmt->AmbiScaling != buffer->AmbiScaling)) ||
3144 BufferFmt->OriginalType != buffer->OriginalType)
3146 context->setError(AL_INVALID_OPERATION, "Queueing buffer with mismatched format");
3148 buffer_error:
3149 /* A buffer failed (invalid ID or format), so unlock and release
3150 * each buffer we had. */
3151 while(BufferListStart)
3153 std::unique_ptr<ALbufferlistitem> head{BufferListStart};
3154 BufferListStart = head->mNext.load(std::memory_order_relaxed);
3155 if((buffer=head->mBuffer) != nullptr) DecrementRef(buffer->ref);
3157 return;
3160 /* All buffers good. */
3161 buflock.unlock();
3163 /* Source is now streaming */
3164 source->SourceType = AL_STREAMING;
3166 BufferList = source->queue;
3167 if(!BufferList)
3168 source->queue = BufferListStart;
3169 else
3171 ALbufferlistitem *next;
3172 while((next=BufferList->mNext.load(std::memory_order_relaxed)) != nullptr)
3173 BufferList = next;
3174 BufferList->mNext.store(BufferListStart, std::memory_order_release);
3177 END_API_FUNC
3179 AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
3180 START_API_FUNC
3182 ContextRef context{GetContextRef()};
3183 if UNLIKELY(!context) return;
3185 if UNLIKELY(nb < 0)
3186 context->setError(AL_INVALID_VALUE, "Unqueueing %d buffers", nb);
3187 if UNLIKELY(nb <= 0) return;
3189 std::lock_guard<std::mutex> _{context->mSourceLock};
3190 ALsource *source{LookupSource(context.get(),src)};
3191 if UNLIKELY(!source)
3192 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
3194 if UNLIKELY(source->Looping)
3195 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from looping source %u", src);
3196 if UNLIKELY(source->SourceType != AL_STREAMING)
3197 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from a non-streaming source %u",
3198 src);
3200 /* Make sure enough buffers have been processed to unqueue. */
3201 ALbufferlistitem *BufferList{source->queue};
3202 ALvoice *voice{GetSourceVoice(source, context.get())};
3203 ALbufferlistitem *Current{nullptr};
3204 if(voice)
3205 Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
3206 else if(source->state == AL_INITIAL)
3207 Current = BufferList;
3208 if UNLIKELY(BufferList == Current)
3209 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing pending buffers");
3211 ALuint i{1u};
3212 while(i < static_cast<ALuint>(nb))
3214 /* If the next bufferlist to check is NULL or is the current one, it's
3215 * trying to unqueue pending buffers.
3217 ALbufferlistitem *next{BufferList->mNext.load(std::memory_order_relaxed)};
3218 if UNLIKELY(!next || next == Current)
3219 SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing pending buffers");
3220 BufferList = next;
3222 ++i;
3225 do {
3226 std::unique_ptr<ALbufferlistitem> head{source->queue};
3227 source->queue = head->mNext.load(std::memory_order_relaxed);
3229 if(ALbuffer *buffer{head->mBuffer})
3231 *(buffers++) = buffer->id;
3232 DecrementRef(buffer->ref);
3234 else
3235 *(buffers++) = 0;
3236 } while(--nb);
3238 END_API_FUNC
3241 ALsource::ALsource(ALuint num_sends)
3243 InnerAngle = 360.0f;
3244 OuterAngle = 360.0f;
3245 Pitch = 1.0f;
3246 Position[0] = 0.0f;
3247 Position[1] = 0.0f;
3248 Position[2] = 0.0f;
3249 Velocity[0] = 0.0f;
3250 Velocity[1] = 0.0f;
3251 Velocity[2] = 0.0f;
3252 Direction[0] = 0.0f;
3253 Direction[1] = 0.0f;
3254 Direction[2] = 0.0f;
3255 OrientAt[0] = 0.0f;
3256 OrientAt[1] = 0.0f;
3257 OrientAt[2] = -1.0f;
3258 OrientUp[0] = 0.0f;
3259 OrientUp[1] = 1.0f;
3260 OrientUp[2] = 0.0f;
3261 RefDistance = 1.0f;
3262 MaxDistance = std::numeric_limits<float>::max();
3263 RolloffFactor = 1.0f;
3264 Gain = 1.0f;
3265 MinGain = 0.0f;
3266 MaxGain = 1.0f;
3267 OuterGain = 0.0f;
3268 OuterGainHF = 1.0f;
3270 DryGainHFAuto = AL_TRUE;
3271 WetGainAuto = AL_TRUE;
3272 WetGainHFAuto = AL_TRUE;
3273 AirAbsorptionFactor = 0.0f;
3274 RoomRolloffFactor = 0.0f;
3275 DopplerFactor = 1.0f;
3276 HeadRelative = AL_FALSE;
3277 Looping = AL_FALSE;
3278 mDistanceModel = DistanceModel::Default;
3279 mResampler = ResamplerDefault;
3280 DirectChannels = AL_FALSE;
3281 mSpatialize = SpatializeAuto;
3283 StereoPan[0] = Deg2Rad( 30.0f);
3284 StereoPan[1] = Deg2Rad(-30.0f);
3286 Radius = 0.0f;
3288 Direct.Gain = 1.0f;
3289 Direct.GainHF = 1.0f;
3290 Direct.HFReference = LOWPASSFREQREF;
3291 Direct.GainLF = 1.0f;
3292 Direct.LFReference = HIGHPASSFREQREF;
3293 Send.resize(num_sends);
3294 for(auto &send : Send)
3296 send.Slot = nullptr;
3297 send.Gain = 1.0f;
3298 send.GainHF = 1.0f;
3299 send.HFReference = LOWPASSFREQREF;
3300 send.GainLF = 1.0f;
3301 send.LFReference = HIGHPASSFREQREF;
3304 PropsClean.test_and_set(std::memory_order_relaxed);
3307 ALsource::~ALsource()
3309 ALbufferlistitem *BufferList{queue};
3310 while(BufferList != nullptr)
3312 std::unique_ptr<ALbufferlistitem> head{BufferList};
3313 BufferList = head->mNext.load(std::memory_order_relaxed);
3314 if(ALbuffer *buffer{head->mBuffer}) DecrementRef(buffer->ref);
3316 queue = nullptr;
3318 std::for_each(Send.begin(), Send.end(),
3319 [](ALsource::SendData &send) -> void
3321 if(send.Slot)
3322 DecrementRef(send.Slot->ref);
3323 send.Slot = nullptr;
3328 void UpdateAllSourceProps(ALCcontext *context)
3330 std::lock_guard<std::mutex> _{context->mSourceLock};
3331 std::for_each(context->mVoices.begin(), context->mVoices.end(),
3332 [context](ALvoice &voice) -> void
3334 ALuint sid{voice.mSourceID.load(std::memory_order_acquire)};
3335 ALsource *source = sid ? LookupSource(context, sid) : nullptr;
3336 if(source && !source->PropsClean.test_and_set(std::memory_order_acq_rel))
3337 UpdateSourceProps(source, &voice, context);
3342 SourceSubList::~SourceSubList()
3344 uint64_t usemask{~FreeMask};
3345 while(usemask)
3347 ALsizei idx{CTZ64(usemask)};
3348 al::destroy_at(Sources+idx);
3349 usemask &= ~(1_u64 << idx);
3351 FreeMask = ~usemask;
3352 al_free(Sources);
3353 Sources = nullptr;