Use c++ headers
[openal-soft.git] / OpenAL32 / alSource.cpp
blob236ed144e3069bb5df23ccefb9b4d02762cb8334
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 <cstdlib>
24 #include <climits>
25 #include <cfloat>
27 #include <cmath>
28 #include <thread>
29 #include <limits>
30 #include <algorithm>
31 #include <functional>
33 #include "AL/al.h"
34 #include "AL/alc.h"
36 #include "alMain.h"
37 #include "alcontext.h"
38 #include "alError.h"
39 #include "alSource.h"
40 #include "alBuffer.h"
41 #include "alFilter.h"
42 #include "alAuxEffectSlot.h"
43 #include "ringbuffer.h"
45 #include "backends/base.h"
47 #include "threads.h"
48 #include "almalloc.h"
51 namespace {
53 using namespace std::placeholders;
55 inline ALvoice *GetSourceVoice(ALsource *source, ALCcontext *context)
57 ALint idx{source->VoiceIdx};
58 if(idx >= 0 && idx < context->VoiceCount.load(std::memory_order_relaxed))
60 ALuint sid{source->id};
61 ALvoice *voice{context->Voices[idx]};
62 if(voice->SourceID.load(std::memory_order_acquire) == sid)
63 return voice;
65 source->VoiceIdx = -1;
66 return nullptr;
69 void UpdateSourceProps(const ALsource *source, ALvoice *voice, ALCcontext *context)
71 /* Get an unused property container, or allocate a new one as needed. */
72 ALvoiceProps *props{context->FreeVoiceProps.load(std::memory_order_acquire)};
73 if(!props)
74 props = new ALvoiceProps{};
75 else
77 ALvoiceProps *next;
78 do {
79 next = props->next.load(std::memory_order_relaxed);
80 } while(context->FreeVoiceProps.compare_exchange_weak(props, next,
81 std::memory_order_acq_rel, std::memory_order_acquire) == 0);
84 /* Copy in current property values. */
85 props->Pitch = source->Pitch;
86 props->Gain = source->Gain;
87 props->OuterGain = source->OuterGain;
88 props->MinGain = source->MinGain;
89 props->MaxGain = source->MaxGain;
90 props->InnerAngle = source->InnerAngle;
91 props->OuterAngle = source->OuterAngle;
92 props->RefDistance = source->RefDistance;
93 props->MaxDistance = source->MaxDistance;
94 props->RolloffFactor = source->RolloffFactor;
95 props->Position = source->Position;
96 props->Velocity = source->Velocity;
97 props->Direction = source->Direction;
98 props->OrientAt = source->OrientAt;
99 props->OrientUp = source->OrientUp;
100 props->HeadRelative = source->HeadRelative;
101 props->mDistanceModel = source->mDistanceModel;
102 props->mResampler = source->mResampler;
103 props->DirectChannels = source->DirectChannels;
104 props->mSpatializeMode = source->mSpatialize;
106 props->DryGainHFAuto = source->DryGainHFAuto;
107 props->WetGainAuto = source->WetGainAuto;
108 props->WetGainHFAuto = source->WetGainHFAuto;
109 props->OuterGainHF = source->OuterGainHF;
111 props->AirAbsorptionFactor = source->AirAbsorptionFactor;
112 props->RoomRolloffFactor = source->RoomRolloffFactor;
113 props->DopplerFactor = source->DopplerFactor;
115 props->StereoPan = source->StereoPan;
117 props->Radius = source->Radius;
119 props->Direct.Gain = source->Direct.Gain;
120 props->Direct.GainHF = source->Direct.GainHF;
121 props->Direct.HFReference = source->Direct.HFReference;
122 props->Direct.GainLF = source->Direct.GainLF;
123 props->Direct.LFReference = source->Direct.LFReference;
125 auto copy_send = [](const ALsource::SendData &srcsend) noexcept -> ALvoicePropsBase::SendData
127 ALvoicePropsBase::SendData ret;
128 ret.Slot = srcsend.Slot;
129 ret.Gain = srcsend.Gain;
130 ret.GainHF = srcsend.GainHF;
131 ret.HFReference = srcsend.HFReference;
132 ret.GainLF = srcsend.GainLF;
133 ret.LFReference = srcsend.LFReference;
134 return ret;
136 std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send);
138 /* Set the new container for updating internal parameters. */
139 props = voice->Update.exchange(props, std::memory_order_acq_rel);
140 if(props)
142 /* If there was an unused update container, put it back in the
143 * freelist.
145 AtomicReplaceHead(context->FreeVoiceProps, props);
150 /* GetSourceSampleOffset
152 * Gets the current read offset for the given Source, in 32.32 fixed-point
153 * samples. The offset is relative to the start of the queue (not the start of
154 * the current buffer).
156 ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, std::chrono::nanoseconds *clocktime)
158 ALCdevice *device{context->Device};
159 const ALbufferlistitem *Current;
160 ALuint64 readPos;
161 ALuint refcount;
162 ALvoice *voice;
164 do {
165 Current = nullptr;
166 readPos = 0;
167 while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
168 std::this_thread::yield();
169 *clocktime = GetDeviceClockTime(device);
171 voice = GetSourceVoice(Source, context);
172 if(voice)
174 Current = voice->current_buffer.load(std::memory_order_relaxed);
176 readPos = static_cast<ALuint64>(voice->position.load(std::memory_order_relaxed)) << 32;
177 readPos |= static_cast<ALuint64>(voice->position_fraction.load(std::memory_order_relaxed)) <<
178 (32-FRACTIONBITS);
180 std::atomic_thread_fence(std::memory_order_acquire);
181 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
183 if(voice)
185 const ALbufferlistitem *BufferList{Source->queue};
186 while(BufferList && BufferList != Current)
188 readPos += static_cast<ALuint64>(BufferList->max_samples) << 32;
189 BufferList = BufferList->next.load(std::memory_order_relaxed);
191 readPos = minu64(readPos, 0x7fffffffffffffff_u64);
194 return static_cast<ALint64>(readPos);
197 /* GetSourceSecOffset
199 * Gets the current read offset for the given Source, in seconds. The offset is
200 * relative to the start of the queue (not the start of the current buffer).
202 ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, std::chrono::nanoseconds *clocktime)
204 ALCdevice *device{context->Device};
205 const ALbufferlistitem *Current;
206 ALuint64 readPos;
207 ALuint refcount;
208 ALvoice *voice;
210 do {
211 Current = nullptr;
212 readPos = 0;
213 while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
214 std::this_thread::yield();
215 *clocktime = GetDeviceClockTime(device);
217 voice = GetSourceVoice(Source, context);
218 if(voice)
220 Current = voice->current_buffer.load(std::memory_order_relaxed);
222 readPos = static_cast<ALuint64>(voice->position.load(std::memory_order_relaxed)) << FRACTIONBITS;
223 readPos |= voice->position_fraction.load(std::memory_order_relaxed);
225 std::atomic_thread_fence(std::memory_order_acquire);
226 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
228 ALdouble offset{0.0};
229 if(voice)
231 const ALbufferlistitem *BufferList{Source->queue};
232 const ALbuffer *BufferFmt{nullptr};
233 while(BufferList && BufferList != Current)
235 for(ALsizei i{0};!BufferFmt && i < BufferList->num_buffers;++i)
236 BufferFmt = BufferList->buffers[i];
237 readPos += static_cast<ALuint64>(BufferList->max_samples) << FRACTIONBITS;
238 BufferList = BufferList->next.load(std::memory_order_relaxed);
241 while(BufferList && !BufferFmt)
243 for(ALsizei i{0};!BufferFmt && i < BufferList->num_buffers;++i)
244 BufferFmt = BufferList->buffers[i];
245 BufferList = BufferList->next.load(std::memory_order_relaxed);
247 assert(BufferFmt != nullptr);
249 offset = static_cast<ALdouble>(readPos) / static_cast<ALdouble>FRACTIONONE /
250 static_cast<ALdouble>(BufferFmt->Frequency);
253 return offset;
256 /* GetSourceOffset
258 * Gets the current read offset for the given Source, in the appropriate format
259 * (Bytes, Samples or Seconds). The offset is relative to the start of the
260 * queue (not the start of the current buffer).
262 ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
264 ALCdevice *device{context->Device};
265 const ALbufferlistitem *Current;
266 ALuint readPos;
267 ALsizei readPosFrac;
268 ALuint refcount;
269 ALvoice *voice;
271 do {
272 Current = nullptr;
273 readPos = readPosFrac = 0;
274 while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
275 std::this_thread::yield();
276 voice = GetSourceVoice(Source, context);
277 if(voice)
279 Current = voice->current_buffer.load(std::memory_order_relaxed);
281 readPos = voice->position.load(std::memory_order_relaxed);
282 readPosFrac = voice->position_fraction.load(std::memory_order_relaxed);
284 std::atomic_thread_fence(std::memory_order_acquire);
285 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
287 ALdouble offset{0.0};
288 if(voice)
290 const ALbufferlistitem *BufferList{Source->queue};
291 const ALbuffer *BufferFmt{nullptr};
292 ALboolean readFin{AL_FALSE};
293 ALuint totalBufferLen{0u};
295 while(BufferList)
297 for(ALsizei i{0};!BufferFmt && i < BufferList->num_buffers;++i)
298 BufferFmt = BufferList->buffers[i];
300 readFin |= (BufferList == Current);
301 totalBufferLen += BufferList->max_samples;
302 if(!readFin) readPos += BufferList->max_samples;
304 BufferList = BufferList->next.load(std::memory_order_relaxed);
306 assert(BufferFmt != nullptr);
308 if(Source->Looping)
309 readPos %= totalBufferLen;
310 else
312 /* Wrap back to 0 */
313 if(readPos >= totalBufferLen)
314 readPos = readPosFrac = 0;
317 offset = 0.0;
318 switch(name)
320 case AL_SEC_OFFSET:
321 offset = (readPos + static_cast<ALdouble>(readPosFrac)/FRACTIONONE) / BufferFmt->Frequency;
322 break;
324 case AL_SAMPLE_OFFSET:
325 offset = readPos + static_cast<ALdouble>(readPosFrac)/FRACTIONONE;
326 break;
328 case AL_BYTE_OFFSET:
329 if(BufferFmt->OriginalType == UserFmtIMA4)
331 ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4;
332 ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->mFmtChannels);
333 ALuint FrameBlockSize = BufferFmt->OriginalAlign;
335 /* Round down to nearest ADPCM block */
336 offset = static_cast<ALdouble>(readPos / FrameBlockSize * BlockSize);
338 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
340 ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7;
341 ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->mFmtChannels);
342 ALuint FrameBlockSize = BufferFmt->OriginalAlign;
344 /* Round down to nearest ADPCM block */
345 offset = static_cast<ALdouble>(readPos / FrameBlockSize * BlockSize);
347 else
349 const ALsizei FrameSize{FrameSizeFromFmt(BufferFmt->mFmtChannels,
350 BufferFmt->mFmtType)};
351 offset = static_cast<ALdouble>(readPos * FrameSize);
353 break;
357 return offset;
361 /* GetSampleOffset
363 * Retrieves the sample offset into the Source's queue (from the Sample, Byte
364 * or Second offset supplied by the application). This takes into account the
365 * fact that the buffer format may have been modifed since.
367 ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac)
369 const ALbuffer *BufferFmt{nullptr};
370 const ALbufferlistitem *BufferList;
372 /* Find the first valid Buffer in the Queue */
373 BufferList = Source->queue;
374 while(BufferList)
376 for(ALsizei i{0};i < BufferList->num_buffers && !BufferFmt;i++)
377 BufferFmt = BufferList->buffers[i];
378 if(BufferFmt) break;
379 BufferList = BufferList->next.load(std::memory_order_relaxed);
381 if(!BufferFmt)
383 Source->OffsetType = AL_NONE;
384 Source->Offset = 0.0;
385 return AL_FALSE;
388 ALdouble dbloff, dblfrac;
389 switch(Source->OffsetType)
391 case AL_BYTE_OFFSET:
392 /* Determine the ByteOffset (and ensure it is block aligned) */
393 *offset = static_cast<ALuint>(Source->Offset);
394 if(BufferFmt->OriginalType == UserFmtIMA4)
396 ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4;
397 *offset /= align * ChannelsFromFmt(BufferFmt->mFmtChannels);
398 *offset *= BufferFmt->OriginalAlign;
400 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
402 ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7;
403 *offset /= align * ChannelsFromFmt(BufferFmt->mFmtChannels);
404 *offset *= BufferFmt->OriginalAlign;
406 else
407 *offset /= FrameSizeFromFmt(BufferFmt->mFmtChannels, BufferFmt->mFmtType);
408 *frac = 0;
409 break;
411 case AL_SAMPLE_OFFSET:
412 dblfrac = modf(Source->Offset, &dbloff);
413 *offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<unsigned int>::max()));
414 *frac = static_cast<ALsizei>(mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0));
415 break;
417 case AL_SEC_OFFSET:
418 dblfrac = modf(Source->Offset*BufferFmt->Frequency, &dbloff);
419 *offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<unsigned int>::max()));
420 *frac = static_cast<ALsizei>(mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0));
421 break;
423 Source->OffsetType = AL_NONE;
424 Source->Offset = 0.0;
426 return AL_TRUE;
429 /* ApplyOffset
431 * Apply the stored playback offset to the Source. This function will update
432 * the number of buffers "played" given the stored offset.
434 ALboolean ApplyOffset(ALsource *Source, ALvoice *voice)
436 /* Get sample frame offset */
437 ALuint offset{0u};
438 ALsizei frac{0};
439 if(!GetSampleOffset(Source, &offset, &frac))
440 return AL_FALSE;
442 ALuint totalBufferLen{0u};
443 ALbufferlistitem *BufferList{Source->queue};
444 while(BufferList && totalBufferLen <= offset)
446 if(static_cast<ALuint>(BufferList->max_samples) > offset-totalBufferLen)
448 /* Offset is in this buffer */
449 voice->position.store(offset - totalBufferLen, std::memory_order_relaxed);
450 voice->position_fraction.store(frac, std::memory_order_relaxed);
451 voice->current_buffer.store(BufferList, std::memory_order_release);
452 return AL_TRUE;
454 totalBufferLen += BufferList->max_samples;
456 BufferList = BufferList->next.load(std::memory_order_relaxed);
459 /* Offset is out of range of the queue */
460 return AL_FALSE;
464 ALsource *AllocSource(ALCcontext *context)
466 ALCdevice *device{context->Device};
467 std::lock_guard<std::mutex> _{context->SourceLock};
468 if(context->NumSources >= device->SourcesMax)
470 alSetError(context, AL_OUT_OF_MEMORY, "Exceeding %u source limit", device->SourcesMax);
471 return nullptr;
473 auto sublist = std::find_if(context->SourceList.begin(), context->SourceList.end(),
474 [](const SourceSubList &entry) noexcept -> bool
475 { return entry.FreeMask != 0; }
477 auto lidx = static_cast<ALsizei>(std::distance(context->SourceList.begin(), sublist));
478 ALsource *source;
479 ALsizei slidx;
480 if(LIKELY(sublist != context->SourceList.end()))
482 slidx = CTZ64(sublist->FreeMask);
483 source = sublist->Sources + slidx;
485 else
487 /* Don't allocate so many list entries that the 32-bit ID could
488 * overflow...
490 if(UNLIKELY(context->SourceList.size() >= 1<<25))
492 alSetError(context, AL_OUT_OF_MEMORY, "Too many sources allocated");
493 return nullptr;
495 context->SourceList.emplace_back();
496 sublist = context->SourceList.end() - 1;
498 sublist->FreeMask = ~0_u64;
499 sublist->Sources = static_cast<ALsource*>(al_calloc(16, sizeof(ALsource)*64));
500 if(UNLIKELY(!sublist->Sources))
502 context->SourceList.pop_back();
503 alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate source batch");
504 return nullptr;
507 slidx = 0;
508 source = sublist->Sources + slidx;
511 source = new (source) ALsource{device->NumAuxSends};
513 /* Add 1 to avoid source ID 0. */
514 source->id = ((lidx<<6) | slidx) + 1;
516 context->NumSources += 1;
517 sublist->FreeMask &= ~(1_u64 << slidx);
519 return source;
522 void FreeSource(ALCcontext *context, ALsource *source)
524 ALuint id = source->id - 1;
525 ALsizei lidx = id >> 6;
526 ALsizei slidx = id & 0x3f;
528 ALCdevice *device{context->Device};
529 BackendUniqueLock backlock{*device->Backend};
530 if(ALvoice *voice{GetSourceVoice(source, context)})
532 voice->SourceID.store(0u, std::memory_order_relaxed);
533 voice->Playing.store(false, std::memory_order_release);
535 backlock.unlock();
537 source->~ALsource();
539 context->SourceList[lidx].FreeMask |= 1_u64 << slidx;
540 context->NumSources--;
544 inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept
546 ALuint lidx = (id-1) >> 6;
547 ALsizei slidx = (id-1) & 0x3f;
549 if(UNLIKELY(lidx >= context->SourceList.size()))
550 return nullptr;
551 SourceSubList &sublist{context->SourceList[lidx]};
552 if(UNLIKELY(sublist.FreeMask & (1_u64 << slidx)))
553 return nullptr;
554 return sublist.Sources + slidx;
557 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
559 ALuint lidx = (id-1) >> 6;
560 ALsizei slidx = (id-1) & 0x3f;
562 if(UNLIKELY(lidx >= device->BufferList.size()))
563 return nullptr;
564 BufferSubList &sublist = device->BufferList[lidx];
565 if(UNLIKELY(sublist.FreeMask & (1_u64 << slidx)))
566 return nullptr;
567 return sublist.Buffers + slidx;
570 inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept
572 ALuint lidx = (id-1) >> 6;
573 ALsizei slidx = (id-1) & 0x3f;
575 if(UNLIKELY(lidx >= device->FilterList.size()))
576 return nullptr;
577 FilterSubList &sublist = device->FilterList[lidx];
578 if(UNLIKELY(sublist.FreeMask & (1_u64 << slidx)))
579 return nullptr;
580 return sublist.Filters + slidx;
583 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
585 --id;
586 if(UNLIKELY(id >= context->EffectSlotList.size()))
587 return nullptr;
588 return context->EffectSlotList[id].get();
592 enum SourceProp : ALenum {
593 srcPitch = AL_PITCH,
594 srcGain = AL_GAIN,
595 srcMinGain = AL_MIN_GAIN,
596 srcMaxGain = AL_MAX_GAIN,
597 srcMaxDistance = AL_MAX_DISTANCE,
598 srcRolloffFactor = AL_ROLLOFF_FACTOR,
599 srcDopplerFactor = AL_DOPPLER_FACTOR,
600 srcConeOuterGain = AL_CONE_OUTER_GAIN,
601 srcSecOffset = AL_SEC_OFFSET,
602 srcSampleOffset = AL_SAMPLE_OFFSET,
603 srcByteOffset = AL_BYTE_OFFSET,
604 srcConeInnerAngle = AL_CONE_INNER_ANGLE,
605 srcConeOuterAngle = AL_CONE_OUTER_ANGLE,
606 srcRefDistance = AL_REFERENCE_DISTANCE,
608 srcPosition = AL_POSITION,
609 srcVelocity = AL_VELOCITY,
610 srcDirection = AL_DIRECTION,
612 srcSourceRelative = AL_SOURCE_RELATIVE,
613 srcLooping = AL_LOOPING,
614 srcBuffer = AL_BUFFER,
615 srcSourceState = AL_SOURCE_STATE,
616 srcBuffersQueued = AL_BUFFERS_QUEUED,
617 srcBuffersProcessed = AL_BUFFERS_PROCESSED,
618 srcSourceType = AL_SOURCE_TYPE,
620 /* ALC_EXT_EFX */
621 srcConeOuterGainHF = AL_CONE_OUTER_GAINHF,
622 srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR,
623 srcRoomRolloffFactor = AL_ROOM_ROLLOFF_FACTOR,
624 srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO,
625 srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO,
626 srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO,
627 srcDirectFilter = AL_DIRECT_FILTER,
628 srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER,
630 /* AL_SOFT_direct_channels */
631 srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT,
633 /* AL_EXT_source_distance_model */
634 srcDistanceModel = AL_DISTANCE_MODEL,
636 /* AL_SOFT_source_latency */
637 srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT,
638 srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT,
640 /* AL_EXT_STEREO_ANGLES */
641 srcAngles = AL_STEREO_ANGLES,
643 /* AL_EXT_SOURCE_RADIUS */
644 srcRadius = AL_SOURCE_RADIUS,
646 /* AL_EXT_BFORMAT */
647 srcOrientation = AL_ORIENTATION,
649 /* AL_SOFT_source_resampler */
650 srcResampler = AL_SOURCE_RESAMPLER_SOFT,
652 /* AL_SOFT_source_spatialize */
653 srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT,
655 /* ALC_SOFT_device_clock */
656 srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT,
657 srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT,
661 * Returns if the last known state for the source was playing or paused. Does
662 * not sync with the mixer voice.
664 inline bool IsPlayingOrPaused(ALsource *source)
665 { return source->state == AL_PLAYING || source->state == AL_PAUSED; }
668 * Returns an updated source state using the matching voice's status (or lack
669 * thereof).
671 inline ALenum GetSourceState(ALsource *source, ALvoice *voice)
673 if(!voice && source->state == AL_PLAYING)
674 source->state = AL_STOPPED;
675 return source->state;
679 * Returns if the source should specify an update, given the context's
680 * deferring state and the source's last known state.
682 inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context)
684 return !context->DeferUpdates.load(std::memory_order_acquire) &&
685 IsPlayingOrPaused(source);
689 /** Can only be called while the mixer is locked! */
690 void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state)
692 ALbitfieldSOFT enabledevt{context->EnabledEvts.load(std::memory_order_acquire)};
693 if(!(enabledevt&EventType_SourceStateChange)) return;
695 /* The mixer may have queued a state change that's not yet been processed,
696 * and we don't want state change messages to occur out of order, so send
697 * it through the async queue to ensure proper ordering.
699 RingBuffer *ring{context->AsyncEvents.get()};
700 auto evt_vec = ring->getWriteVector();
701 if(evt_vec.first.len < 1) return;
703 AsyncEvent *evt{new (evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}};
704 evt->u.srcstate.id = id;
705 evt->u.srcstate.state = state;
706 ring->writeAdvance(1);
707 context->EventSem.post();
711 ALint FloatValsByProp(ALenum prop)
713 switch(static_cast<SourceProp>(prop))
715 case AL_PITCH:
716 case AL_GAIN:
717 case AL_MIN_GAIN:
718 case AL_MAX_GAIN:
719 case AL_MAX_DISTANCE:
720 case AL_ROLLOFF_FACTOR:
721 case AL_DOPPLER_FACTOR:
722 case AL_CONE_OUTER_GAIN:
723 case AL_SEC_OFFSET:
724 case AL_SAMPLE_OFFSET:
725 case AL_BYTE_OFFSET:
726 case AL_CONE_INNER_ANGLE:
727 case AL_CONE_OUTER_ANGLE:
728 case AL_REFERENCE_DISTANCE:
729 case AL_CONE_OUTER_GAINHF:
730 case AL_AIR_ABSORPTION_FACTOR:
731 case AL_ROOM_ROLLOFF_FACTOR:
732 case AL_DIRECT_FILTER_GAINHF_AUTO:
733 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
734 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
735 case AL_DIRECT_CHANNELS_SOFT:
736 case AL_DISTANCE_MODEL:
737 case AL_SOURCE_RELATIVE:
738 case AL_LOOPING:
739 case AL_SOURCE_STATE:
740 case AL_BUFFERS_QUEUED:
741 case AL_BUFFERS_PROCESSED:
742 case AL_SOURCE_TYPE:
743 case AL_SOURCE_RADIUS:
744 case AL_SOURCE_RESAMPLER_SOFT:
745 case AL_SOURCE_SPATIALIZE_SOFT:
746 return 1;
748 case AL_STEREO_ANGLES:
749 return 2;
751 case AL_POSITION:
752 case AL_VELOCITY:
753 case AL_DIRECTION:
754 return 3;
756 case AL_ORIENTATION:
757 return 6;
759 case AL_SEC_OFFSET_LATENCY_SOFT:
760 case AL_SEC_OFFSET_CLOCK_SOFT:
761 break; /* Double only */
763 case AL_BUFFER:
764 case AL_DIRECT_FILTER:
765 case AL_AUXILIARY_SEND_FILTER:
766 break; /* i/i64 only */
767 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
768 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
769 break; /* i64 only */
771 return 0;
773 ALint DoubleValsByProp(ALenum prop)
775 switch(static_cast<SourceProp>(prop))
777 case AL_PITCH:
778 case AL_GAIN:
779 case AL_MIN_GAIN:
780 case AL_MAX_GAIN:
781 case AL_MAX_DISTANCE:
782 case AL_ROLLOFF_FACTOR:
783 case AL_DOPPLER_FACTOR:
784 case AL_CONE_OUTER_GAIN:
785 case AL_SEC_OFFSET:
786 case AL_SAMPLE_OFFSET:
787 case AL_BYTE_OFFSET:
788 case AL_CONE_INNER_ANGLE:
789 case AL_CONE_OUTER_ANGLE:
790 case AL_REFERENCE_DISTANCE:
791 case AL_CONE_OUTER_GAINHF:
792 case AL_AIR_ABSORPTION_FACTOR:
793 case AL_ROOM_ROLLOFF_FACTOR:
794 case AL_DIRECT_FILTER_GAINHF_AUTO:
795 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
796 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
797 case AL_DIRECT_CHANNELS_SOFT:
798 case AL_DISTANCE_MODEL:
799 case AL_SOURCE_RELATIVE:
800 case AL_LOOPING:
801 case AL_SOURCE_STATE:
802 case AL_BUFFERS_QUEUED:
803 case AL_BUFFERS_PROCESSED:
804 case AL_SOURCE_TYPE:
805 case AL_SOURCE_RADIUS:
806 case AL_SOURCE_RESAMPLER_SOFT:
807 case AL_SOURCE_SPATIALIZE_SOFT:
808 return 1;
810 case AL_SEC_OFFSET_LATENCY_SOFT:
811 case AL_SEC_OFFSET_CLOCK_SOFT:
812 case AL_STEREO_ANGLES:
813 return 2;
815 case AL_POSITION:
816 case AL_VELOCITY:
817 case AL_DIRECTION:
818 return 3;
820 case AL_ORIENTATION:
821 return 6;
823 case AL_BUFFER:
824 case AL_DIRECT_FILTER:
825 case AL_AUXILIARY_SEND_FILTER:
826 break; /* i/i64 only */
827 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
828 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
829 break; /* i64 only */
831 return 0;
834 ALint IntValsByProp(ALenum prop)
836 switch(static_cast<SourceProp>(prop))
838 case AL_PITCH:
839 case AL_GAIN:
840 case AL_MIN_GAIN:
841 case AL_MAX_GAIN:
842 case AL_MAX_DISTANCE:
843 case AL_ROLLOFF_FACTOR:
844 case AL_DOPPLER_FACTOR:
845 case AL_CONE_OUTER_GAIN:
846 case AL_SEC_OFFSET:
847 case AL_SAMPLE_OFFSET:
848 case AL_BYTE_OFFSET:
849 case AL_CONE_INNER_ANGLE:
850 case AL_CONE_OUTER_ANGLE:
851 case AL_REFERENCE_DISTANCE:
852 case AL_CONE_OUTER_GAINHF:
853 case AL_AIR_ABSORPTION_FACTOR:
854 case AL_ROOM_ROLLOFF_FACTOR:
855 case AL_DIRECT_FILTER_GAINHF_AUTO:
856 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
857 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
858 case AL_DIRECT_CHANNELS_SOFT:
859 case AL_DISTANCE_MODEL:
860 case AL_SOURCE_RELATIVE:
861 case AL_LOOPING:
862 case AL_BUFFER:
863 case AL_SOURCE_STATE:
864 case AL_BUFFERS_QUEUED:
865 case AL_BUFFERS_PROCESSED:
866 case AL_SOURCE_TYPE:
867 case AL_DIRECT_FILTER:
868 case AL_SOURCE_RADIUS:
869 case AL_SOURCE_RESAMPLER_SOFT:
870 case AL_SOURCE_SPATIALIZE_SOFT:
871 return 1;
873 case AL_POSITION:
874 case AL_VELOCITY:
875 case AL_DIRECTION:
876 case AL_AUXILIARY_SEND_FILTER:
877 return 3;
879 case AL_ORIENTATION:
880 return 6;
882 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
883 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
884 break; /* i64 only */
885 case AL_SEC_OFFSET_LATENCY_SOFT:
886 case AL_SEC_OFFSET_CLOCK_SOFT:
887 break; /* Double only */
888 case AL_STEREO_ANGLES:
889 break; /* Float/double only */
891 return 0;
893 ALint Int64ValsByProp(ALenum prop)
895 switch(static_cast<SourceProp>(prop))
897 case AL_PITCH:
898 case AL_GAIN:
899 case AL_MIN_GAIN:
900 case AL_MAX_GAIN:
901 case AL_MAX_DISTANCE:
902 case AL_ROLLOFF_FACTOR:
903 case AL_DOPPLER_FACTOR:
904 case AL_CONE_OUTER_GAIN:
905 case AL_SEC_OFFSET:
906 case AL_SAMPLE_OFFSET:
907 case AL_BYTE_OFFSET:
908 case AL_CONE_INNER_ANGLE:
909 case AL_CONE_OUTER_ANGLE:
910 case AL_REFERENCE_DISTANCE:
911 case AL_CONE_OUTER_GAINHF:
912 case AL_AIR_ABSORPTION_FACTOR:
913 case AL_ROOM_ROLLOFF_FACTOR:
914 case AL_DIRECT_FILTER_GAINHF_AUTO:
915 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
916 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
917 case AL_DIRECT_CHANNELS_SOFT:
918 case AL_DISTANCE_MODEL:
919 case AL_SOURCE_RELATIVE:
920 case AL_LOOPING:
921 case AL_BUFFER:
922 case AL_SOURCE_STATE:
923 case AL_BUFFERS_QUEUED:
924 case AL_BUFFERS_PROCESSED:
925 case AL_SOURCE_TYPE:
926 case AL_DIRECT_FILTER:
927 case AL_SOURCE_RADIUS:
928 case AL_SOURCE_RESAMPLER_SOFT:
929 case AL_SOURCE_SPATIALIZE_SOFT:
930 return 1;
932 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
933 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
934 return 2;
936 case AL_POSITION:
937 case AL_VELOCITY:
938 case AL_DIRECTION:
939 case AL_AUXILIARY_SEND_FILTER:
940 return 3;
942 case AL_ORIENTATION:
943 return 6;
945 case AL_SEC_OFFSET_LATENCY_SOFT:
946 case AL_SEC_OFFSET_CLOCK_SOFT:
947 break; /* Double only */
948 case AL_STEREO_ANGLES:
949 break; /* Float/double only */
951 return 0;
955 ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values);
956 ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values);
957 ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values);
959 #define CHECKVAL(x) do { \
960 if(!(x)) \
962 alSetError(Context, AL_INVALID_VALUE, "Value out of range"); \
963 return AL_FALSE; \
965 } while(0)
967 #define DO_UPDATEPROPS() do { \
968 ALvoice *voice; \
969 if(SourceShouldUpdate(Source, Context) && \
970 (voice=GetSourceVoice(Source, Context)) != nullptr) \
971 UpdateSourceProps(Source, voice, Context); \
972 else \
973 Source->PropsClean.clear(std::memory_order_release); \
974 } while(0)
976 ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values)
978 ALint ival;
980 switch(prop)
982 case AL_SEC_OFFSET_LATENCY_SOFT:
983 case AL_SEC_OFFSET_CLOCK_SOFT:
984 /* Query only */
985 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
986 "Setting read-only source property 0x%04x", prop);
988 case AL_PITCH:
989 CHECKVAL(*values >= 0.0f);
991 Source->Pitch = *values;
992 DO_UPDATEPROPS();
993 return AL_TRUE;
995 case AL_CONE_INNER_ANGLE:
996 CHECKVAL(*values >= 0.0f && *values <= 360.0f);
998 Source->InnerAngle = *values;
999 DO_UPDATEPROPS();
1000 return AL_TRUE;
1002 case AL_CONE_OUTER_ANGLE:
1003 CHECKVAL(*values >= 0.0f && *values <= 360.0f);
1005 Source->OuterAngle = *values;
1006 DO_UPDATEPROPS();
1007 return AL_TRUE;
1009 case AL_GAIN:
1010 CHECKVAL(*values >= 0.0f);
1012 Source->Gain = *values;
1013 DO_UPDATEPROPS();
1014 return AL_TRUE;
1016 case AL_MAX_DISTANCE:
1017 CHECKVAL(*values >= 0.0f);
1019 Source->MaxDistance = *values;
1020 DO_UPDATEPROPS();
1021 return AL_TRUE;
1023 case AL_ROLLOFF_FACTOR:
1024 CHECKVAL(*values >= 0.0f);
1026 Source->RolloffFactor = *values;
1027 DO_UPDATEPROPS();
1028 return AL_TRUE;
1030 case AL_REFERENCE_DISTANCE:
1031 CHECKVAL(*values >= 0.0f);
1033 Source->RefDistance = *values;
1034 DO_UPDATEPROPS();
1035 return AL_TRUE;
1037 case AL_MIN_GAIN:
1038 CHECKVAL(*values >= 0.0f);
1040 Source->MinGain = *values;
1041 DO_UPDATEPROPS();
1042 return AL_TRUE;
1044 case AL_MAX_GAIN:
1045 CHECKVAL(*values >= 0.0f);
1047 Source->MaxGain = *values;
1048 DO_UPDATEPROPS();
1049 return AL_TRUE;
1051 case AL_CONE_OUTER_GAIN:
1052 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
1054 Source->OuterGain = *values;
1055 DO_UPDATEPROPS();
1056 return AL_TRUE;
1058 case AL_CONE_OUTER_GAINHF:
1059 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
1061 Source->OuterGainHF = *values;
1062 DO_UPDATEPROPS();
1063 return AL_TRUE;
1065 case AL_AIR_ABSORPTION_FACTOR:
1066 CHECKVAL(*values >= 0.0f && *values <= 10.0f);
1068 Source->AirAbsorptionFactor = *values;
1069 DO_UPDATEPROPS();
1070 return AL_TRUE;
1072 case AL_ROOM_ROLLOFF_FACTOR:
1073 CHECKVAL(*values >= 0.0f && *values <= 10.0f);
1075 Source->RoomRolloffFactor = *values;
1076 DO_UPDATEPROPS();
1077 return AL_TRUE;
1079 case AL_DOPPLER_FACTOR:
1080 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
1082 Source->DopplerFactor = *values;
1083 DO_UPDATEPROPS();
1084 return AL_TRUE;
1086 case AL_SEC_OFFSET:
1087 case AL_SAMPLE_OFFSET:
1088 case AL_BYTE_OFFSET:
1089 CHECKVAL(*values >= 0.0f);
1091 Source->OffsetType = prop;
1092 Source->Offset = *values;
1094 if(IsPlayingOrPaused(Source))
1096 ALCdevice *device{Context->Device};
1097 BackendLockGuard _{*device->Backend};
1098 /* Double-check that the source is still playing while we have
1099 * the lock.
1101 if(ALvoice *voice{GetSourceVoice(Source, Context)})
1103 if(ApplyOffset(Source, voice) == AL_FALSE)
1104 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid offset");
1107 return AL_TRUE;
1109 case AL_SOURCE_RADIUS:
1110 CHECKVAL(*values >= 0.0f && std::isfinite(*values));
1112 Source->Radius = *values;
1113 DO_UPDATEPROPS();
1114 return AL_TRUE;
1116 case AL_STEREO_ANGLES:
1117 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]));
1119 Source->StereoPan[0] = values[0];
1120 Source->StereoPan[1] = values[1];
1121 DO_UPDATEPROPS();
1122 return AL_TRUE;
1125 case AL_POSITION:
1126 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1128 Source->Position[0] = values[0];
1129 Source->Position[1] = values[1];
1130 Source->Position[2] = values[2];
1131 DO_UPDATEPROPS();
1132 return AL_TRUE;
1134 case AL_VELOCITY:
1135 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1137 Source->Velocity[0] = values[0];
1138 Source->Velocity[1] = values[1];
1139 Source->Velocity[2] = values[2];
1140 DO_UPDATEPROPS();
1141 return AL_TRUE;
1143 case AL_DIRECTION:
1144 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1146 Source->Direction[0] = values[0];
1147 Source->Direction[1] = values[1];
1148 Source->Direction[2] = values[2];
1149 DO_UPDATEPROPS();
1150 return AL_TRUE;
1152 case AL_ORIENTATION:
1153 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) &&
1154 std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]));
1156 Source->OrientAt[0] = values[0];
1157 Source->OrientAt[1] = values[1];
1158 Source->OrientAt[2] = values[2];
1159 Source->OrientUp[0] = values[3];
1160 Source->OrientUp[1] = values[4];
1161 Source->OrientUp[2] = values[5];
1162 DO_UPDATEPROPS();
1163 return AL_TRUE;
1166 case AL_SOURCE_RELATIVE:
1167 case AL_LOOPING:
1168 case AL_SOURCE_STATE:
1169 case AL_SOURCE_TYPE:
1170 case AL_DISTANCE_MODEL:
1171 case AL_DIRECT_FILTER_GAINHF_AUTO:
1172 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1173 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1174 case AL_DIRECT_CHANNELS_SOFT:
1175 case AL_SOURCE_RESAMPLER_SOFT:
1176 case AL_SOURCE_SPATIALIZE_SOFT:
1177 ival = static_cast<ALint>(values[0]);
1178 return SetSourceiv(Source, Context, prop, &ival);
1180 case AL_BUFFERS_QUEUED:
1181 case AL_BUFFERS_PROCESSED:
1182 ival = static_cast<ALint>(static_cast<ALuint>(values[0]));
1183 return SetSourceiv(Source, Context, prop, &ival);
1185 case AL_BUFFER:
1186 case AL_DIRECT_FILTER:
1187 case AL_AUXILIARY_SEND_FILTER:
1188 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1189 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1190 break;
1193 ERR("Unexpected property: 0x%04x\n", prop);
1194 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source float property 0x%04x", prop);
1197 ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values)
1199 ALCdevice *device{Context->Device};
1200 ALbuffer *buffer{nullptr};
1201 ALfilter *filter{nullptr};
1202 ALeffectslot *slot{nullptr};
1203 ALbufferlistitem *oldlist{nullptr};
1204 std::unique_lock<std::mutex> slotlock;
1205 std::unique_lock<std::mutex> filtlock;
1206 std::unique_lock<std::mutex> buflock;
1207 ALfloat fvals[6];
1209 switch(prop)
1211 case AL_SOURCE_STATE:
1212 case AL_SOURCE_TYPE:
1213 case AL_BUFFERS_QUEUED:
1214 case AL_BUFFERS_PROCESSED:
1215 /* Query only */
1216 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1217 "Setting read-only source property 0x%04x", prop);
1219 case AL_SOURCE_RELATIVE:
1220 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1222 Source->HeadRelative = static_cast<ALboolean>(*values);
1223 DO_UPDATEPROPS();
1224 return AL_TRUE;
1226 case AL_LOOPING:
1227 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1229 Source->Looping = static_cast<ALboolean>(*values);
1230 if(IsPlayingOrPaused(Source))
1232 ALvoice *voice{GetSourceVoice(Source, Context)};
1233 if(voice)
1235 if(Source->Looping)
1236 voice->loop_buffer.store(Source->queue, std::memory_order_release);
1237 else
1238 voice->loop_buffer.store(nullptr, std::memory_order_release);
1240 /* If the source is playing, wait for the current mix to finish
1241 * to ensure it isn't currently looping back or reaching the
1242 * end.
1244 while((device->MixCount.load(std::memory_order_acquire)&1))
1245 std::this_thread::yield();
1248 return AL_TRUE;
1250 case AL_BUFFER:
1251 buflock = std::unique_lock<std::mutex>{device->BufferLock};
1252 if(!(*values == 0 || (buffer=LookupBuffer(device, *values)) != nullptr))
1253 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid buffer ID %u",
1254 *values);
1256 if(buffer && buffer->MappedAccess != 0 &&
1257 !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
1258 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1259 "Setting non-persistently mapped buffer %u", buffer->id);
1260 else
1262 ALenum state = GetSourceState(Source, GetSourceVoice(Source, Context));
1263 if(state == AL_PLAYING || state == AL_PAUSED)
1264 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1265 "Setting buffer on playing or paused source %u", Source->id);
1268 oldlist = Source->queue;
1269 if(buffer != nullptr)
1271 /* Add the selected buffer to a one-item queue */
1272 auto newlist = static_cast<ALbufferlistitem*>(al_calloc(DEF_ALIGN,
1273 FAM_SIZE(ALbufferlistitem, buffers, 1)));
1274 newlist->next.store(nullptr, std::memory_order_relaxed);
1275 newlist->max_samples = buffer->SampleLen;
1276 newlist->num_buffers = 1;
1277 newlist->buffers[0] = buffer;
1278 IncrementRef(&buffer->ref);
1280 /* Source is now Static */
1281 Source->SourceType = AL_STATIC;
1282 Source->queue = newlist;
1284 else
1286 /* Source is now Undetermined */
1287 Source->SourceType = AL_UNDETERMINED;
1288 Source->queue = nullptr;
1290 buflock.unlock();
1292 /* Delete all elements in the previous queue */
1293 while(oldlist != nullptr)
1295 ALbufferlistitem *temp{oldlist};
1296 oldlist = temp->next.load(std::memory_order_relaxed);
1298 for(ALsizei i{0};i < temp->num_buffers;i++)
1300 if(temp->buffers[i])
1301 DecrementRef(&temp->buffers[i]->ref);
1303 al_free(temp);
1305 return AL_TRUE;
1307 case AL_SEC_OFFSET:
1308 case AL_SAMPLE_OFFSET:
1309 case AL_BYTE_OFFSET:
1310 CHECKVAL(*values >= 0);
1312 Source->OffsetType = prop;
1313 Source->Offset = *values;
1315 if(IsPlayingOrPaused(Source))
1317 ALCdevice *device{Context->Device};
1318 BackendLockGuard _{*device->Backend};
1319 if(ALvoice *voice{GetSourceVoice(Source, Context)})
1321 if(ApplyOffset(Source, voice) == AL_FALSE)
1322 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE,
1323 "Invalid source offset");
1326 return AL_TRUE;
1328 case AL_DIRECT_FILTER:
1329 filtlock = std::unique_lock<std::mutex>{device->FilterLock};
1330 if(!(*values == 0 || (filter=LookupFilter(device, *values)) != nullptr))
1331 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u",
1332 *values);
1334 if(!filter)
1336 Source->Direct.Gain = 1.0f;
1337 Source->Direct.GainHF = 1.0f;
1338 Source->Direct.HFReference = LOWPASSFREQREF;
1339 Source->Direct.GainLF = 1.0f;
1340 Source->Direct.LFReference = HIGHPASSFREQREF;
1342 else
1344 Source->Direct.Gain = filter->Gain;
1345 Source->Direct.GainHF = filter->GainHF;
1346 Source->Direct.HFReference = filter->HFReference;
1347 Source->Direct.GainLF = filter->GainLF;
1348 Source->Direct.LFReference = filter->LFReference;
1350 filtlock.unlock();
1351 DO_UPDATEPROPS();
1352 return AL_TRUE;
1354 case AL_DIRECT_FILTER_GAINHF_AUTO:
1355 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1357 Source->DryGainHFAuto = *values;
1358 DO_UPDATEPROPS();
1359 return AL_TRUE;
1361 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1362 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1364 Source->WetGainAuto = *values;
1365 DO_UPDATEPROPS();
1366 return AL_TRUE;
1368 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1369 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1371 Source->WetGainHFAuto = *values;
1372 DO_UPDATEPROPS();
1373 return AL_TRUE;
1375 case AL_DIRECT_CHANNELS_SOFT:
1376 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1378 Source->DirectChannels = *values;
1379 DO_UPDATEPROPS();
1380 return AL_TRUE;
1382 case AL_DISTANCE_MODEL:
1383 CHECKVAL(*values == AL_NONE ||
1384 *values == AL_INVERSE_DISTANCE ||
1385 *values == AL_INVERSE_DISTANCE_CLAMPED ||
1386 *values == AL_LINEAR_DISTANCE ||
1387 *values == AL_LINEAR_DISTANCE_CLAMPED ||
1388 *values == AL_EXPONENT_DISTANCE ||
1389 *values == AL_EXPONENT_DISTANCE_CLAMPED);
1391 Source->mDistanceModel = static_cast<DistanceModel>(*values);
1392 if(Context->SourceDistanceModel)
1393 DO_UPDATEPROPS();
1394 return AL_TRUE;
1396 case AL_SOURCE_RESAMPLER_SOFT:
1397 CHECKVAL(*values >= 0 && *values <= ResamplerMax);
1399 Source->mResampler = static_cast<Resampler>(*values);
1400 DO_UPDATEPROPS();
1401 return AL_TRUE;
1403 case AL_SOURCE_SPATIALIZE_SOFT:
1404 CHECKVAL(*values >= AL_FALSE && *values <= AL_AUTO_SOFT);
1406 Source->mSpatialize = static_cast<SpatializeMode>(*values);
1407 DO_UPDATEPROPS();
1408 return AL_TRUE;
1411 case AL_AUXILIARY_SEND_FILTER:
1412 slotlock = std::unique_lock<std::mutex>{Context->EffectSlotLock};
1413 if(!(values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != nullptr))
1414 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid effect ID %u",
1415 values[0]);
1416 if(static_cast<ALuint>(values[1]) >= static_cast<ALuint>(device->NumAuxSends))
1417 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid send %u", values[1]);
1419 filtlock = std::unique_lock<std::mutex>{device->FilterLock};
1420 if(!(values[2] == 0 || (filter=LookupFilter(device, values[2])) != nullptr))
1421 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u",
1422 values[2]);
1424 if(!filter)
1426 /* Disable filter */
1427 Source->Send[values[1]].Gain = 1.0f;
1428 Source->Send[values[1]].GainHF = 1.0f;
1429 Source->Send[values[1]].HFReference = LOWPASSFREQREF;
1430 Source->Send[values[1]].GainLF = 1.0f;
1431 Source->Send[values[1]].LFReference = HIGHPASSFREQREF;
1433 else
1435 Source->Send[values[1]].Gain = filter->Gain;
1436 Source->Send[values[1]].GainHF = filter->GainHF;
1437 Source->Send[values[1]].HFReference = filter->HFReference;
1438 Source->Send[values[1]].GainLF = filter->GainLF;
1439 Source->Send[values[1]].LFReference = filter->LFReference;
1441 filtlock.unlock();
1443 if(slot != Source->Send[values[1]].Slot && IsPlayingOrPaused(Source))
1445 /* Add refcount on the new slot, and release the previous slot */
1446 if(slot) IncrementRef(&slot->ref);
1447 if(Source->Send[values[1]].Slot)
1448 DecrementRef(&Source->Send[values[1]].Slot->ref);
1449 Source->Send[values[1]].Slot = slot;
1451 /* We must force an update if the auxiliary slot changed on an
1452 * active source, in case the slot is about to be deleted.
1454 ALvoice *voice{GetSourceVoice(Source, Context)};
1455 if(voice) UpdateSourceProps(Source, voice, Context);
1456 else Source->PropsClean.clear(std::memory_order_release);
1458 else
1460 if(slot) IncrementRef(&slot->ref);
1461 if(Source->Send[values[1]].Slot)
1462 DecrementRef(&Source->Send[values[1]].Slot->ref);
1463 Source->Send[values[1]].Slot = slot;
1464 DO_UPDATEPROPS();
1467 return AL_TRUE;
1470 /* 1x float */
1471 case AL_CONE_INNER_ANGLE:
1472 case AL_CONE_OUTER_ANGLE:
1473 case AL_PITCH:
1474 case AL_GAIN:
1475 case AL_MIN_GAIN:
1476 case AL_MAX_GAIN:
1477 case AL_REFERENCE_DISTANCE:
1478 case AL_ROLLOFF_FACTOR:
1479 case AL_CONE_OUTER_GAIN:
1480 case AL_MAX_DISTANCE:
1481 case AL_DOPPLER_FACTOR:
1482 case AL_CONE_OUTER_GAINHF:
1483 case AL_AIR_ABSORPTION_FACTOR:
1484 case AL_ROOM_ROLLOFF_FACTOR:
1485 case AL_SOURCE_RADIUS:
1486 fvals[0] = static_cast<ALfloat>(*values);
1487 return SetSourcefv(Source, Context, prop, fvals);
1489 /* 3x float */
1490 case AL_POSITION:
1491 case AL_VELOCITY:
1492 case AL_DIRECTION:
1493 fvals[0] = static_cast<ALfloat>(values[0]);
1494 fvals[1] = static_cast<ALfloat>(values[1]);
1495 fvals[2] = static_cast<ALfloat>(values[2]);
1496 return SetSourcefv(Source, Context, prop, fvals);
1498 /* 6x float */
1499 case AL_ORIENTATION:
1500 fvals[0] = static_cast<ALfloat>(values[0]);
1501 fvals[1] = static_cast<ALfloat>(values[1]);
1502 fvals[2] = static_cast<ALfloat>(values[2]);
1503 fvals[3] = static_cast<ALfloat>(values[3]);
1504 fvals[4] = static_cast<ALfloat>(values[4]);
1505 fvals[5] = static_cast<ALfloat>(values[5]);
1506 return SetSourcefv(Source, Context, prop, fvals);
1508 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1509 case AL_SEC_OFFSET_LATENCY_SOFT:
1510 case AL_SEC_OFFSET_CLOCK_SOFT:
1511 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1512 case AL_STEREO_ANGLES:
1513 break;
1516 ERR("Unexpected property: 0x%04x\n", prop);
1517 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x",
1518 prop);
1521 ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values)
1523 ALfloat fvals[6];
1524 ALint ivals[3];
1526 switch(prop)
1528 case AL_SOURCE_TYPE:
1529 case AL_BUFFERS_QUEUED:
1530 case AL_BUFFERS_PROCESSED:
1531 case AL_SOURCE_STATE:
1532 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1533 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1534 /* Query only */
1535 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1536 "Setting read-only source property 0x%04x", prop);
1538 /* 1x int */
1539 case AL_SOURCE_RELATIVE:
1540 case AL_LOOPING:
1541 case AL_SEC_OFFSET:
1542 case AL_SAMPLE_OFFSET:
1543 case AL_BYTE_OFFSET:
1544 case AL_DIRECT_FILTER_GAINHF_AUTO:
1545 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1546 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1547 case AL_DIRECT_CHANNELS_SOFT:
1548 case AL_DISTANCE_MODEL:
1549 case AL_SOURCE_RESAMPLER_SOFT:
1550 case AL_SOURCE_SPATIALIZE_SOFT:
1551 CHECKVAL(*values <= INT_MAX && *values >= INT_MIN);
1553 ivals[0] = static_cast<ALint>(*values);
1554 return SetSourceiv(Source, Context, prop, ivals);
1556 /* 1x uint */
1557 case AL_BUFFER:
1558 case AL_DIRECT_FILTER:
1559 CHECKVAL(*values <= UINT_MAX && *values >= 0);
1561 ivals[0] = static_cast<ALuint>(*values);
1562 return SetSourceiv(Source, Context, prop, ivals);
1564 /* 3x uint */
1565 case AL_AUXILIARY_SEND_FILTER:
1566 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 &&
1567 values[1] <= UINT_MAX && values[1] >= 0 &&
1568 values[2] <= UINT_MAX && values[2] >= 0);
1570 ivals[0] = static_cast<ALuint>(values[0]);
1571 ivals[1] = static_cast<ALuint>(values[1]);
1572 ivals[2] = static_cast<ALuint>(values[2]);
1573 return SetSourceiv(Source, Context, prop, ivals);
1575 /* 1x float */
1576 case AL_CONE_INNER_ANGLE:
1577 case AL_CONE_OUTER_ANGLE:
1578 case AL_PITCH:
1579 case AL_GAIN:
1580 case AL_MIN_GAIN:
1581 case AL_MAX_GAIN:
1582 case AL_REFERENCE_DISTANCE:
1583 case AL_ROLLOFF_FACTOR:
1584 case AL_CONE_OUTER_GAIN:
1585 case AL_MAX_DISTANCE:
1586 case AL_DOPPLER_FACTOR:
1587 case AL_CONE_OUTER_GAINHF:
1588 case AL_AIR_ABSORPTION_FACTOR:
1589 case AL_ROOM_ROLLOFF_FACTOR:
1590 case AL_SOURCE_RADIUS:
1591 fvals[0] = static_cast<ALfloat>(*values);
1592 return SetSourcefv(Source, Context, prop, fvals);
1594 /* 3x float */
1595 case AL_POSITION:
1596 case AL_VELOCITY:
1597 case AL_DIRECTION:
1598 fvals[0] = static_cast<ALfloat>(values[0]);
1599 fvals[1] = static_cast<ALfloat>(values[1]);
1600 fvals[2] = static_cast<ALfloat>(values[2]);
1601 return SetSourcefv(Source, Context, prop, fvals);
1603 /* 6x float */
1604 case AL_ORIENTATION:
1605 fvals[0] = static_cast<ALfloat>(values[0]);
1606 fvals[1] = static_cast<ALfloat>(values[1]);
1607 fvals[2] = static_cast<ALfloat>(values[2]);
1608 fvals[3] = static_cast<ALfloat>(values[3]);
1609 fvals[4] = static_cast<ALfloat>(values[4]);
1610 fvals[5] = static_cast<ALfloat>(values[5]);
1611 return SetSourcefv(Source, Context, prop, fvals);
1613 case AL_SEC_OFFSET_LATENCY_SOFT:
1614 case AL_SEC_OFFSET_CLOCK_SOFT:
1615 case AL_STEREO_ANGLES:
1616 break;
1619 ERR("Unexpected property: 0x%04x\n", prop);
1620 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x",
1621 prop);
1624 #undef CHECKVAL
1627 ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values);
1628 ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values);
1629 ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values);
1631 ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values)
1633 ALCdevice *device{Context->Device};
1634 ClockLatency clocktime;
1635 std::chrono::nanoseconds srcclock;
1636 ALint ivals[3];
1637 ALboolean err;
1639 switch(prop)
1641 case AL_GAIN:
1642 *values = Source->Gain;
1643 return AL_TRUE;
1645 case AL_PITCH:
1646 *values = Source->Pitch;
1647 return AL_TRUE;
1649 case AL_MAX_DISTANCE:
1650 *values = Source->MaxDistance;
1651 return AL_TRUE;
1653 case AL_ROLLOFF_FACTOR:
1654 *values = Source->RolloffFactor;
1655 return AL_TRUE;
1657 case AL_REFERENCE_DISTANCE:
1658 *values = Source->RefDistance;
1659 return AL_TRUE;
1661 case AL_CONE_INNER_ANGLE:
1662 *values = Source->InnerAngle;
1663 return AL_TRUE;
1665 case AL_CONE_OUTER_ANGLE:
1666 *values = Source->OuterAngle;
1667 return AL_TRUE;
1669 case AL_MIN_GAIN:
1670 *values = Source->MinGain;
1671 return AL_TRUE;
1673 case AL_MAX_GAIN:
1674 *values = Source->MaxGain;
1675 return AL_TRUE;
1677 case AL_CONE_OUTER_GAIN:
1678 *values = Source->OuterGain;
1679 return AL_TRUE;
1681 case AL_SEC_OFFSET:
1682 case AL_SAMPLE_OFFSET:
1683 case AL_BYTE_OFFSET:
1684 *values = GetSourceOffset(Source, prop, Context);
1685 return AL_TRUE;
1687 case AL_CONE_OUTER_GAINHF:
1688 *values = Source->OuterGainHF;
1689 return AL_TRUE;
1691 case AL_AIR_ABSORPTION_FACTOR:
1692 *values = Source->AirAbsorptionFactor;
1693 return AL_TRUE;
1695 case AL_ROOM_ROLLOFF_FACTOR:
1696 *values = Source->RoomRolloffFactor;
1697 return AL_TRUE;
1699 case AL_DOPPLER_FACTOR:
1700 *values = Source->DopplerFactor;
1701 return AL_TRUE;
1703 case AL_SOURCE_RADIUS:
1704 *values = Source->Radius;
1705 return AL_TRUE;
1707 case AL_STEREO_ANGLES:
1708 values[0] = Source->StereoPan[0];
1709 values[1] = Source->StereoPan[1];
1710 return AL_TRUE;
1712 case AL_SEC_OFFSET_LATENCY_SOFT:
1713 /* Get the source offset with the clock time first. Then get the
1714 * clock time with the device latency. Order is important.
1716 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1717 { std::lock_guard<std::mutex> _{device->StateLock};
1718 clocktime = GetClockLatency(device);
1720 if(srcclock == clocktime.ClockTime)
1721 values[1] = static_cast<ALdouble>(clocktime.Latency.count()) / 1000000000.0;
1722 else
1724 /* If the clock time incremented, reduce the latency by that
1725 * much since it's that much closer to the source offset it got
1726 * earlier.
1728 std::chrono::nanoseconds diff = clocktime.ClockTime - srcclock;
1729 values[1] = static_cast<ALdouble>((clocktime.Latency - std::min(clocktime.Latency, diff)).count()) /
1730 1000000000.0;
1732 return AL_TRUE;
1734 case AL_SEC_OFFSET_CLOCK_SOFT:
1735 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1736 values[1] = srcclock.count() / 1000000000.0;
1737 return AL_TRUE;
1739 case AL_POSITION:
1740 values[0] = Source->Position[0];
1741 values[1] = Source->Position[1];
1742 values[2] = Source->Position[2];
1743 return AL_TRUE;
1745 case AL_VELOCITY:
1746 values[0] = Source->Velocity[0];
1747 values[1] = Source->Velocity[1];
1748 values[2] = Source->Velocity[2];
1749 return AL_TRUE;
1751 case AL_DIRECTION:
1752 values[0] = Source->Direction[0];
1753 values[1] = Source->Direction[1];
1754 values[2] = Source->Direction[2];
1755 return AL_TRUE;
1757 case AL_ORIENTATION:
1758 values[0] = Source->OrientAt[0];
1759 values[1] = Source->OrientAt[1];
1760 values[2] = Source->OrientAt[2];
1761 values[3] = Source->OrientUp[0];
1762 values[4] = Source->OrientUp[1];
1763 values[5] = Source->OrientUp[2];
1764 return AL_TRUE;
1766 /* 1x int */
1767 case AL_SOURCE_RELATIVE:
1768 case AL_LOOPING:
1769 case AL_SOURCE_STATE:
1770 case AL_BUFFERS_QUEUED:
1771 case AL_BUFFERS_PROCESSED:
1772 case AL_SOURCE_TYPE:
1773 case AL_DIRECT_FILTER_GAINHF_AUTO:
1774 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1775 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1776 case AL_DIRECT_CHANNELS_SOFT:
1777 case AL_DISTANCE_MODEL:
1778 case AL_SOURCE_RESAMPLER_SOFT:
1779 case AL_SOURCE_SPATIALIZE_SOFT:
1780 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
1781 *values = static_cast<ALdouble>(ivals[0]);
1782 return err;
1784 case AL_BUFFER:
1785 case AL_DIRECT_FILTER:
1786 case AL_AUXILIARY_SEND_FILTER:
1787 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1788 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1789 break;
1792 ERR("Unexpected property: 0x%04x\n", prop);
1793 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source double property 0x%04x",
1794 prop);
1797 ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values)
1799 ALbufferlistitem *BufferList;
1800 ALdouble dvals[6];
1801 ALboolean err;
1803 switch(prop)
1805 case AL_SOURCE_RELATIVE:
1806 *values = Source->HeadRelative;
1807 return AL_TRUE;
1809 case AL_LOOPING:
1810 *values = Source->Looping;
1811 return AL_TRUE;
1813 case AL_BUFFER:
1814 BufferList = (Source->SourceType == AL_STATIC) ? Source->queue : nullptr;
1815 *values = (BufferList && BufferList->num_buffers >= 1 && BufferList->buffers[0]) ?
1816 BufferList->buffers[0]->id : 0;
1817 return AL_TRUE;
1819 case AL_SOURCE_STATE:
1820 *values = GetSourceState(Source, GetSourceVoice(Source, Context));
1821 return AL_TRUE;
1823 case AL_BUFFERS_QUEUED:
1824 if(!(BufferList=Source->queue))
1825 *values = 0;
1826 else
1828 ALsizei count = 0;
1829 do {
1830 count += BufferList->num_buffers;
1831 BufferList = BufferList->next.load(std::memory_order_relaxed);
1832 } while(BufferList != nullptr);
1833 *values = count;
1835 return AL_TRUE;
1837 case AL_BUFFERS_PROCESSED:
1838 if(Source->Looping || Source->SourceType != AL_STREAMING)
1840 /* Buffers on a looping source are in a perpetual state of
1841 * PENDING, so don't report any as PROCESSED */
1842 *values = 0;
1844 else
1846 const ALbufferlistitem *BufferList{Source->queue};
1847 const ALbufferlistitem *Current{nullptr};
1848 ALsizei played{0};
1850 ALvoice *voice{GetSourceVoice(Source, Context)};
1851 if(voice != nullptr)
1852 Current = voice->current_buffer.load(std::memory_order_relaxed);
1853 else if(Source->state == AL_INITIAL)
1854 Current = BufferList;
1856 while(BufferList && BufferList != Current)
1858 played += BufferList->num_buffers;
1859 BufferList = BufferList->next.load(std::memory_order_relaxed);
1861 *values = played;
1863 return AL_TRUE;
1865 case AL_SOURCE_TYPE:
1866 *values = Source->SourceType;
1867 return AL_TRUE;
1869 case AL_DIRECT_FILTER_GAINHF_AUTO:
1870 *values = Source->DryGainHFAuto;
1871 return AL_TRUE;
1873 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1874 *values = Source->WetGainAuto;
1875 return AL_TRUE;
1877 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1878 *values = Source->WetGainHFAuto;
1879 return AL_TRUE;
1881 case AL_DIRECT_CHANNELS_SOFT:
1882 *values = Source->DirectChannels;
1883 return AL_TRUE;
1885 case AL_DISTANCE_MODEL:
1886 *values = static_cast<int>(Source->mDistanceModel);
1887 return AL_TRUE;
1889 case AL_SOURCE_RESAMPLER_SOFT:
1890 *values = Source->mResampler;
1891 return AL_TRUE;
1893 case AL_SOURCE_SPATIALIZE_SOFT:
1894 *values = Source->mSpatialize;
1895 return AL_TRUE;
1897 /* 1x float/double */
1898 case AL_CONE_INNER_ANGLE:
1899 case AL_CONE_OUTER_ANGLE:
1900 case AL_PITCH:
1901 case AL_GAIN:
1902 case AL_MIN_GAIN:
1903 case AL_MAX_GAIN:
1904 case AL_REFERENCE_DISTANCE:
1905 case AL_ROLLOFF_FACTOR:
1906 case AL_CONE_OUTER_GAIN:
1907 case AL_MAX_DISTANCE:
1908 case AL_SEC_OFFSET:
1909 case AL_SAMPLE_OFFSET:
1910 case AL_BYTE_OFFSET:
1911 case AL_DOPPLER_FACTOR:
1912 case AL_AIR_ABSORPTION_FACTOR:
1913 case AL_ROOM_ROLLOFF_FACTOR:
1914 case AL_CONE_OUTER_GAINHF:
1915 case AL_SOURCE_RADIUS:
1916 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1917 *values = static_cast<ALint>(dvals[0]);
1918 return err;
1920 /* 3x float/double */
1921 case AL_POSITION:
1922 case AL_VELOCITY:
1923 case AL_DIRECTION:
1924 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1926 values[0] = static_cast<ALint>(dvals[0]);
1927 values[1] = static_cast<ALint>(dvals[1]);
1928 values[2] = static_cast<ALint>(dvals[2]);
1930 return err;
1932 /* 6x float/double */
1933 case AL_ORIENTATION:
1934 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1936 values[0] = static_cast<ALint>(dvals[0]);
1937 values[1] = static_cast<ALint>(dvals[1]);
1938 values[2] = static_cast<ALint>(dvals[2]);
1939 values[3] = static_cast<ALint>(dvals[3]);
1940 values[4] = static_cast<ALint>(dvals[4]);
1941 values[5] = static_cast<ALint>(dvals[5]);
1943 return err;
1945 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1946 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1947 break; /* i64 only */
1948 case AL_SEC_OFFSET_LATENCY_SOFT:
1949 case AL_SEC_OFFSET_CLOCK_SOFT:
1950 break; /* Double only */
1951 case AL_STEREO_ANGLES:
1952 break; /* Float/double only */
1954 case AL_DIRECT_FILTER:
1955 case AL_AUXILIARY_SEND_FILTER:
1956 break; /* ??? */
1959 ERR("Unexpected property: 0x%04x\n", prop);
1960 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x",
1961 prop);
1964 ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values)
1966 ALCdevice *device = Context->Device;
1967 ClockLatency clocktime;
1968 std::chrono::nanoseconds srcclock;
1969 ALdouble dvals[6];
1970 ALint ivals[3];
1971 ALboolean err;
1973 switch(prop)
1975 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1976 /* Get the source offset with the clock time first. Then get the
1977 * clock time with the device latency. Order is important.
1979 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
1980 { std::lock_guard<std::mutex> _{device->StateLock};
1981 clocktime = GetClockLatency(device);
1983 if(srcclock == clocktime.ClockTime)
1984 values[1] = clocktime.Latency.count();
1985 else
1987 /* If the clock time incremented, reduce the latency by that
1988 * much since it's that much closer to the source offset it got
1989 * earlier.
1991 auto diff = clocktime.ClockTime - srcclock;
1992 values[1] = (clocktime.Latency - std::min(clocktime.Latency, diff)).count();
1994 return AL_TRUE;
1996 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1997 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
1998 values[1] = srcclock.count();
1999 return AL_TRUE;
2001 /* 1x float/double */
2002 case AL_CONE_INNER_ANGLE:
2003 case AL_CONE_OUTER_ANGLE:
2004 case AL_PITCH:
2005 case AL_GAIN:
2006 case AL_MIN_GAIN:
2007 case AL_MAX_GAIN:
2008 case AL_REFERENCE_DISTANCE:
2009 case AL_ROLLOFF_FACTOR:
2010 case AL_CONE_OUTER_GAIN:
2011 case AL_MAX_DISTANCE:
2012 case AL_SEC_OFFSET:
2013 case AL_SAMPLE_OFFSET:
2014 case AL_BYTE_OFFSET:
2015 case AL_DOPPLER_FACTOR:
2016 case AL_AIR_ABSORPTION_FACTOR:
2017 case AL_ROOM_ROLLOFF_FACTOR:
2018 case AL_CONE_OUTER_GAINHF:
2019 case AL_SOURCE_RADIUS:
2020 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
2021 *values = static_cast<ALint64>(dvals[0]);
2022 return err;
2024 /* 3x float/double */
2025 case AL_POSITION:
2026 case AL_VELOCITY:
2027 case AL_DIRECTION:
2028 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
2030 values[0] = static_cast<ALint64>(dvals[0]);
2031 values[1] = static_cast<ALint64>(dvals[1]);
2032 values[2] = static_cast<ALint64>(dvals[2]);
2034 return err;
2036 /* 6x float/double */
2037 case AL_ORIENTATION:
2038 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
2040 values[0] = static_cast<ALint64>(dvals[0]);
2041 values[1] = static_cast<ALint64>(dvals[1]);
2042 values[2] = static_cast<ALint64>(dvals[2]);
2043 values[3] = static_cast<ALint64>(dvals[3]);
2044 values[4] = static_cast<ALint64>(dvals[4]);
2045 values[5] = static_cast<ALint64>(dvals[5]);
2047 return err;
2049 /* 1x int */
2050 case AL_SOURCE_RELATIVE:
2051 case AL_LOOPING:
2052 case AL_SOURCE_STATE:
2053 case AL_BUFFERS_QUEUED:
2054 case AL_BUFFERS_PROCESSED:
2055 case AL_SOURCE_TYPE:
2056 case AL_DIRECT_FILTER_GAINHF_AUTO:
2057 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2058 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2059 case AL_DIRECT_CHANNELS_SOFT:
2060 case AL_DISTANCE_MODEL:
2061 case AL_SOURCE_RESAMPLER_SOFT:
2062 case AL_SOURCE_SPATIALIZE_SOFT:
2063 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
2064 *values = ivals[0];
2065 return err;
2067 /* 1x uint */
2068 case AL_BUFFER:
2069 case AL_DIRECT_FILTER:
2070 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
2071 *values = static_cast<ALuint>(ivals[0]);
2072 return err;
2074 /* 3x uint */
2075 case AL_AUXILIARY_SEND_FILTER:
2076 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
2078 values[0] = static_cast<ALuint>(ivals[0]);
2079 values[1] = static_cast<ALuint>(ivals[1]);
2080 values[2] = static_cast<ALuint>(ivals[2]);
2082 return err;
2084 case AL_SEC_OFFSET_LATENCY_SOFT:
2085 case AL_SEC_OFFSET_CLOCK_SOFT:
2086 break; /* Double only */
2087 case AL_STEREO_ANGLES:
2088 break; /* Float/double only */
2091 ERR("Unexpected property: 0x%04x\n", prop);
2092 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x",
2093 prop);
2096 } // namespace
2098 AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
2100 ContextRef context{GetContextRef()};
2101 if(UNLIKELY(!context)) return;
2103 if(n < 0)
2104 alSetError(context.get(), AL_INVALID_VALUE, "Generating %d sources", n);
2105 else if(n == 1)
2107 ALsource *source = AllocSource(context.get());
2108 if(source) sources[0] = source->id;
2110 else
2112 al::vector<ALuint> tempids(n);
2113 auto alloc_end = std::find_if_not(tempids.begin(), tempids.end(),
2114 [&context](ALuint &id) -> bool
2116 ALsource *source{AllocSource(context.get())};
2117 if(!source) return false;
2118 id = source->id;
2119 return true;
2122 if(alloc_end != tempids.end())
2123 alDeleteSources(static_cast<ALsizei>(std::distance(tempids.begin(), alloc_end)),
2124 tempids.data());
2125 else
2126 std::copy(tempids.cbegin(), tempids.cend(), sources);
2131 AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
2133 ContextRef context{GetContextRef()};
2134 if(UNLIKELY(!context)) return;
2136 if(n < 0)
2137 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Deleting %d sources", n);
2139 std::lock_guard<std::mutex> _{context->SourceLock};
2141 /* Check that all Sources are valid */
2142 const ALuint *sources_end = sources + n;
2143 auto invsrc = std::find_if_not(sources, sources_end,
2144 [&context](ALuint sid) -> bool
2146 if(!LookupSource(context.get(), sid))
2148 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", sid);
2149 return false;
2151 return true;
2154 if(LIKELY(invsrc == sources_end))
2156 /* All good. Delete source IDs. */
2157 std::for_each(sources, sources_end,
2158 [&context](ALuint sid) -> void
2160 ALsource *src{LookupSource(context.get(), sid)};
2161 if(src) FreeSource(context.get(), src);
2168 AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
2170 ContextRef context{GetContextRef()};
2171 if(LIKELY(context))
2173 std::lock_guard<std::mutex> _{context->SourceLock};
2174 if(LookupSource(context.get(), source) != nullptr)
2175 return AL_TRUE;
2177 return AL_FALSE;
2181 AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
2183 ContextRef context{GetContextRef()};
2184 if(UNLIKELY(!context)) return;
2186 std::lock_guard<std::mutex> _{context->PropLock};
2187 std::lock_guard<std::mutex> __{context->SourceLock};
2188 ALsource *Source = LookupSource(context.get(), source);
2189 if(UNLIKELY(!Source))
2190 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2191 else if(FloatValsByProp(param) != 1)
2192 alSetError(context.get(), AL_INVALID_ENUM, "Invalid float property 0x%04x", param);
2193 else
2194 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), &value);
2197 AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
2199 ContextRef context{GetContextRef()};
2200 if(UNLIKELY(!context)) return;
2202 std::lock_guard<std::mutex> _{context->PropLock};
2203 std::lock_guard<std::mutex> __{context->SourceLock};
2204 ALsource *Source = LookupSource(context.get(), source);
2205 if(UNLIKELY(!Source))
2206 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2207 else if(FloatValsByProp(param) != 3)
2208 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param);
2209 else
2211 ALfloat fvals[3] = { value1, value2, value3 };
2212 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2216 AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
2218 ContextRef context{GetContextRef()};
2219 if(UNLIKELY(!context)) return;
2221 std::lock_guard<std::mutex> _{context->PropLock};
2222 std::lock_guard<std::mutex> __{context->SourceLock};
2223 ALsource *Source = LookupSource(context.get(), source);
2224 if(UNLIKELY(!Source))
2225 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2226 else if(!values)
2227 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2228 else if(FloatValsByProp(param) < 1)
2229 alSetError(context.get(), AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param);
2230 else
2231 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), values);
2235 AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
2237 ContextRef context{GetContextRef()};
2238 if(UNLIKELY(!context)) return;
2240 std::lock_guard<std::mutex> _{context->PropLock};
2241 std::lock_guard<std::mutex> __{context->SourceLock};
2242 ALsource *Source = LookupSource(context.get(), source);
2243 if(UNLIKELY(!Source))
2244 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2245 else if(DoubleValsByProp(param) != 1)
2246 alSetError(context.get(), AL_INVALID_ENUM, "Invalid double property 0x%04x", param);
2247 else
2249 ALfloat fval = static_cast<ALfloat>(value);
2250 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), &fval);
2254 AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
2256 ContextRef context{GetContextRef()};
2257 if(UNLIKELY(!context)) return;
2259 std::lock_guard<std::mutex> _{context->PropLock};
2260 std::lock_guard<std::mutex> __{context->SourceLock};
2261 ALsource *Source = LookupSource(context.get(), source);
2262 if(UNLIKELY(!Source))
2263 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2264 else if(DoubleValsByProp(param) != 3)
2265 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param);
2266 else {
2267 ALfloat fvals[3] = {static_cast<ALfloat>(value1),
2268 static_cast<ALfloat>(value2),
2269 static_cast<ALfloat>(value3)};
2270 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2274 AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
2276 ContextRef context{GetContextRef()};
2277 if(UNLIKELY(!context)) return;
2279 std::lock_guard<std::mutex> _{context->PropLock};
2280 std::lock_guard<std::mutex> __{context->SourceLock};
2281 ALsource *Source = LookupSource(context.get(), source);
2282 if(UNLIKELY(!Source))
2283 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2284 else if(!values)
2285 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2286 else
2288 ALint count{DoubleValsByProp(param)};
2289 if(count < 1 || count > 6)
2290 alSetError(context.get(), AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param);
2291 else
2293 ALfloat fvals[6];
2294 ALint i;
2296 for(i = 0;i < count;i++)
2297 fvals[i] = static_cast<ALfloat>(values[i]);
2298 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2304 AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
2306 ContextRef context{GetContextRef()};
2307 if(UNLIKELY(!context)) return;
2309 std::lock_guard<std::mutex> _{context->PropLock};
2310 std::lock_guard<std::mutex> __{context->SourceLock};
2311 ALsource *Source = LookupSource(context.get(), source);
2312 if(UNLIKELY(!Source))
2313 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2314 else if(IntValsByProp(param) != 1)
2315 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer property 0x%04x", param);
2316 else
2317 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), &value);
2320 AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
2322 ContextRef context{GetContextRef()};
2323 if(UNLIKELY(!context)) return;
2325 std::lock_guard<std::mutex> _{context->PropLock};
2326 std::lock_guard<std::mutex> __{context->SourceLock};
2327 ALsource *Source = LookupSource(context.get(), source);
2328 if(UNLIKELY(!Source))
2329 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2330 else if(IntValsByProp(param) != 3)
2331 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param);
2332 else
2334 ALint ivals[3] = { value1, value2, value3 };
2335 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals);
2339 AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
2341 ContextRef context{GetContextRef()};
2342 if(UNLIKELY(!context)) return;
2344 std::lock_guard<std::mutex> _{context->PropLock};
2345 std::lock_guard<std::mutex> __{context->SourceLock};
2346 ALsource *Source = LookupSource(context.get(), source);
2347 if(UNLIKELY(!Source))
2348 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2349 else if(!values)
2350 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2351 else if(IntValsByProp(param) < 1)
2352 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param);
2353 else
2354 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), values);
2358 AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
2360 ContextRef context{GetContextRef()};
2361 if(UNLIKELY(!context)) return;
2363 std::lock_guard<std::mutex> _{context->PropLock};
2364 std::lock_guard<std::mutex> __{context->SourceLock};
2365 ALsource *Source{LookupSource(context.get(), source)};
2366 if(UNLIKELY(!Source))
2367 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2368 else if(Int64ValsByProp(param) != 1)
2369 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param);
2370 else
2371 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), &value);
2374 AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
2376 ContextRef context{GetContextRef()};
2377 if(UNLIKELY(!context)) return;
2379 std::lock_guard<std::mutex> _{context->PropLock};
2380 std::lock_guard<std::mutex> __{context->SourceLock};
2381 ALsource *Source{LookupSource(context.get(), source)};
2382 if(UNLIKELY(!Source))
2383 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2384 else if(Int64ValsByProp(param) != 3)
2385 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param);
2386 else
2388 ALint64SOFT i64vals[3] = { value1, value2, value3 };
2389 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals);
2393 AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
2395 ContextRef context{GetContextRef()};
2396 if(UNLIKELY(!context)) return;
2398 std::lock_guard<std::mutex> _{context->PropLock};
2399 std::lock_guard<std::mutex> __{context->SourceLock};
2400 ALsource *Source{LookupSource(context.get(), source)};
2401 if(UNLIKELY(!Source))
2402 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2403 else if(!values)
2404 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2405 else if(Int64ValsByProp(param) < 1)
2406 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param);
2407 else
2408 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), values);
2412 AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
2414 ContextRef context{GetContextRef()};
2415 if(UNLIKELY(!context)) return;
2417 std::lock_guard<std::mutex> _{context->SourceLock};
2418 ALsource *Source{LookupSource(context.get(), source)};
2419 if(UNLIKELY(!Source))
2420 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2421 else if(!value)
2422 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2423 else if(FloatValsByProp(param) != 1)
2424 alSetError(context.get(), AL_INVALID_ENUM, "Invalid float property 0x%04x", param);
2425 else
2427 ALdouble dval;
2428 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), &dval))
2429 *value = static_cast<ALfloat>(dval);
2434 AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
2436 ContextRef context{GetContextRef()};
2437 if(UNLIKELY(!context)) return;
2439 std::lock_guard<std::mutex> _{context->SourceLock};
2440 ALsource *Source{LookupSource(context.get(), source)};
2441 if(UNLIKELY(!Source))
2442 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2443 else if(!(value1 && value2 && value3))
2444 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2445 else if(FloatValsByProp(param) != 3)
2446 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param);
2447 else
2449 ALdouble dvals[3];
2450 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2452 *value1 = static_cast<ALfloat>(dvals[0]);
2453 *value2 = static_cast<ALfloat>(dvals[1]);
2454 *value3 = static_cast<ALfloat>(dvals[2]);
2460 AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
2462 ContextRef context{GetContextRef()};
2463 if(UNLIKELY(!context)) return;
2465 std::lock_guard<std::mutex> _{context->SourceLock};
2466 ALsource *Source{LookupSource(context.get(), source)};
2467 if(UNLIKELY(!Source))
2468 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2469 else if(!values)
2470 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2471 else
2473 ALint count{FloatValsByProp(param)};
2474 if(count < 1 && count > 6)
2475 alSetError(context.get(), AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param);
2476 else
2478 ALdouble dvals[6];
2479 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2481 for(ALint i{0};i < count;i++)
2482 values[i] = static_cast<ALfloat>(dvals[i]);
2489 AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
2491 ContextRef context{GetContextRef()};
2492 if(UNLIKELY(!context)) return;
2494 std::lock_guard<std::mutex> _{context->SourceLock};
2495 ALsource *Source{LookupSource(context.get(), source)};
2496 if(UNLIKELY(!Source))
2497 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2498 else if(!value)
2499 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2500 else if(DoubleValsByProp(param) != 1)
2501 alSetError(context.get(), AL_INVALID_ENUM, "Invalid double property 0x%04x", param);
2502 else
2503 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), value);
2506 AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
2508 ContextRef context{GetContextRef()};
2509 if(UNLIKELY(!context)) return;
2511 std::lock_guard<std::mutex> _{context->SourceLock};
2512 ALsource *Source{LookupSource(context.get(), source)};
2513 if(UNLIKELY(!Source))
2514 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2515 else if(!(value1 && value2 && value3))
2516 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2517 else if(DoubleValsByProp(param) != 3)
2518 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param);
2519 else
2521 ALdouble dvals[3];
2522 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2524 *value1 = dvals[0];
2525 *value2 = dvals[1];
2526 *value3 = dvals[2];
2531 AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
2533 ContextRef context{GetContextRef()};
2534 if(UNLIKELY(!context)) return;
2536 std::lock_guard<std::mutex> _{context->SourceLock};
2537 ALsource *Source{LookupSource(context.get(), source)};
2538 if(UNLIKELY(!Source))
2539 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2540 else if(!values)
2541 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2542 else if(DoubleValsByProp(param) < 1)
2543 alSetError(context.get(), AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param);
2544 else
2545 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), values);
2549 AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
2551 ContextRef context{GetContextRef()};
2552 if(UNLIKELY(!context)) return;
2554 std::lock_guard<std::mutex> _{context->SourceLock};
2555 ALsource *Source{LookupSource(context.get(), source)};
2556 if(UNLIKELY(!Source))
2557 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2558 else if(!value)
2559 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2560 else if(IntValsByProp(param) != 1)
2561 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer property 0x%04x", param);
2562 else
2563 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), value);
2567 AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
2569 ContextRef context{GetContextRef()};
2570 if(UNLIKELY(!context)) return;
2572 std::lock_guard<std::mutex> _{context->SourceLock};
2573 ALsource *Source{LookupSource(context.get(), source)};
2574 if(UNLIKELY(!Source))
2575 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2576 else if(!(value1 && value2 && value3))
2577 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2578 else if(IntValsByProp(param) != 3)
2579 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param);
2580 else
2582 ALint ivals[3];
2583 if(GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals))
2585 *value1 = ivals[0];
2586 *value2 = ivals[1];
2587 *value3 = ivals[2];
2593 AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
2595 ContextRef context{GetContextRef()};
2596 if(UNLIKELY(!context)) return;
2598 std::lock_guard<std::mutex> _{context->SourceLock};
2599 ALsource *Source{LookupSource(context.get(), source)};
2600 if(UNLIKELY(!Source))
2601 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2602 else if(!values)
2603 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2604 else if(IntValsByProp(param) < 1)
2605 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param);
2606 else
2607 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), values);
2611 AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
2613 ContextRef context{GetContextRef()};
2614 if(UNLIKELY(!context)) return;
2616 std::lock_guard<std::mutex> _{context->SourceLock};
2617 ALsource *Source{LookupSource(context.get(), source)};
2618 if(UNLIKELY(!Source))
2619 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2620 else if(!value)
2621 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2622 else if(Int64ValsByProp(param) != 1)
2623 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param);
2624 else
2625 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), value);
2628 AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
2630 ContextRef context{GetContextRef()};
2631 if(UNLIKELY(!context)) return;
2633 std::lock_guard<std::mutex> _{context->SourceLock};
2634 ALsource *Source{LookupSource(context.get(), source)};
2635 if(UNLIKELY(!Source))
2636 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2637 else if(!(value1 && value2 && value3))
2638 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2639 else if(Int64ValsByProp(param) != 3)
2640 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param);
2641 else
2643 ALint64 i64vals[3];
2644 if(GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals))
2646 *value1 = i64vals[0];
2647 *value2 = i64vals[1];
2648 *value3 = i64vals[2];
2653 AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
2655 ContextRef context{GetContextRef()};
2656 if(UNLIKELY(!context)) return;
2658 std::lock_guard<std::mutex> _{context->SourceLock};
2659 ALsource *Source{LookupSource(context.get(), source)};
2660 if(UNLIKELY(!Source))
2661 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2662 else if(!values)
2663 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2664 else if(Int64ValsByProp(param) < 1)
2665 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param);
2666 else
2667 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), values);
2671 AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source)
2673 alSourcePlayv(1, &source);
2675 AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
2677 ContextRef context{GetContextRef()};
2678 if(UNLIKELY(!context)) return;
2680 if(n < 0)
2681 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Playing %d sources", n);
2682 if(n == 0) return;
2684 std::lock_guard<std::mutex> _{context->SourceLock};
2685 auto sources_end = sources+n;
2686 auto bad_sid = std::find_if_not(sources, sources_end,
2687 [&context](ALuint sid) -> bool
2689 ALsource *source{LookupSource(context.get(), sid)};
2690 return LIKELY(source != nullptr);
2693 if(UNLIKELY(bad_sid != sources+n))
2694 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", *bad_sid);
2696 ALCdevice *device{context->Device};
2697 BackendLockGuard __{*device->Backend};
2698 /* If the device is disconnected, go right to stopped. */
2699 if(UNLIKELY(!device->Connected.load(std::memory_order_acquire)))
2701 /* TODO: Send state change event? */
2702 std::for_each(sources, sources_end,
2703 [&context](ALuint sid) -> void
2705 ALsource *source{LookupSource(context.get(), sid)};
2706 source->OffsetType = AL_NONE;
2707 source->Offset = 0.0;
2708 source->state = AL_STOPPED;
2711 return;
2714 while(n > context->MaxVoices-context->VoiceCount.load(std::memory_order_relaxed))
2716 if(UNLIKELY(context->MaxVoices > std::numeric_limits<ALsizei>::max()>>1))
2717 SETERR_RETURN(context.get(), AL_OUT_OF_MEMORY,,
2718 "Overflow increasing voice count from %d", context->MaxVoices);
2719 ALsizei newcount = context->MaxVoices << 1;
2720 AllocateVoices(context.get(), newcount, device->NumAuxSends);
2723 auto start_source = [&context,device](ALuint sid) -> void
2725 ALsource *source{LookupSource(context.get(), sid)};
2726 /* Check that there is a queue containing at least one valid, non zero
2727 * length buffer.
2729 ALbufferlistitem *BufferList{source->queue};
2730 while(BufferList && BufferList->max_samples == 0)
2731 BufferList = BufferList->next.load(std::memory_order_relaxed);
2733 /* If there's nothing to play, go right to stopped. */
2734 if(UNLIKELY(!BufferList))
2736 /* NOTE: A source without any playable buffers should not have an
2737 * ALvoice since it shouldn't be in a playing or paused state. So
2738 * there's no need to look up its voice and clear the source.
2740 ALenum oldstate{GetSourceState(source, nullptr)};
2741 source->OffsetType = AL_NONE;
2742 source->Offset = 0.0;
2743 if(oldstate != AL_STOPPED)
2745 source->state = AL_STOPPED;
2746 SendStateChangeEvent(context.get(), source->id, AL_STOPPED);
2748 return;
2751 ALvoice *voice{GetSourceVoice(source, context.get())};
2752 switch(GetSourceState(source, voice))
2754 case AL_PLAYING:
2755 assert(voice != nullptr);
2756 /* A source that's already playing is restarted from the beginning. */
2757 voice->current_buffer.store(BufferList, std::memory_order_relaxed);
2758 voice->position.store(0u, std::memory_order_relaxed);
2759 voice->position_fraction.store(0, std::memory_order_release);
2760 return;
2762 case AL_PAUSED:
2763 assert(voice != nullptr);
2764 /* A source that's paused simply resumes. */
2765 voice->Playing.store(true, std::memory_order_release);
2766 source->state = AL_PLAYING;
2767 SendStateChangeEvent(context.get(), source->id, AL_PLAYING);
2768 return;
2770 default:
2771 assert(voice == nullptr);
2772 break;
2775 /* Look for an unused voice to play this source with. */
2776 auto voices_end = context->Voices + context->VoiceCount.load(std::memory_order_relaxed);
2777 auto voice_iter = std::find_if(context->Voices, voices_end,
2778 [](const ALvoice *voice) noexcept -> bool
2779 { return voice->SourceID.load(std::memory_order_relaxed) == 0u; }
2781 auto vidx = static_cast<ALint>(std::distance(context->Voices, voice_iter));
2782 voice = *voice_iter;
2783 voice->Playing.store(false, std::memory_order_release);
2784 if(voice_iter == voices_end) context->VoiceCount.fetch_add(1, std::memory_order_acq_rel);
2786 source->PropsClean.test_and_set(std::memory_order_acquire);
2787 UpdateSourceProps(source, voice, context.get());
2789 /* A source that's not playing or paused has any offset applied when it
2790 * starts playing.
2792 if(source->Looping)
2793 voice->loop_buffer.store(source->queue, std::memory_order_relaxed);
2794 else
2795 voice->loop_buffer.store(nullptr, std::memory_order_relaxed);
2796 voice->current_buffer.store(BufferList, std::memory_order_relaxed);
2797 voice->position.store(0u, std::memory_order_relaxed);
2798 voice->position_fraction.store(0, std::memory_order_relaxed);
2799 bool start_fading{false};
2800 if(ApplyOffset(source, voice) != AL_FALSE)
2801 start_fading = voice->position.load(std::memory_order_relaxed) != 0 ||
2802 voice->position_fraction.load(std::memory_order_relaxed) != 0 ||
2803 voice->current_buffer.load(std::memory_order_relaxed) != BufferList;
2805 auto buffers_end = BufferList->buffers + BufferList->num_buffers;
2806 auto buffer = std::find_if(BufferList->buffers, buffers_end,
2807 std::bind(std::not_equal_to<const ALbuffer*>{}, _1, nullptr));
2808 if(buffer != buffers_end)
2810 voice->NumChannels = ChannelsFromFmt((*buffer)->mFmtChannels);
2811 voice->SampleSize = BytesFromFmt((*buffer)->mFmtType);
2814 /* Clear previous samples. */
2815 for(auto &samples : voice->PrevSamples)
2816 std::fill(std::begin(samples), std::end(samples), 0.0f);
2818 /* Clear the stepping value so the mixer knows not to mix this until
2819 * the update gets applied.
2821 voice->Step = 0;
2823 voice->Flags = start_fading ? VOICE_IS_FADING : 0;
2824 if(source->SourceType == AL_STATIC) voice->Flags |= VOICE_IS_STATIC;
2826 std::fill_n(std::begin(voice->Direct.Params), voice->NumChannels, DirectParams{});
2827 std::for_each(voice->Send+0, voice->Send+source->Send.size(),
2828 [voice](ALvoice::SendData &send) -> void
2829 { std::fill_n(std::begin(send.Params), voice->NumChannels, SendParams{}); }
2832 if(device->AvgSpeakerDist > 0.0f)
2834 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
2835 (device->AvgSpeakerDist * device->Frequency);
2836 std::for_each(voice->Direct.Params+0, voice->Direct.Params+voice->NumChannels,
2837 [w1](DirectParams &parms) noexcept -> void
2838 { parms.NFCtrlFilter.init(0.0f, w1); }
2842 voice->SourceID.store(source->id, std::memory_order_relaxed);
2843 voice->Playing.store(true, std::memory_order_release);
2844 source->state = AL_PLAYING;
2845 source->VoiceIdx = vidx;
2847 SendStateChangeEvent(context.get(), source->id, AL_PLAYING);
2849 std::for_each(sources, sources_end, start_source);
2852 AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source)
2854 alSourcePausev(1, &source);
2856 AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
2858 ContextRef context{GetContextRef()};
2859 if(UNLIKELY(!context)) return;
2861 if(n < 0)
2862 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Pausing %d sources", n);
2863 if(n == 0) return;
2865 std::lock_guard<std::mutex> _{context->SourceLock};
2866 for(ALsizei i{0};i < n;i++)
2868 if(!LookupSource(context.get(), sources[i]))
2869 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]);
2872 ALCdevice *device{context->Device};
2873 BackendLockGuard __{*device->Backend};
2874 for(ALsizei i{0};i < n;i++)
2876 ALsource *source{LookupSource(context.get(), sources[i])};
2877 ALvoice *voice{GetSourceVoice(source, context.get())};
2878 if(voice) voice->Playing.store(false, std::memory_order_release);
2879 if(GetSourceState(source, voice) == AL_PLAYING)
2881 source->state = AL_PAUSED;
2882 SendStateChangeEvent(context.get(), source->id, AL_PAUSED);
2887 AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source)
2889 alSourceStopv(1, &source);
2891 AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
2893 ContextRef context{GetContextRef()};
2894 if(UNLIKELY(!context)) return;
2896 if(n < 0)
2897 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Stopping %d sources", n);
2898 if(n == 0) return;
2900 std::lock_guard<std::mutex> _{context->SourceLock};
2901 for(ALsizei i{0};i < n;i++)
2903 if(!LookupSource(context.get(), sources[i]))
2904 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]);
2907 ALCdevice *device{context->Device};
2908 BackendLockGuard __{*device->Backend};
2909 for(ALsizei i{0};i < n;i++)
2911 ALsource *source{LookupSource(context.get(), sources[i])};
2912 ALvoice *voice{GetSourceVoice(source, context.get())};
2913 if(voice != nullptr)
2915 voice->SourceID.store(0u, std::memory_order_relaxed);
2916 voice->Playing.store(false, std::memory_order_release);
2917 voice = nullptr;
2919 ALenum oldstate{GetSourceState(source, voice)};
2920 if(oldstate != AL_INITIAL && oldstate != AL_STOPPED)
2922 source->state = AL_STOPPED;
2923 SendStateChangeEvent(context.get(), source->id, AL_STOPPED);
2925 source->OffsetType = AL_NONE;
2926 source->Offset = 0.0;
2930 AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source)
2932 alSourceRewindv(1, &source);
2934 AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
2936 ContextRef context{GetContextRef()};
2937 if(UNLIKELY(!context)) return;
2939 if(n < 0)
2940 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Rewinding %d sources", n);
2941 if(n == 0) return;
2943 std::lock_guard<std::mutex> _{context->SourceLock};
2944 for(ALsizei i{0};i < n;i++)
2946 if(!LookupSource(context.get(), sources[i]))
2947 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]);
2950 ALCdevice *device{context->Device};
2951 BackendLockGuard __{*device->Backend};
2952 for(ALsizei i{0};i < n;i++)
2954 ALsource *source{LookupSource(context.get(), sources[i])};
2955 ALvoice *voice{GetSourceVoice(source, context.get())};
2956 if(voice != nullptr)
2958 voice->SourceID.store(0u, std::memory_order_relaxed);
2959 voice->Playing.store(false, std::memory_order_release);
2960 voice = nullptr;
2962 if(GetSourceState(source, voice) != AL_INITIAL)
2964 source->state = AL_INITIAL;
2965 SendStateChangeEvent(context.get(), source->id, AL_INITIAL);
2967 source->OffsetType = AL_NONE;
2968 source->Offset = 0.0;
2973 AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
2975 ContextRef context{GetContextRef()};
2976 if(UNLIKELY(!context)) return;
2978 if(nb < 0)
2979 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Queueing %d buffers", nb);
2980 if(nb == 0) return;
2982 std::lock_guard<std::mutex> _{context->SourceLock};
2983 ALsource *source{LookupSource(context.get(),src)};
2984 if(UNLIKELY(!source))
2985 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src);
2987 /* Can't queue on a Static Source */
2988 if(UNLIKELY(source->SourceType == AL_STATIC))
2989 SETERR_RETURN(context.get(), AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
2991 /* Check for a valid Buffer, for its frequency and format */
2992 ALCdevice *device{context->Device};
2993 ALbuffer *BufferFmt{nullptr};
2994 ALbufferlistitem *BufferList{source->queue};
2995 while(BufferList)
2997 for(ALsizei i{0};i < BufferList->num_buffers;i++)
2999 if((BufferFmt=BufferList->buffers[i]) != nullptr)
3000 break;
3002 if(BufferFmt) break;
3003 BufferList = BufferList->next.load(std::memory_order_relaxed);
3006 std::unique_lock<std::mutex> buflock{device->BufferLock};
3007 ALbufferlistitem *BufferListStart{nullptr};
3008 BufferList = nullptr;
3009 for(ALsizei i{0};i < nb;i++)
3011 ALbuffer *buffer{nullptr};
3012 if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr)
3014 alSetError(context.get(), AL_INVALID_NAME, "Queueing invalid buffer ID %u",
3015 buffers[i]);
3016 goto buffer_error;
3019 if(!BufferListStart)
3021 BufferListStart = static_cast<ALbufferlistitem*>(al_calloc(DEF_ALIGN,
3022 FAM_SIZE(ALbufferlistitem, buffers, 1)));
3023 BufferList = BufferListStart;
3025 else
3027 auto item = static_cast<ALbufferlistitem*>(al_calloc(DEF_ALIGN,
3028 FAM_SIZE(ALbufferlistitem, buffers, 1)));
3029 BufferList->next.store(item, std::memory_order_relaxed);
3030 BufferList = item;
3032 BufferList->next.store(nullptr, std::memory_order_relaxed);
3033 BufferList->max_samples = buffer ? buffer->SampleLen : 0;
3034 BufferList->num_buffers = 1;
3035 BufferList->buffers[0] = buffer;
3036 if(!buffer) continue;
3038 IncrementRef(&buffer->ref);
3040 if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
3042 alSetError(context.get(), AL_INVALID_OPERATION,
3043 "Queueing non-persistently mapped buffer %u", buffer->id);
3044 goto buffer_error;
3047 if(BufferFmt == nullptr)
3048 BufferFmt = buffer;
3049 else if(BufferFmt->Frequency != buffer->Frequency ||
3050 BufferFmt->mFmtChannels != buffer->mFmtChannels ||
3051 BufferFmt->OriginalType != buffer->OriginalType)
3053 alSetError(context.get(), AL_INVALID_OPERATION,
3054 "Queueing buffer with mismatched format");
3056 buffer_error:
3057 /* A buffer failed (invalid ID or format), so unlock and release
3058 * each buffer we had. */
3059 while(BufferListStart)
3061 ALbufferlistitem *next = BufferListStart->next.load(std::memory_order_relaxed);
3062 for(i = 0;i < BufferListStart->num_buffers;i++)
3064 if((buffer=BufferListStart->buffers[i]) != nullptr)
3065 DecrementRef(&buffer->ref);
3067 al_free(BufferListStart);
3068 BufferListStart = next;
3070 return;
3073 /* All buffers good. */
3074 buflock.unlock();
3076 /* Source is now streaming */
3077 source->SourceType = AL_STREAMING;
3079 if(!(BufferList=source->queue))
3080 source->queue = BufferListStart;
3081 else
3083 ALbufferlistitem *next;
3084 while((next=BufferList->next.load(std::memory_order_relaxed)) != nullptr)
3085 BufferList = next;
3086 BufferList->next.store(BufferListStart, std::memory_order_release);
3090 AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers)
3092 ContextRef context{GetContextRef()};
3093 if(UNLIKELY(!context)) return;
3095 if(nb < 0)
3096 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Queueing %d buffer layers", nb);
3097 if(nb == 0) return;
3099 std::lock_guard<std::mutex> _{context->SourceLock};
3100 ALsource *source{LookupSource(context.get(),src)};
3101 if(UNLIKELY(!source))
3102 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src);
3104 /* Can't queue on a Static Source */
3105 if(UNLIKELY(source->SourceType == AL_STATIC))
3106 SETERR_RETURN(context.get(), AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
3108 /* Check for a valid Buffer, for its frequency and format */
3109 ALCdevice *device{context->Device};
3110 ALbuffer *BufferFmt{nullptr};
3111 ALbufferlistitem *BufferList{source->queue};
3112 while(BufferList)
3114 for(ALsizei i{0};i < BufferList->num_buffers;i++)
3116 if((BufferFmt=BufferList->buffers[i]) != nullptr)
3117 break;
3119 if(BufferFmt) break;
3120 BufferList = BufferList->next.load(std::memory_order_relaxed);
3123 std::unique_lock<std::mutex> buflock{device->BufferLock};
3124 auto BufferListStart = static_cast<ALbufferlistitem*>(al_calloc(DEF_ALIGN,
3125 FAM_SIZE(ALbufferlistitem, buffers, nb)));
3126 BufferList = BufferListStart;
3127 BufferList->next.store(nullptr, std::memory_order_relaxed);
3128 BufferList->max_samples = 0;
3129 BufferList->num_buffers = 0;
3131 for(ALsizei i{0};i < nb;i++)
3133 ALbuffer *buffer{nullptr};
3134 if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr)
3136 alSetError(context.get(), AL_INVALID_NAME, "Queueing invalid buffer ID %u",
3137 buffers[i]);
3138 goto buffer_error;
3141 BufferList->buffers[BufferList->num_buffers++] = buffer;
3142 if(!buffer) continue;
3144 IncrementRef(&buffer->ref);
3146 BufferList->max_samples = maxi(BufferList->max_samples, buffer->SampleLen);
3148 if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
3150 alSetError(context.get(), AL_INVALID_OPERATION,
3151 "Queueing non-persistently mapped buffer %u", buffer->id);
3152 goto buffer_error;
3155 if(BufferFmt == nullptr)
3156 BufferFmt = buffer;
3157 else if(BufferFmt->Frequency != buffer->Frequency ||
3158 BufferFmt->mFmtChannels != buffer->mFmtChannels ||
3159 BufferFmt->OriginalType != buffer->OriginalType)
3161 alSetError(context.get(), AL_INVALID_OPERATION,
3162 "Queueing buffer with mismatched format");
3164 buffer_error:
3165 /* A buffer failed (invalid ID or format), so unlock and release
3166 * each buffer we had. */
3167 while(BufferListStart)
3169 ALbufferlistitem *next{BufferListStart->next.load(std::memory_order_relaxed)};
3170 for(i = 0;i < BufferListStart->num_buffers;i++)
3172 if((buffer=BufferListStart->buffers[i]) != nullptr)
3173 DecrementRef(&buffer->ref);
3175 al_free(BufferListStart);
3176 BufferListStart = next;
3178 return;
3181 /* All buffers good. */
3182 buflock.unlock();
3184 /* Source is now streaming */
3185 source->SourceType = AL_STREAMING;
3187 if(!(BufferList=source->queue))
3188 source->queue = BufferListStart;
3189 else
3191 ALbufferlistitem *next;
3192 while((next=BufferList->next.load(std::memory_order_relaxed)) != nullptr)
3193 BufferList = next;
3194 BufferList->next.store(BufferListStart, std::memory_order_release);
3198 AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
3200 ContextRef context{GetContextRef()};
3201 if(UNLIKELY(!context)) return;
3203 if(nb < 0)
3204 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing %d buffers", nb);
3205 if(nb == 0) return;
3207 std::lock_guard<std::mutex> _{context->SourceLock};
3208 ALsource *source{LookupSource(context.get(),src)};
3209 if(UNLIKELY(!source))
3210 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src);
3212 if(UNLIKELY(source->Looping))
3213 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing from looping source %u", src);
3214 if(UNLIKELY(source->SourceType != AL_STREAMING))
3215 SETERR_RETURN(context.get(), AL_INVALID_VALUE,,
3216 "Unqueueing from a non-streaming source %u", src);
3218 /* Make sure enough buffers have been processed to unqueue. */
3219 ALbufferlistitem *BufferList{source->queue};
3220 ALvoice *voice{GetSourceVoice(source, context.get())};
3221 ALbufferlistitem *Current{nullptr};
3222 if(voice)
3223 Current = voice->current_buffer.load(std::memory_order_relaxed);
3224 else if(source->state == AL_INITIAL)
3225 Current = BufferList;
3226 if(UNLIKELY(BufferList == Current))
3227 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing pending buffers");
3229 ALsizei i{BufferList->num_buffers};
3230 while(i < nb)
3232 /* If the next bufferlist to check is NULL or is the current one, it's
3233 * trying to unqueue pending buffers.
3235 ALbufferlistitem *next{BufferList->next.load(std::memory_order_relaxed)};
3236 if(UNLIKELY(!next) || UNLIKELY(next == Current))
3237 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing pending buffers");
3238 BufferList = next;
3240 i += BufferList->num_buffers;
3243 while(nb > 0)
3245 ALbufferlistitem *head{source->queue};
3246 ALbufferlistitem *next{head->next.load(std::memory_order_relaxed)};
3247 for(i = 0;i < head->num_buffers && nb > 0;i++,nb--)
3249 ALbuffer *buffer{head->buffers[i]};
3250 if(!buffer)
3251 *(buffers++) = 0;
3252 else
3254 *(buffers++) = buffer->id;
3255 DecrementRef(&buffer->ref);
3258 if(i < head->num_buffers)
3260 /* This head has some buffers left over, so move them to the front
3261 * and update the sample and buffer count.
3263 ALsizei max_length{0};
3264 ALsizei j{0};
3265 while(i < head->num_buffers)
3267 ALbuffer *buffer{head->buffers[i++]};
3268 if(buffer) max_length = maxi(max_length, buffer->SampleLen);
3269 head->buffers[j++] = buffer;
3271 head->max_samples = max_length;
3272 head->num_buffers = j;
3273 break;
3276 /* Otherwise, free this item and set the source queue head to the next
3277 * one.
3279 al_free(head);
3280 source->queue = next;
3285 ALsource::ALsource(ALsizei num_sends)
3287 InnerAngle = 360.0f;
3288 OuterAngle = 360.0f;
3289 Pitch = 1.0f;
3290 Position[0] = 0.0f;
3291 Position[1] = 0.0f;
3292 Position[2] = 0.0f;
3293 Velocity[0] = 0.0f;
3294 Velocity[1] = 0.0f;
3295 Velocity[2] = 0.0f;
3296 Direction[0] = 0.0f;
3297 Direction[1] = 0.0f;
3298 Direction[2] = 0.0f;
3299 OrientAt[0] = 0.0f;
3300 OrientAt[1] = 0.0f;
3301 OrientAt[2] = -1.0f;
3302 OrientUp[0] = 0.0f;
3303 OrientUp[1] = 1.0f;
3304 OrientUp[2] = 0.0f;
3305 RefDistance = 1.0f;
3306 MaxDistance = std::numeric_limits<float>::max();
3307 RolloffFactor = 1.0f;
3308 Gain = 1.0f;
3309 MinGain = 0.0f;
3310 MaxGain = 1.0f;
3311 OuterGain = 0.0f;
3312 OuterGainHF = 1.0f;
3314 DryGainHFAuto = AL_TRUE;
3315 WetGainAuto = AL_TRUE;
3316 WetGainHFAuto = AL_TRUE;
3317 AirAbsorptionFactor = 0.0f;
3318 RoomRolloffFactor = 0.0f;
3319 DopplerFactor = 1.0f;
3320 HeadRelative = AL_FALSE;
3321 Looping = AL_FALSE;
3322 mDistanceModel = DistanceModel::Default;
3323 mResampler = ResamplerDefault;
3324 DirectChannels = AL_FALSE;
3325 mSpatialize = SpatializeAuto;
3327 StereoPan[0] = Deg2Rad( 30.0f);
3328 StereoPan[1] = Deg2Rad(-30.0f);
3330 Radius = 0.0f;
3332 Direct.Gain = 1.0f;
3333 Direct.GainHF = 1.0f;
3334 Direct.HFReference = LOWPASSFREQREF;
3335 Direct.GainLF = 1.0f;
3336 Direct.LFReference = HIGHPASSFREQREF;
3337 Send.resize(num_sends);
3338 for(auto &send : Send)
3340 send.Slot = nullptr;
3341 send.Gain = 1.0f;
3342 send.GainHF = 1.0f;
3343 send.HFReference = LOWPASSFREQREF;
3344 send.GainLF = 1.0f;
3345 send.LFReference = HIGHPASSFREQREF;
3348 Offset = 0.0;
3349 OffsetType = AL_NONE;
3350 SourceType = AL_UNDETERMINED;
3351 state = AL_INITIAL;
3353 queue = nullptr;
3355 VoiceIdx = -1;
3358 ALsource::~ALsource()
3360 ALbufferlistitem *BufferList{queue};
3361 while(BufferList != nullptr)
3363 ALbufferlistitem *next{BufferList->next.load(std::memory_order_relaxed)};
3364 for(ALsizei i{0};i < BufferList->num_buffers;i++)
3366 if(BufferList->buffers[i])
3367 DecrementRef(&BufferList->buffers[i]->ref);
3369 al_free(BufferList);
3370 BufferList = next;
3372 queue = nullptr;
3374 std::for_each(Send.begin(), Send.end(),
3375 [](ALsource::SendData &send) -> void
3377 if(send.Slot)
3378 DecrementRef(&send.Slot->ref);
3379 send.Slot = nullptr;
3384 void UpdateAllSourceProps(ALCcontext *context)
3386 auto voices_end = context->Voices + context->VoiceCount.load(std::memory_order_relaxed);
3387 std::for_each(context->Voices, voices_end,
3388 [context](ALvoice *voice) -> void
3390 ALuint sid{voice->SourceID.load(std::memory_order_acquire)};
3391 ALsource *source = sid ? LookupSource(context, sid) : nullptr;
3392 if(source && !source->PropsClean.test_and_set(std::memory_order_acq_rel))
3393 UpdateSourceProps(source, voice, context);
3398 SourceSubList::~SourceSubList()
3400 ALuint64 usemask = ~FreeMask;
3401 while(usemask)
3403 ALsizei idx{CTZ64(usemask)};
3404 Sources[idx].~ALsource();
3405 usemask &= ~(1_u64 << idx);
3407 FreeMask = ~usemask;
3408 al_free(Sources);
3409 Sources = nullptr;