Make the context VoiceCount atomic
[openal-soft.git] / OpenAL32 / alSource.cpp
blob8ee94600d38da3aef33335f0a0ac4e79ed87fbaf
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 <stdlib.h>
24 #include <limits.h>
25 #include <float.h>
27 #include <cmath>
28 #include <limits>
29 #include <algorithm>
31 #include "AL/al.h"
32 #include "AL/alc.h"
34 #include "alMain.h"
35 #include "alcontext.h"
36 #include "alError.h"
37 #include "alSource.h"
38 #include "alBuffer.h"
39 #include "alFilter.h"
40 #include "alAuxEffectSlot.h"
41 #include "ringbuffer.h"
43 #include "backends/base.h"
45 #include "threads.h"
46 #include "almalloc.h"
49 namespace {
51 inline ALvoice *GetSourceVoice(ALsource *source, ALCcontext *context)
53 ALint idx{source->VoiceIdx};
54 if(idx >= 0 && idx < context->VoiceCount.load(std::memory_order_relaxed))
56 ALvoice *voice{context->Voices[idx]};
57 if(voice->Source.load(std::memory_order_acquire) == source)
58 return voice;
60 source->VoiceIdx = -1;
61 return nullptr;
64 void UpdateSourceProps(ALsource *source, ALvoice *voice, ALCcontext *context)
66 /* Get an unused property container, or allocate a new one as needed. */
67 ALvoiceProps *props{context->FreeVoiceProps.load(std::memory_order_acquire)};
68 if(!props)
69 props = static_cast<ALvoiceProps*>(al_calloc(16,
70 FAM_SIZE(ALvoiceProps, Send, source->Send.size()))
72 else
74 ALvoiceProps *next;
75 do {
76 next = props->next.load(std::memory_order_relaxed);
77 } while(context->FreeVoiceProps.compare_exchange_weak(props, next,
78 std::memory_order_acq_rel, std::memory_order_acquire) == 0);
81 /* Copy in current property values. */
82 props->Pitch = source->Pitch;
83 props->Gain = source->Gain;
84 props->OuterGain = source->OuterGain;
85 props->MinGain = source->MinGain;
86 props->MaxGain = source->MaxGain;
87 props->InnerAngle = source->InnerAngle;
88 props->OuterAngle = source->OuterAngle;
89 props->RefDistance = source->RefDistance;
90 props->MaxDistance = source->MaxDistance;
91 props->RolloffFactor = source->RolloffFactor;
92 std::copy_n(source->Position, 3, props->Position);
93 std::copy_n(source->Velocity, 3, props->Velocity);
94 std::copy_n(source->Direction, 3, props->Direction);
95 for(ALsizei i{0};i < 2;i++)
96 std::copy_n(source->Orientation[i], 3, props->Orientation[i]);
97 props->HeadRelative = source->HeadRelative;
98 props->mDistanceModel = source->mDistanceModel;
99 props->Resampler = source->Resampler;
100 props->DirectChannels = source->DirectChannels;
101 props->SpatializeMode = source->Spatialize;
103 props->DryGainHFAuto = source->DryGainHFAuto;
104 props->WetGainAuto = source->WetGainAuto;
105 props->WetGainHFAuto = source->WetGainHFAuto;
106 props->OuterGainHF = source->OuterGainHF;
108 props->AirAbsorptionFactor = source->AirAbsorptionFactor;
109 props->RoomRolloffFactor = source->RoomRolloffFactor;
110 props->DopplerFactor = source->DopplerFactor;
112 std::copy_n(source->StereoPan, 2, props->StereoPan);
114 props->Radius = source->Radius;
116 props->Direct.Gain = source->Direct.Gain;
117 props->Direct.GainHF = source->Direct.GainHF;
118 props->Direct.HFReference = source->Direct.HFReference;
119 props->Direct.GainLF = source->Direct.GainLF;
120 props->Direct.LFReference = source->Direct.LFReference;
122 for(size_t i{0u};i < source->Send.size();i++)
124 props->Send[i].Slot = source->Send[i].Slot;
125 props->Send[i].Gain = source->Send[i].Gain;
126 props->Send[i].GainHF = source->Send[i].GainHF;
127 props->Send[i].HFReference = source->Send[i].HFReference;
128 props->Send[i].GainLF = source->Send[i].GainLF;
129 props->Send[i].LFReference = source->Send[i].LFReference;
132 /* Set the new container for updating internal parameters. */
133 props = voice->Update.exchange(props, std::memory_order_acq_rel);
134 if(props)
136 /* If there was an unused update container, put it back in the
137 * freelist.
139 AtomicReplaceHead(context->FreeVoiceProps, props);
144 /* GetSourceSampleOffset
146 * Gets the current read offset for the given Source, in 32.32 fixed-point
147 * samples. The offset is relative to the start of the queue (not the start of
148 * the current buffer).
150 ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, std::chrono::nanoseconds *clocktime)
152 ALCdevice *device{context->Device};
153 const ALbufferlistitem *Current;
154 ALuint64 readPos;
155 ALuint refcount;
156 ALvoice *voice;
158 do {
159 Current = nullptr;
160 readPos = 0;
161 while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
162 althrd_yield();
163 *clocktime = GetDeviceClockTime(device);
165 voice = GetSourceVoice(Source, context);
166 if(voice)
168 Current = voice->current_buffer.load(std::memory_order_relaxed);
170 readPos = (ALuint64)voice->position.load(std::memory_order_relaxed) << 32;
171 readPos |= (ALuint64)voice->position_fraction.load(std::memory_order_relaxed) <<
172 (32-FRACTIONBITS);
174 std::atomic_thread_fence(std::memory_order_acquire);
175 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
177 if(voice)
179 const ALbufferlistitem *BufferList{Source->queue};
180 while(BufferList && BufferList != Current)
182 readPos += (ALuint64)BufferList->max_samples << 32;
183 BufferList = BufferList->next.load(std::memory_order_relaxed);
185 readPos = minu64(readPos, U64(0x7fffffffffffffff));
188 return (ALint64)readPos;
191 /* GetSourceSecOffset
193 * Gets the current read offset for the given Source, in seconds. The offset is
194 * relative to the start of the queue (not the start of the current buffer).
196 ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, std::chrono::nanoseconds *clocktime)
198 ALCdevice *device{context->Device};
199 const ALbufferlistitem *Current;
200 ALuint64 readPos;
201 ALuint refcount;
202 ALvoice *voice;
204 do {
205 Current = nullptr;
206 readPos = 0;
207 while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
208 althrd_yield();
209 *clocktime = GetDeviceClockTime(device);
211 voice = GetSourceVoice(Source, context);
212 if(voice)
214 Current = voice->current_buffer.load(std::memory_order_relaxed);
216 readPos = (ALuint64)voice->position.load(std::memory_order_relaxed) << FRACTIONBITS;
217 readPos |= voice->position_fraction.load(std::memory_order_relaxed);
219 std::atomic_thread_fence(std::memory_order_acquire);
220 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
222 ALdouble offset{0.0};
223 if(voice)
225 const ALbufferlistitem *BufferList{Source->queue};
226 const ALbuffer *BufferFmt{nullptr};
227 while(BufferList && BufferList != Current)
229 for(ALsizei i{0};!BufferFmt && i < BufferList->num_buffers;++i)
230 BufferFmt = BufferList->buffers[i];
231 readPos += (ALuint64)BufferList->max_samples << FRACTIONBITS;
232 BufferList = BufferList->next.load(std::memory_order_relaxed);
235 while(BufferList && !BufferFmt)
237 for(ALsizei i{0};!BufferFmt && i < BufferList->num_buffers;++i)
238 BufferFmt = BufferList->buffers[i];
239 BufferList = BufferList->next.load(std::memory_order_relaxed);
241 assert(BufferFmt != nullptr);
243 offset = (ALdouble)readPos / (ALdouble)FRACTIONONE /
244 (ALdouble)BufferFmt->Frequency;
247 return offset;
250 /* GetSourceOffset
252 * Gets the current read offset for the given Source, in the appropriate format
253 * (Bytes, Samples or Seconds). The offset is relative to the start of the
254 * queue (not the start of the current buffer).
256 ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
258 ALCdevice *device{context->Device};
259 const ALbufferlistitem *Current;
260 ALuint readPos;
261 ALsizei readPosFrac;
262 ALuint refcount;
263 ALvoice *voice;
265 do {
266 Current = nullptr;
267 readPos = readPosFrac = 0;
268 while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
269 althrd_yield();
270 voice = GetSourceVoice(Source, context);
271 if(voice)
273 Current = voice->current_buffer.load(std::memory_order_relaxed);
275 readPos = voice->position.load(std::memory_order_relaxed);
276 readPosFrac = voice->position_fraction.load(std::memory_order_relaxed);
278 std::atomic_thread_fence(std::memory_order_acquire);
279 } while(refcount != device->MixCount.load(std::memory_order_relaxed));
281 ALdouble offset{0.0};
282 if(voice)
284 const ALbufferlistitem *BufferList{Source->queue};
285 const ALbuffer *BufferFmt{nullptr};
286 ALboolean readFin{AL_FALSE};
287 ALuint totalBufferLen{0u};
289 while(BufferList)
291 for(ALsizei i{0};!BufferFmt && i < BufferList->num_buffers;++i)
292 BufferFmt = BufferList->buffers[i];
294 readFin |= (BufferList == Current);
295 totalBufferLen += BufferList->max_samples;
296 if(!readFin) readPos += BufferList->max_samples;
298 BufferList = BufferList->next.load(std::memory_order_relaxed);
300 assert(BufferFmt != nullptr);
302 if(Source->Looping)
303 readPos %= totalBufferLen;
304 else
306 /* Wrap back to 0 */
307 if(readPos >= totalBufferLen)
308 readPos = readPosFrac = 0;
311 offset = 0.0;
312 switch(name)
314 case AL_SEC_OFFSET:
315 offset = (readPos + (ALdouble)readPosFrac/FRACTIONONE) / BufferFmt->Frequency;
316 break;
318 case AL_SAMPLE_OFFSET:
319 offset = readPos + (ALdouble)readPosFrac/FRACTIONONE;
320 break;
322 case AL_BYTE_OFFSET:
323 if(BufferFmt->OriginalType == UserFmtIMA4)
325 ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4;
326 ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels);
327 ALuint FrameBlockSize = BufferFmt->OriginalAlign;
329 /* Round down to nearest ADPCM block */
330 offset = (ALdouble)(readPos / FrameBlockSize * BlockSize);
332 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
334 ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7;
335 ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels);
336 ALuint FrameBlockSize = BufferFmt->OriginalAlign;
338 /* Round down to nearest ADPCM block */
339 offset = (ALdouble)(readPos / FrameBlockSize * BlockSize);
341 else
343 ALuint FrameSize = FrameSizeFromFmt(BufferFmt->FmtChannels,
344 BufferFmt->FmtType);
345 offset = (ALdouble)(readPos * FrameSize);
347 break;
351 return offset;
355 /* GetSampleOffset
357 * Retrieves the sample offset into the Source's queue (from the Sample, Byte
358 * or Second offset supplied by the application). This takes into account the
359 * fact that the buffer format may have been modifed since.
361 ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac)
363 const ALbuffer *BufferFmt{nullptr};
364 const ALbufferlistitem *BufferList;
366 /* Find the first valid Buffer in the Queue */
367 BufferList = Source->queue;
368 while(BufferList)
370 for(ALsizei i{0};i < BufferList->num_buffers && !BufferFmt;i++)
371 BufferFmt = BufferList->buffers[i];
372 if(BufferFmt) break;
373 BufferList = BufferList->next.load(std::memory_order_relaxed);
375 if(!BufferFmt)
377 Source->OffsetType = AL_NONE;
378 Source->Offset = 0.0;
379 return AL_FALSE;
382 ALdouble dbloff, dblfrac;
383 switch(Source->OffsetType)
385 case AL_BYTE_OFFSET:
386 /* Determine the ByteOffset (and ensure it is block aligned) */
387 *offset = (ALuint)Source->Offset;
388 if(BufferFmt->OriginalType == UserFmtIMA4)
390 ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4;
391 *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels);
392 *offset *= BufferFmt->OriginalAlign;
394 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
396 ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7;
397 *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels);
398 *offset *= BufferFmt->OriginalAlign;
400 else
401 *offset /= FrameSizeFromFmt(BufferFmt->FmtChannels, BufferFmt->FmtType);
402 *frac = 0;
403 break;
405 case AL_SAMPLE_OFFSET:
406 dblfrac = modf(Source->Offset, &dbloff);
407 *offset = (ALuint)mind(dbloff, std::numeric_limits<unsigned int>::max());
408 *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0);
409 break;
411 case AL_SEC_OFFSET:
412 dblfrac = modf(Source->Offset*BufferFmt->Frequency, &dbloff);
413 *offset = (ALuint)mind(dbloff, std::numeric_limits<unsigned int>::max());
414 *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0);
415 break;
417 Source->OffsetType = AL_NONE;
418 Source->Offset = 0.0;
420 return AL_TRUE;
423 /* ApplyOffset
425 * Apply the stored playback offset to the Source. This function will update
426 * the number of buffers "played" given the stored offset.
428 ALboolean ApplyOffset(ALsource *Source, ALvoice *voice)
430 /* Get sample frame offset */
431 ALuint offset{0u};
432 ALsizei frac{0};
433 if(!GetSampleOffset(Source, &offset, &frac))
434 return AL_FALSE;
436 ALuint totalBufferLen{0u};
437 ALbufferlistitem *BufferList{Source->queue};
438 while(BufferList && totalBufferLen <= offset)
440 if((ALuint)BufferList->max_samples > offset-totalBufferLen)
442 /* Offset is in this buffer */
443 voice->position.store(offset - totalBufferLen, std::memory_order_relaxed);
444 voice->position_fraction.store(frac, std::memory_order_relaxed);
445 voice->current_buffer.store(BufferList, std::memory_order_release);
446 return AL_TRUE;
448 totalBufferLen += BufferList->max_samples;
450 BufferList = BufferList->next.load(std::memory_order_relaxed);
453 /* Offset is out of range of the queue */
454 return AL_FALSE;
458 ALsource *AllocSource(ALCcontext *context)
460 ALCdevice *device{context->Device};
461 std::lock_guard<almtx_t> _{context->SourceLock};
462 if(context->NumSources >= device->SourcesMax)
464 alSetError(context, AL_OUT_OF_MEMORY, "Exceeding %u source limit", device->SourcesMax);
465 return nullptr;
467 auto sublist = std::find_if(context->SourceList.begin(), context->SourceList.end(),
468 [](const SourceSubList &entry) -> bool
469 { return entry.FreeMask != 0; }
471 ALsizei lidx = std::distance(context->SourceList.begin(), sublist);
472 ALsource *source;
473 ALsizei slidx;
474 if(LIKELY(sublist != context->SourceList.end()))
476 slidx = CTZ64(sublist->FreeMask);
477 source = sublist->Sources + slidx;
479 else
481 /* Don't allocate so many list entries that the 32-bit ID could
482 * overflow...
484 if(UNLIKELY(context->SourceList.size() >= 1<<25))
486 alSetError(context, AL_OUT_OF_MEMORY, "Too many sources allocated");
487 return nullptr;
489 context->SourceList.emplace_back();
490 sublist = context->SourceList.end() - 1;
492 sublist->FreeMask = ~U64(0);
493 sublist->Sources = static_cast<ALsource*>(al_calloc(16, sizeof(ALsource)*64));
494 if(UNLIKELY(!sublist->Sources))
496 context->SourceList.pop_back();
497 alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate source batch");
498 return nullptr;
501 slidx = 0;
502 source = sublist->Sources + slidx;
505 source = new (source) ALsource{device->NumAuxSends};
507 /* Add 1 to avoid source ID 0. */
508 source->id = ((lidx<<6) | slidx) + 1;
510 context->NumSources += 1;
511 sublist->FreeMask &= ~(U64(1)<<slidx);
513 return source;
516 void FreeSource(ALCcontext *context, ALsource *source)
518 ALuint id = source->id - 1;
519 ALsizei lidx = id >> 6;
520 ALsizei slidx = id & 0x3f;
522 ALCdevice *device{context->Device};
523 ALCdevice_Lock(device);
524 ALvoice *voice{GetSourceVoice(source, context)};
525 if(voice)
527 voice->Source.store(nullptr, std::memory_order_relaxed);
528 voice->Playing.store(false, std::memory_order_release);
530 ALCdevice_Unlock(device);
532 source->~ALsource();
534 context->SourceList[lidx].FreeMask |= U64(1) << slidx;
535 context->NumSources--;
539 inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept
541 ALuint lidx = (id-1) >> 6;
542 ALsizei slidx = (id-1) & 0x3f;
544 if(UNLIKELY(lidx >= context->SourceList.size()))
545 return nullptr;
546 SourceSubList &sublist{context->SourceList[lidx]};
547 if(UNLIKELY(sublist.FreeMask & (U64(1)<<slidx)))
548 return nullptr;
549 return sublist.Sources + slidx;
552 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
554 ALuint lidx = (id-1) >> 6;
555 ALsizei slidx = (id-1) & 0x3f;
557 if(UNLIKELY(lidx >= device->BufferList.size()))
558 return nullptr;
559 BufferSubList &sublist = device->BufferList[lidx];
560 if(UNLIKELY(sublist.FreeMask & (U64(1)<<slidx)))
561 return nullptr;
562 return sublist.Buffers + slidx;
565 inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept
567 ALuint lidx = (id-1) >> 6;
568 ALsizei slidx = (id-1) & 0x3f;
570 if(UNLIKELY(lidx >= device->FilterList.size()))
571 return nullptr;
572 FilterSubList &sublist = device->FilterList[lidx];
573 if(UNLIKELY(sublist.FreeMask & (U64(1)<<slidx)))
574 return nullptr;
575 return sublist.Filters + slidx;
578 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
580 --id;
581 if(UNLIKELY(id >= context->EffectSlotList.size()))
582 return nullptr;
583 return context->EffectSlotList[id].get();
587 enum SourceProp {
588 srcPitch = AL_PITCH,
589 srcGain = AL_GAIN,
590 srcMinGain = AL_MIN_GAIN,
591 srcMaxGain = AL_MAX_GAIN,
592 srcMaxDistance = AL_MAX_DISTANCE,
593 srcRolloffFactor = AL_ROLLOFF_FACTOR,
594 srcDopplerFactor = AL_DOPPLER_FACTOR,
595 srcConeOuterGain = AL_CONE_OUTER_GAIN,
596 srcSecOffset = AL_SEC_OFFSET,
597 srcSampleOffset = AL_SAMPLE_OFFSET,
598 srcByteOffset = AL_BYTE_OFFSET,
599 srcConeInnerAngle = AL_CONE_INNER_ANGLE,
600 srcConeOuterAngle = AL_CONE_OUTER_ANGLE,
601 srcRefDistance = AL_REFERENCE_DISTANCE,
603 srcPosition = AL_POSITION,
604 srcVelocity = AL_VELOCITY,
605 srcDirection = AL_DIRECTION,
607 srcSourceRelative = AL_SOURCE_RELATIVE,
608 srcLooping = AL_LOOPING,
609 srcBuffer = AL_BUFFER,
610 srcSourceState = AL_SOURCE_STATE,
611 srcBuffersQueued = AL_BUFFERS_QUEUED,
612 srcBuffersProcessed = AL_BUFFERS_PROCESSED,
613 srcSourceType = AL_SOURCE_TYPE,
615 /* ALC_EXT_EFX */
616 srcConeOuterGainHF = AL_CONE_OUTER_GAINHF,
617 srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR,
618 srcRoomRolloffFactor = AL_ROOM_ROLLOFF_FACTOR,
619 srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO,
620 srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO,
621 srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO,
622 srcDirectFilter = AL_DIRECT_FILTER,
623 srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER,
625 /* AL_SOFT_direct_channels */
626 srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT,
628 /* AL_EXT_source_distance_model */
629 srcDistanceModel = AL_DISTANCE_MODEL,
631 /* AL_SOFT_source_latency */
632 srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT,
633 srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT,
635 /* AL_EXT_STEREO_ANGLES */
636 srcAngles = AL_STEREO_ANGLES,
638 /* AL_EXT_SOURCE_RADIUS */
639 srcRadius = AL_SOURCE_RADIUS,
641 /* AL_EXT_BFORMAT */
642 srcOrientation = AL_ORIENTATION,
644 /* AL_SOFT_source_resampler */
645 srcResampler = AL_SOURCE_RESAMPLER_SOFT,
647 /* AL_SOFT_source_spatialize */
648 srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT,
650 /* ALC_SOFT_device_clock */
651 srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT,
652 srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT,
656 * Returns if the last known state for the source was playing or paused. Does
657 * not sync with the mixer voice.
659 inline bool IsPlayingOrPaused(ALsource *source)
660 { return source->state == AL_PLAYING || source->state == AL_PAUSED; }
663 * Returns an updated source state using the matching voice's status (or lack
664 * thereof).
666 inline ALenum GetSourceState(ALsource *source, ALvoice *voice)
668 if(!voice && source->state == AL_PLAYING)
669 source->state = AL_STOPPED;
670 return source->state;
674 * Returns if the source should specify an update, given the context's
675 * deferring state and the source's last known state.
677 inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context)
679 return !context->DeferUpdates.load(std::memory_order_acquire) &&
680 IsPlayingOrPaused(source);
684 /** Can only be called while the mixer is locked! */
685 void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state)
687 AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
688 ALbitfieldSOFT enabledevt;
690 enabledevt = context->EnabledEvts.load(std::memory_order_acquire);
691 if(!(enabledevt&EventType_SourceStateChange)) return;
693 evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
694 evt.u.user.id = id;
695 evt.u.user.param = state;
696 snprintf(evt.u.user.msg, sizeof(evt.u.user.msg), "Source ID %u state changed to %s", id,
697 (state==AL_INITIAL) ? "AL_INITIAL" :
698 (state==AL_PLAYING) ? "AL_PLAYING" :
699 (state==AL_PAUSED) ? "AL_PAUSED" :
700 (state==AL_STOPPED) ? "AL_STOPPED" : "<unknown>"
702 /* The mixer may have queued a state change that's not yet been processed,
703 * and we don't want state change messages to occur out of order, so send
704 * it through the async queue to ensure proper ordering.
706 if(ll_ringbuffer_write(context->AsyncEvents, &evt, 1) == 1)
707 alsem_post(&context->EventSem);
711 ALint FloatValsByProp(ALenum prop)
713 if(prop != (ALenum)((SourceProp)prop))
714 return 0;
715 switch((SourceProp)prop)
717 case AL_PITCH:
718 case AL_GAIN:
719 case AL_MIN_GAIN:
720 case AL_MAX_GAIN:
721 case AL_MAX_DISTANCE:
722 case AL_ROLLOFF_FACTOR:
723 case AL_DOPPLER_FACTOR:
724 case AL_CONE_OUTER_GAIN:
725 case AL_SEC_OFFSET:
726 case AL_SAMPLE_OFFSET:
727 case AL_BYTE_OFFSET:
728 case AL_CONE_INNER_ANGLE:
729 case AL_CONE_OUTER_ANGLE:
730 case AL_REFERENCE_DISTANCE:
731 case AL_CONE_OUTER_GAINHF:
732 case AL_AIR_ABSORPTION_FACTOR:
733 case AL_ROOM_ROLLOFF_FACTOR:
734 case AL_DIRECT_FILTER_GAINHF_AUTO:
735 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
736 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
737 case AL_DIRECT_CHANNELS_SOFT:
738 case AL_DISTANCE_MODEL:
739 case AL_SOURCE_RELATIVE:
740 case AL_LOOPING:
741 case AL_SOURCE_STATE:
742 case AL_BUFFERS_QUEUED:
743 case AL_BUFFERS_PROCESSED:
744 case AL_SOURCE_TYPE:
745 case AL_SOURCE_RADIUS:
746 case AL_SOURCE_RESAMPLER_SOFT:
747 case AL_SOURCE_SPATIALIZE_SOFT:
748 return 1;
750 case AL_STEREO_ANGLES:
751 return 2;
753 case AL_POSITION:
754 case AL_VELOCITY:
755 case AL_DIRECTION:
756 return 3;
758 case AL_ORIENTATION:
759 return 6;
761 case AL_SEC_OFFSET_LATENCY_SOFT:
762 case AL_SEC_OFFSET_CLOCK_SOFT:
763 break; /* Double only */
765 case AL_BUFFER:
766 case AL_DIRECT_FILTER:
767 case AL_AUXILIARY_SEND_FILTER:
768 break; /* i/i64 only */
769 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
770 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
771 break; /* i64 only */
773 return 0;
775 ALint DoubleValsByProp(ALenum prop)
777 if(prop != (ALenum)((SourceProp)prop))
778 return 0;
779 switch((SourceProp)prop)
781 case AL_PITCH:
782 case AL_GAIN:
783 case AL_MIN_GAIN:
784 case AL_MAX_GAIN:
785 case AL_MAX_DISTANCE:
786 case AL_ROLLOFF_FACTOR:
787 case AL_DOPPLER_FACTOR:
788 case AL_CONE_OUTER_GAIN:
789 case AL_SEC_OFFSET:
790 case AL_SAMPLE_OFFSET:
791 case AL_BYTE_OFFSET:
792 case AL_CONE_INNER_ANGLE:
793 case AL_CONE_OUTER_ANGLE:
794 case AL_REFERENCE_DISTANCE:
795 case AL_CONE_OUTER_GAINHF:
796 case AL_AIR_ABSORPTION_FACTOR:
797 case AL_ROOM_ROLLOFF_FACTOR:
798 case AL_DIRECT_FILTER_GAINHF_AUTO:
799 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
800 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
801 case AL_DIRECT_CHANNELS_SOFT:
802 case AL_DISTANCE_MODEL:
803 case AL_SOURCE_RELATIVE:
804 case AL_LOOPING:
805 case AL_SOURCE_STATE:
806 case AL_BUFFERS_QUEUED:
807 case AL_BUFFERS_PROCESSED:
808 case AL_SOURCE_TYPE:
809 case AL_SOURCE_RADIUS:
810 case AL_SOURCE_RESAMPLER_SOFT:
811 case AL_SOURCE_SPATIALIZE_SOFT:
812 return 1;
814 case AL_SEC_OFFSET_LATENCY_SOFT:
815 case AL_SEC_OFFSET_CLOCK_SOFT:
816 case AL_STEREO_ANGLES:
817 return 2;
819 case AL_POSITION:
820 case AL_VELOCITY:
821 case AL_DIRECTION:
822 return 3;
824 case AL_ORIENTATION:
825 return 6;
827 case AL_BUFFER:
828 case AL_DIRECT_FILTER:
829 case AL_AUXILIARY_SEND_FILTER:
830 break; /* i/i64 only */
831 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
832 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
833 break; /* i64 only */
835 return 0;
838 ALint IntValsByProp(ALenum prop)
840 if(prop != (ALenum)((SourceProp)prop))
841 return 0;
842 switch((SourceProp)prop)
844 case AL_PITCH:
845 case AL_GAIN:
846 case AL_MIN_GAIN:
847 case AL_MAX_GAIN:
848 case AL_MAX_DISTANCE:
849 case AL_ROLLOFF_FACTOR:
850 case AL_DOPPLER_FACTOR:
851 case AL_CONE_OUTER_GAIN:
852 case AL_SEC_OFFSET:
853 case AL_SAMPLE_OFFSET:
854 case AL_BYTE_OFFSET:
855 case AL_CONE_INNER_ANGLE:
856 case AL_CONE_OUTER_ANGLE:
857 case AL_REFERENCE_DISTANCE:
858 case AL_CONE_OUTER_GAINHF:
859 case AL_AIR_ABSORPTION_FACTOR:
860 case AL_ROOM_ROLLOFF_FACTOR:
861 case AL_DIRECT_FILTER_GAINHF_AUTO:
862 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
863 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
864 case AL_DIRECT_CHANNELS_SOFT:
865 case AL_DISTANCE_MODEL:
866 case AL_SOURCE_RELATIVE:
867 case AL_LOOPING:
868 case AL_BUFFER:
869 case AL_SOURCE_STATE:
870 case AL_BUFFERS_QUEUED:
871 case AL_BUFFERS_PROCESSED:
872 case AL_SOURCE_TYPE:
873 case AL_DIRECT_FILTER:
874 case AL_SOURCE_RADIUS:
875 case AL_SOURCE_RESAMPLER_SOFT:
876 case AL_SOURCE_SPATIALIZE_SOFT:
877 return 1;
879 case AL_POSITION:
880 case AL_VELOCITY:
881 case AL_DIRECTION:
882 case AL_AUXILIARY_SEND_FILTER:
883 return 3;
885 case AL_ORIENTATION:
886 return 6;
888 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
889 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
890 break; /* i64 only */
891 case AL_SEC_OFFSET_LATENCY_SOFT:
892 case AL_SEC_OFFSET_CLOCK_SOFT:
893 break; /* Double only */
894 case AL_STEREO_ANGLES:
895 break; /* Float/double only */
897 return 0;
899 ALint Int64ValsByProp(ALenum prop)
901 if(prop != (ALenum)((SourceProp)prop))
902 return 0;
903 switch((SourceProp)prop)
905 case AL_PITCH:
906 case AL_GAIN:
907 case AL_MIN_GAIN:
908 case AL_MAX_GAIN:
909 case AL_MAX_DISTANCE:
910 case AL_ROLLOFF_FACTOR:
911 case AL_DOPPLER_FACTOR:
912 case AL_CONE_OUTER_GAIN:
913 case AL_SEC_OFFSET:
914 case AL_SAMPLE_OFFSET:
915 case AL_BYTE_OFFSET:
916 case AL_CONE_INNER_ANGLE:
917 case AL_CONE_OUTER_ANGLE:
918 case AL_REFERENCE_DISTANCE:
919 case AL_CONE_OUTER_GAINHF:
920 case AL_AIR_ABSORPTION_FACTOR:
921 case AL_ROOM_ROLLOFF_FACTOR:
922 case AL_DIRECT_FILTER_GAINHF_AUTO:
923 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
924 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
925 case AL_DIRECT_CHANNELS_SOFT:
926 case AL_DISTANCE_MODEL:
927 case AL_SOURCE_RELATIVE:
928 case AL_LOOPING:
929 case AL_BUFFER:
930 case AL_SOURCE_STATE:
931 case AL_BUFFERS_QUEUED:
932 case AL_BUFFERS_PROCESSED:
933 case AL_SOURCE_TYPE:
934 case AL_DIRECT_FILTER:
935 case AL_SOURCE_RADIUS:
936 case AL_SOURCE_RESAMPLER_SOFT:
937 case AL_SOURCE_SPATIALIZE_SOFT:
938 return 1;
940 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
941 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
942 return 2;
944 case AL_POSITION:
945 case AL_VELOCITY:
946 case AL_DIRECTION:
947 case AL_AUXILIARY_SEND_FILTER:
948 return 3;
950 case AL_ORIENTATION:
951 return 6;
953 case AL_SEC_OFFSET_LATENCY_SOFT:
954 case AL_SEC_OFFSET_CLOCK_SOFT:
955 break; /* Double only */
956 case AL_STEREO_ANGLES:
957 break; /* Float/double only */
959 return 0;
963 ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values);
964 ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values);
965 ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values);
967 #define CHECKVAL(x) do { \
968 if(!(x)) \
970 alSetError(Context, AL_INVALID_VALUE, "Value out of range"); \
971 return AL_FALSE; \
973 } while(0)
975 #define DO_UPDATEPROPS() do { \
976 ALvoice *voice; \
977 if(SourceShouldUpdate(Source, Context) && \
978 (voice=GetSourceVoice(Source, Context)) != nullptr) \
979 UpdateSourceProps(Source, voice, Context); \
980 else \
981 Source->PropsClean.clear(std::memory_order_release); \
982 } while(0)
984 ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values)
986 ALint ival;
988 switch(prop)
990 case AL_SEC_OFFSET_LATENCY_SOFT:
991 case AL_SEC_OFFSET_CLOCK_SOFT:
992 /* Query only */
993 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
994 "Setting read-only source property 0x%04x", prop);
996 case AL_PITCH:
997 CHECKVAL(*values >= 0.0f);
999 Source->Pitch = *values;
1000 DO_UPDATEPROPS();
1001 return AL_TRUE;
1003 case AL_CONE_INNER_ANGLE:
1004 CHECKVAL(*values >= 0.0f && *values <= 360.0f);
1006 Source->InnerAngle = *values;
1007 DO_UPDATEPROPS();
1008 return AL_TRUE;
1010 case AL_CONE_OUTER_ANGLE:
1011 CHECKVAL(*values >= 0.0f && *values <= 360.0f);
1013 Source->OuterAngle = *values;
1014 DO_UPDATEPROPS();
1015 return AL_TRUE;
1017 case AL_GAIN:
1018 CHECKVAL(*values >= 0.0f);
1020 Source->Gain = *values;
1021 DO_UPDATEPROPS();
1022 return AL_TRUE;
1024 case AL_MAX_DISTANCE:
1025 CHECKVAL(*values >= 0.0f);
1027 Source->MaxDistance = *values;
1028 DO_UPDATEPROPS();
1029 return AL_TRUE;
1031 case AL_ROLLOFF_FACTOR:
1032 CHECKVAL(*values >= 0.0f);
1034 Source->RolloffFactor = *values;
1035 DO_UPDATEPROPS();
1036 return AL_TRUE;
1038 case AL_REFERENCE_DISTANCE:
1039 CHECKVAL(*values >= 0.0f);
1041 Source->RefDistance = *values;
1042 DO_UPDATEPROPS();
1043 return AL_TRUE;
1045 case AL_MIN_GAIN:
1046 CHECKVAL(*values >= 0.0f);
1048 Source->MinGain = *values;
1049 DO_UPDATEPROPS();
1050 return AL_TRUE;
1052 case AL_MAX_GAIN:
1053 CHECKVAL(*values >= 0.0f);
1055 Source->MaxGain = *values;
1056 DO_UPDATEPROPS();
1057 return AL_TRUE;
1059 case AL_CONE_OUTER_GAIN:
1060 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
1062 Source->OuterGain = *values;
1063 DO_UPDATEPROPS();
1064 return AL_TRUE;
1066 case AL_CONE_OUTER_GAINHF:
1067 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
1069 Source->OuterGainHF = *values;
1070 DO_UPDATEPROPS();
1071 return AL_TRUE;
1073 case AL_AIR_ABSORPTION_FACTOR:
1074 CHECKVAL(*values >= 0.0f && *values <= 10.0f);
1076 Source->AirAbsorptionFactor = *values;
1077 DO_UPDATEPROPS();
1078 return AL_TRUE;
1080 case AL_ROOM_ROLLOFF_FACTOR:
1081 CHECKVAL(*values >= 0.0f && *values <= 10.0f);
1083 Source->RoomRolloffFactor = *values;
1084 DO_UPDATEPROPS();
1085 return AL_TRUE;
1087 case AL_DOPPLER_FACTOR:
1088 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
1090 Source->DopplerFactor = *values;
1091 DO_UPDATEPROPS();
1092 return AL_TRUE;
1094 case AL_SEC_OFFSET:
1095 case AL_SAMPLE_OFFSET:
1096 case AL_BYTE_OFFSET:
1097 CHECKVAL(*values >= 0.0f);
1099 Source->OffsetType = prop;
1100 Source->Offset = *values;
1102 if(IsPlayingOrPaused(Source))
1104 ALvoice *voice;
1106 ALCdevice_Lock(Context->Device);
1107 /* Double-check that the source is still playing while we have
1108 * the lock.
1110 voice = GetSourceVoice(Source, Context);
1111 if(voice)
1113 if(ApplyOffset(Source, voice) == AL_FALSE)
1115 ALCdevice_Unlock(Context->Device);
1116 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid offset");
1119 ALCdevice_Unlock(Context->Device);
1121 return AL_TRUE;
1123 case AL_SOURCE_RADIUS:
1124 CHECKVAL(*values >= 0.0f && std::isfinite(*values));
1126 Source->Radius = *values;
1127 DO_UPDATEPROPS();
1128 return AL_TRUE;
1130 case AL_STEREO_ANGLES:
1131 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]));
1133 Source->StereoPan[0] = values[0];
1134 Source->StereoPan[1] = values[1];
1135 DO_UPDATEPROPS();
1136 return AL_TRUE;
1139 case AL_POSITION:
1140 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1142 Source->Position[0] = values[0];
1143 Source->Position[1] = values[1];
1144 Source->Position[2] = values[2];
1145 DO_UPDATEPROPS();
1146 return AL_TRUE;
1148 case AL_VELOCITY:
1149 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1151 Source->Velocity[0] = values[0];
1152 Source->Velocity[1] = values[1];
1153 Source->Velocity[2] = values[2];
1154 DO_UPDATEPROPS();
1155 return AL_TRUE;
1157 case AL_DIRECTION:
1158 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
1160 Source->Direction[0] = values[0];
1161 Source->Direction[1] = values[1];
1162 Source->Direction[2] = values[2];
1163 DO_UPDATEPROPS();
1164 return AL_TRUE;
1166 case AL_ORIENTATION:
1167 CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) &&
1168 std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]));
1170 Source->Orientation[0][0] = values[0];
1171 Source->Orientation[0][1] = values[1];
1172 Source->Orientation[0][2] = values[2];
1173 Source->Orientation[1][0] = values[3];
1174 Source->Orientation[1][1] = values[4];
1175 Source->Orientation[1][2] = values[5];
1176 DO_UPDATEPROPS();
1177 return AL_TRUE;
1180 case AL_SOURCE_RELATIVE:
1181 case AL_LOOPING:
1182 case AL_SOURCE_STATE:
1183 case AL_SOURCE_TYPE:
1184 case AL_DISTANCE_MODEL:
1185 case AL_DIRECT_FILTER_GAINHF_AUTO:
1186 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1187 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1188 case AL_DIRECT_CHANNELS_SOFT:
1189 case AL_SOURCE_RESAMPLER_SOFT:
1190 case AL_SOURCE_SPATIALIZE_SOFT:
1191 ival = (ALint)values[0];
1192 return SetSourceiv(Source, Context, prop, &ival);
1194 case AL_BUFFERS_QUEUED:
1195 case AL_BUFFERS_PROCESSED:
1196 ival = (ALint)((ALuint)values[0]);
1197 return SetSourceiv(Source, Context, prop, &ival);
1199 case AL_BUFFER:
1200 case AL_DIRECT_FILTER:
1201 case AL_AUXILIARY_SEND_FILTER:
1202 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1203 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1204 break;
1207 ERR("Unexpected property: 0x%04x\n", prop);
1208 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source float property 0x%04x", prop);
1211 ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values)
1213 ALCdevice *device{Context->Device};
1214 ALbuffer *buffer{nullptr};
1215 ALfilter *filter{nullptr};
1216 ALeffectslot *slot{nullptr};
1217 ALbufferlistitem *oldlist{nullptr};
1218 std::unique_lock<almtx_t> slotlock;
1219 std::unique_lock<almtx_t> buflock;
1220 ALfloat fvals[6];
1222 switch(prop)
1224 case AL_SOURCE_STATE:
1225 case AL_SOURCE_TYPE:
1226 case AL_BUFFERS_QUEUED:
1227 case AL_BUFFERS_PROCESSED:
1228 /* Query only */
1229 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1230 "Setting read-only source property 0x%04x", prop);
1232 case AL_SOURCE_RELATIVE:
1233 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1235 Source->HeadRelative = (ALboolean)*values;
1236 DO_UPDATEPROPS();
1237 return AL_TRUE;
1239 case AL_LOOPING:
1240 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1242 Source->Looping = (ALboolean)*values;
1243 if(IsPlayingOrPaused(Source))
1245 ALvoice *voice{GetSourceVoice(Source, Context)};
1246 if(voice)
1248 if(Source->Looping)
1249 voice->loop_buffer.store(Source->queue, std::memory_order_release);
1250 else
1251 voice->loop_buffer.store(nullptr, std::memory_order_release);
1253 /* If the source is playing, wait for the current mix to finish
1254 * to ensure it isn't currently looping back or reaching the
1255 * end.
1257 while((device->MixCount.load(std::memory_order_acquire)&1))
1258 althrd_yield();
1261 return AL_TRUE;
1263 case AL_BUFFER:
1264 buflock = std::unique_lock<almtx_t>{device->BufferLock};
1265 if(!(*values == 0 || (buffer=LookupBuffer(device, *values)) != nullptr))
1266 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid buffer ID %u",
1267 *values);
1269 if(buffer && buffer->MappedAccess != 0 &&
1270 !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
1271 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1272 "Setting non-persistently mapped buffer %u", buffer->id);
1273 else
1275 ALenum state = GetSourceState(Source, GetSourceVoice(Source, Context));
1276 if(state == AL_PLAYING || state == AL_PAUSED)
1277 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1278 "Setting buffer on playing or paused source %u", Source->id);
1281 oldlist = Source->queue;
1282 if(buffer != nullptr)
1284 /* Add the selected buffer to a one-item queue */
1285 auto newlist = static_cast<ALbufferlistitem*>(al_calloc(DEF_ALIGN,
1286 FAM_SIZE(ALbufferlistitem, buffers, 1)));
1287 ATOMIC_INIT(&newlist->next, static_cast<ALbufferlistitem*>(nullptr));
1288 newlist->max_samples = buffer->SampleLen;
1289 newlist->num_buffers = 1;
1290 newlist->buffers[0] = buffer;
1291 IncrementRef(&buffer->ref);
1293 /* Source is now Static */
1294 Source->SourceType = AL_STATIC;
1295 Source->queue = newlist;
1297 else
1299 /* Source is now Undetermined */
1300 Source->SourceType = AL_UNDETERMINED;
1301 Source->queue = nullptr;
1303 buflock.unlock();
1305 /* Delete all elements in the previous queue */
1306 while(oldlist != nullptr)
1308 ALbufferlistitem *temp{oldlist};
1309 oldlist = temp->next.load(std::memory_order_relaxed);
1311 for(ALsizei i{0};i < temp->num_buffers;i++)
1313 if(temp->buffers[i])
1314 DecrementRef(&temp->buffers[i]->ref);
1316 al_free(temp);
1318 return AL_TRUE;
1320 case AL_SEC_OFFSET:
1321 case AL_SAMPLE_OFFSET:
1322 case AL_BYTE_OFFSET:
1323 CHECKVAL(*values >= 0);
1325 Source->OffsetType = prop;
1326 Source->Offset = *values;
1328 if(IsPlayingOrPaused(Source))
1330 ALCdevice_Lock(Context->Device);
1331 ALvoice *voice{GetSourceVoice(Source, Context)};
1332 if(voice)
1334 if(ApplyOffset(Source, voice) == AL_FALSE)
1336 ALCdevice_Unlock(Context->Device);
1337 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE,
1338 "Invalid source offset");
1341 ALCdevice_Unlock(Context->Device);
1343 return AL_TRUE;
1345 case AL_DIRECT_FILTER:
1346 LockFilterList(device);
1347 if(!(*values == 0 || (filter=LookupFilter(device, *values)) != nullptr))
1349 UnlockFilterList(device);
1350 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u",
1351 *values);
1354 if(!filter)
1356 Source->Direct.Gain = 1.0f;
1357 Source->Direct.GainHF = 1.0f;
1358 Source->Direct.HFReference = LOWPASSFREQREF;
1359 Source->Direct.GainLF = 1.0f;
1360 Source->Direct.LFReference = HIGHPASSFREQREF;
1362 else
1364 Source->Direct.Gain = filter->Gain;
1365 Source->Direct.GainHF = filter->GainHF;
1366 Source->Direct.HFReference = filter->HFReference;
1367 Source->Direct.GainLF = filter->GainLF;
1368 Source->Direct.LFReference = filter->LFReference;
1370 UnlockFilterList(device);
1371 DO_UPDATEPROPS();
1372 return AL_TRUE;
1374 case AL_DIRECT_FILTER_GAINHF_AUTO:
1375 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1377 Source->DryGainHFAuto = *values;
1378 DO_UPDATEPROPS();
1379 return AL_TRUE;
1381 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1382 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1384 Source->WetGainAuto = *values;
1385 DO_UPDATEPROPS();
1386 return AL_TRUE;
1388 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1389 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1391 Source->WetGainHFAuto = *values;
1392 DO_UPDATEPROPS();
1393 return AL_TRUE;
1395 case AL_DIRECT_CHANNELS_SOFT:
1396 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
1398 Source->DirectChannels = *values;
1399 DO_UPDATEPROPS();
1400 return AL_TRUE;
1402 case AL_DISTANCE_MODEL:
1403 CHECKVAL(*values == AL_NONE ||
1404 *values == AL_INVERSE_DISTANCE ||
1405 *values == AL_INVERSE_DISTANCE_CLAMPED ||
1406 *values == AL_LINEAR_DISTANCE ||
1407 *values == AL_LINEAR_DISTANCE_CLAMPED ||
1408 *values == AL_EXPONENT_DISTANCE ||
1409 *values == AL_EXPONENT_DISTANCE_CLAMPED);
1411 Source->mDistanceModel = static_cast<DistanceModel>(*values);
1412 if(Context->SourceDistanceModel)
1413 DO_UPDATEPROPS();
1414 return AL_TRUE;
1416 case AL_SOURCE_RESAMPLER_SOFT:
1417 CHECKVAL(*values >= 0 && *values <= ResamplerMax);
1419 Source->Resampler = static_cast<enum Resampler>(*values);
1420 DO_UPDATEPROPS();
1421 return AL_TRUE;
1423 case AL_SOURCE_SPATIALIZE_SOFT:
1424 CHECKVAL(*values >= AL_FALSE && *values <= AL_AUTO_SOFT);
1426 Source->Spatialize = static_cast<enum SpatializeMode>(*values);
1427 DO_UPDATEPROPS();
1428 return AL_TRUE;
1431 case AL_AUXILIARY_SEND_FILTER:
1432 slotlock = std::unique_lock<almtx_t>{Context->EffectSlotLock};
1433 if(!(values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != nullptr))
1434 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid effect ID %u",
1435 values[0]);
1436 if((ALuint)values[1] >= (ALuint)device->NumAuxSends)
1437 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid send %u", values[1]);
1438 LockFilterList(device);
1439 if(!(values[2] == 0 || (filter=LookupFilter(device, values[2])) != nullptr))
1441 UnlockFilterList(device);
1442 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u",
1443 values[2]);
1446 if(!filter)
1448 /* Disable filter */
1449 Source->Send[values[1]].Gain = 1.0f;
1450 Source->Send[values[1]].GainHF = 1.0f;
1451 Source->Send[values[1]].HFReference = LOWPASSFREQREF;
1452 Source->Send[values[1]].GainLF = 1.0f;
1453 Source->Send[values[1]].LFReference = HIGHPASSFREQREF;
1455 else
1457 Source->Send[values[1]].Gain = filter->Gain;
1458 Source->Send[values[1]].GainHF = filter->GainHF;
1459 Source->Send[values[1]].HFReference = filter->HFReference;
1460 Source->Send[values[1]].GainLF = filter->GainLF;
1461 Source->Send[values[1]].LFReference = filter->LFReference;
1463 UnlockFilterList(device);
1465 if(slot != Source->Send[values[1]].Slot && IsPlayingOrPaused(Source))
1467 /* Add refcount on the new slot, and release the previous slot */
1468 if(slot) IncrementRef(&slot->ref);
1469 if(Source->Send[values[1]].Slot)
1470 DecrementRef(&Source->Send[values[1]].Slot->ref);
1471 Source->Send[values[1]].Slot = slot;
1473 /* We must force an update if the auxiliary slot changed on an
1474 * active source, in case the slot is about to be deleted.
1476 ALvoice *voice{GetSourceVoice(Source, Context)};
1477 if(voice) UpdateSourceProps(Source, voice, Context);
1478 else Source->PropsClean.clear(std::memory_order_release);
1480 else
1482 if(slot) IncrementRef(&slot->ref);
1483 if(Source->Send[values[1]].Slot)
1484 DecrementRef(&Source->Send[values[1]].Slot->ref);
1485 Source->Send[values[1]].Slot = slot;
1486 DO_UPDATEPROPS();
1489 return AL_TRUE;
1492 /* 1x float */
1493 case AL_CONE_INNER_ANGLE:
1494 case AL_CONE_OUTER_ANGLE:
1495 case AL_PITCH:
1496 case AL_GAIN:
1497 case AL_MIN_GAIN:
1498 case AL_MAX_GAIN:
1499 case AL_REFERENCE_DISTANCE:
1500 case AL_ROLLOFF_FACTOR:
1501 case AL_CONE_OUTER_GAIN:
1502 case AL_MAX_DISTANCE:
1503 case AL_DOPPLER_FACTOR:
1504 case AL_CONE_OUTER_GAINHF:
1505 case AL_AIR_ABSORPTION_FACTOR:
1506 case AL_ROOM_ROLLOFF_FACTOR:
1507 case AL_SOURCE_RADIUS:
1508 fvals[0] = (ALfloat)*values;
1509 return SetSourcefv(Source, Context, prop, fvals);
1511 /* 3x float */
1512 case AL_POSITION:
1513 case AL_VELOCITY:
1514 case AL_DIRECTION:
1515 fvals[0] = (ALfloat)values[0];
1516 fvals[1] = (ALfloat)values[1];
1517 fvals[2] = (ALfloat)values[2];
1518 return SetSourcefv(Source, Context, prop, fvals);
1520 /* 6x float */
1521 case AL_ORIENTATION:
1522 fvals[0] = (ALfloat)values[0];
1523 fvals[1] = (ALfloat)values[1];
1524 fvals[2] = (ALfloat)values[2];
1525 fvals[3] = (ALfloat)values[3];
1526 fvals[4] = (ALfloat)values[4];
1527 fvals[5] = (ALfloat)values[5];
1528 return SetSourcefv(Source, Context, prop, fvals);
1530 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1531 case AL_SEC_OFFSET_LATENCY_SOFT:
1532 case AL_SEC_OFFSET_CLOCK_SOFT:
1533 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1534 case AL_STEREO_ANGLES:
1535 break;
1538 ERR("Unexpected property: 0x%04x\n", prop);
1539 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x",
1540 prop);
1543 ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values)
1545 ALfloat fvals[6];
1546 ALint ivals[3];
1548 switch(prop)
1550 case AL_SOURCE_TYPE:
1551 case AL_BUFFERS_QUEUED:
1552 case AL_BUFFERS_PROCESSED:
1553 case AL_SOURCE_STATE:
1554 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1555 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1556 /* Query only */
1557 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1558 "Setting read-only source property 0x%04x", prop);
1560 /* 1x int */
1561 case AL_SOURCE_RELATIVE:
1562 case AL_LOOPING:
1563 case AL_SEC_OFFSET:
1564 case AL_SAMPLE_OFFSET:
1565 case AL_BYTE_OFFSET:
1566 case AL_DIRECT_FILTER_GAINHF_AUTO:
1567 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1568 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1569 case AL_DIRECT_CHANNELS_SOFT:
1570 case AL_DISTANCE_MODEL:
1571 case AL_SOURCE_RESAMPLER_SOFT:
1572 case AL_SOURCE_SPATIALIZE_SOFT:
1573 CHECKVAL(*values <= INT_MAX && *values >= INT_MIN);
1575 ivals[0] = (ALint)*values;
1576 return SetSourceiv(Source, Context, prop, ivals);
1578 /* 1x uint */
1579 case AL_BUFFER:
1580 case AL_DIRECT_FILTER:
1581 CHECKVAL(*values <= UINT_MAX && *values >= 0);
1583 ivals[0] = (ALuint)*values;
1584 return SetSourceiv(Source, Context, prop, ivals);
1586 /* 3x uint */
1587 case AL_AUXILIARY_SEND_FILTER:
1588 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 &&
1589 values[1] <= UINT_MAX && values[1] >= 0 &&
1590 values[2] <= UINT_MAX && values[2] >= 0);
1592 ivals[0] = (ALuint)values[0];
1593 ivals[1] = (ALuint)values[1];
1594 ivals[2] = (ALuint)values[2];
1595 return SetSourceiv(Source, Context, prop, ivals);
1597 /* 1x float */
1598 case AL_CONE_INNER_ANGLE:
1599 case AL_CONE_OUTER_ANGLE:
1600 case AL_PITCH:
1601 case AL_GAIN:
1602 case AL_MIN_GAIN:
1603 case AL_MAX_GAIN:
1604 case AL_REFERENCE_DISTANCE:
1605 case AL_ROLLOFF_FACTOR:
1606 case AL_CONE_OUTER_GAIN:
1607 case AL_MAX_DISTANCE:
1608 case AL_DOPPLER_FACTOR:
1609 case AL_CONE_OUTER_GAINHF:
1610 case AL_AIR_ABSORPTION_FACTOR:
1611 case AL_ROOM_ROLLOFF_FACTOR:
1612 case AL_SOURCE_RADIUS:
1613 fvals[0] = (ALfloat)*values;
1614 return SetSourcefv(Source, Context, prop, fvals);
1616 /* 3x float */
1617 case AL_POSITION:
1618 case AL_VELOCITY:
1619 case AL_DIRECTION:
1620 fvals[0] = (ALfloat)values[0];
1621 fvals[1] = (ALfloat)values[1];
1622 fvals[2] = (ALfloat)values[2];
1623 return SetSourcefv(Source, Context, prop, fvals);
1625 /* 6x float */
1626 case AL_ORIENTATION:
1627 fvals[0] = (ALfloat)values[0];
1628 fvals[1] = (ALfloat)values[1];
1629 fvals[2] = (ALfloat)values[2];
1630 fvals[3] = (ALfloat)values[3];
1631 fvals[4] = (ALfloat)values[4];
1632 fvals[5] = (ALfloat)values[5];
1633 return SetSourcefv(Source, Context, prop, fvals);
1635 case AL_SEC_OFFSET_LATENCY_SOFT:
1636 case AL_SEC_OFFSET_CLOCK_SOFT:
1637 case AL_STEREO_ANGLES:
1638 break;
1641 ERR("Unexpected property: 0x%04x\n", prop);
1642 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x",
1643 prop);
1646 #undef CHECKVAL
1649 ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values);
1650 ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values);
1651 ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values);
1653 ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values)
1655 ALCdevice *device{Context->Device};
1656 ClockLatency clocktime;
1657 std::chrono::nanoseconds srcclock;
1658 ALint ivals[3];
1659 ALboolean err;
1661 switch(prop)
1663 case AL_GAIN:
1664 *values = Source->Gain;
1665 return AL_TRUE;
1667 case AL_PITCH:
1668 *values = Source->Pitch;
1669 return AL_TRUE;
1671 case AL_MAX_DISTANCE:
1672 *values = Source->MaxDistance;
1673 return AL_TRUE;
1675 case AL_ROLLOFF_FACTOR:
1676 *values = Source->RolloffFactor;
1677 return AL_TRUE;
1679 case AL_REFERENCE_DISTANCE:
1680 *values = Source->RefDistance;
1681 return AL_TRUE;
1683 case AL_CONE_INNER_ANGLE:
1684 *values = Source->InnerAngle;
1685 return AL_TRUE;
1687 case AL_CONE_OUTER_ANGLE:
1688 *values = Source->OuterAngle;
1689 return AL_TRUE;
1691 case AL_MIN_GAIN:
1692 *values = Source->MinGain;
1693 return AL_TRUE;
1695 case AL_MAX_GAIN:
1696 *values = Source->MaxGain;
1697 return AL_TRUE;
1699 case AL_CONE_OUTER_GAIN:
1700 *values = Source->OuterGain;
1701 return AL_TRUE;
1703 case AL_SEC_OFFSET:
1704 case AL_SAMPLE_OFFSET:
1705 case AL_BYTE_OFFSET:
1706 *values = GetSourceOffset(Source, prop, Context);
1707 return AL_TRUE;
1709 case AL_CONE_OUTER_GAINHF:
1710 *values = Source->OuterGainHF;
1711 return AL_TRUE;
1713 case AL_AIR_ABSORPTION_FACTOR:
1714 *values = Source->AirAbsorptionFactor;
1715 return AL_TRUE;
1717 case AL_ROOM_ROLLOFF_FACTOR:
1718 *values = Source->RoomRolloffFactor;
1719 return AL_TRUE;
1721 case AL_DOPPLER_FACTOR:
1722 *values = Source->DopplerFactor;
1723 return AL_TRUE;
1725 case AL_SOURCE_RADIUS:
1726 *values = Source->Radius;
1727 return AL_TRUE;
1729 case AL_STEREO_ANGLES:
1730 values[0] = Source->StereoPan[0];
1731 values[1] = Source->StereoPan[1];
1732 return AL_TRUE;
1734 case AL_SEC_OFFSET_LATENCY_SOFT:
1735 /* Get the source offset with the clock time first. Then get the
1736 * clock time with the device latency. Order is important.
1738 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1739 { std::lock_guard<almtx_t> _{device->BackendLock};
1740 clocktime = GetClockLatency(device);
1742 if(srcclock == clocktime.ClockTime)
1743 values[1] = (ALdouble)clocktime.Latency.count() / 1000000000.0;
1744 else
1746 /* If the clock time incremented, reduce the latency by that
1747 * much since it's that much closer to the source offset it got
1748 * earlier.
1750 std::chrono::nanoseconds diff = clocktime.ClockTime - srcclock;
1751 values[1] = (ALdouble)(clocktime.Latency - std::min(clocktime.Latency, diff)).count() /
1752 1000000000.0;
1754 return AL_TRUE;
1756 case AL_SEC_OFFSET_CLOCK_SOFT:
1757 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1758 values[1] = srcclock.count() / 1000000000.0;
1759 return AL_TRUE;
1761 case AL_POSITION:
1762 values[0] = Source->Position[0];
1763 values[1] = Source->Position[1];
1764 values[2] = Source->Position[2];
1765 return AL_TRUE;
1767 case AL_VELOCITY:
1768 values[0] = Source->Velocity[0];
1769 values[1] = Source->Velocity[1];
1770 values[2] = Source->Velocity[2];
1771 return AL_TRUE;
1773 case AL_DIRECTION:
1774 values[0] = Source->Direction[0];
1775 values[1] = Source->Direction[1];
1776 values[2] = Source->Direction[2];
1777 return AL_TRUE;
1779 case AL_ORIENTATION:
1780 values[0] = Source->Orientation[0][0];
1781 values[1] = Source->Orientation[0][1];
1782 values[2] = Source->Orientation[0][2];
1783 values[3] = Source->Orientation[1][0];
1784 values[4] = Source->Orientation[1][1];
1785 values[5] = Source->Orientation[1][2];
1786 return AL_TRUE;
1788 /* 1x int */
1789 case AL_SOURCE_RELATIVE:
1790 case AL_LOOPING:
1791 case AL_SOURCE_STATE:
1792 case AL_BUFFERS_QUEUED:
1793 case AL_BUFFERS_PROCESSED:
1794 case AL_SOURCE_TYPE:
1795 case AL_DIRECT_FILTER_GAINHF_AUTO:
1796 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1797 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1798 case AL_DIRECT_CHANNELS_SOFT:
1799 case AL_DISTANCE_MODEL:
1800 case AL_SOURCE_RESAMPLER_SOFT:
1801 case AL_SOURCE_SPATIALIZE_SOFT:
1802 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
1803 *values = (ALdouble)ivals[0];
1804 return err;
1806 case AL_BUFFER:
1807 case AL_DIRECT_FILTER:
1808 case AL_AUXILIARY_SEND_FILTER:
1809 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1810 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1811 break;
1814 ERR("Unexpected property: 0x%04x\n", prop);
1815 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source double property 0x%04x",
1816 prop);
1819 ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values)
1821 ALbufferlistitem *BufferList;
1822 ALdouble dvals[6];
1823 ALboolean err;
1825 switch(prop)
1827 case AL_SOURCE_RELATIVE:
1828 *values = Source->HeadRelative;
1829 return AL_TRUE;
1831 case AL_LOOPING:
1832 *values = Source->Looping;
1833 return AL_TRUE;
1835 case AL_BUFFER:
1836 BufferList = (Source->SourceType == AL_STATIC) ? Source->queue : nullptr;
1837 *values = (BufferList && BufferList->num_buffers >= 1 && BufferList->buffers[0]) ?
1838 BufferList->buffers[0]->id : 0;
1839 return AL_TRUE;
1841 case AL_SOURCE_STATE:
1842 *values = GetSourceState(Source, GetSourceVoice(Source, Context));
1843 return AL_TRUE;
1845 case AL_BUFFERS_QUEUED:
1846 if(!(BufferList=Source->queue))
1847 *values = 0;
1848 else
1850 ALsizei count = 0;
1851 do {
1852 count += BufferList->num_buffers;
1853 BufferList = BufferList->next.load(std::memory_order_relaxed);
1854 } while(BufferList != nullptr);
1855 *values = count;
1857 return AL_TRUE;
1859 case AL_BUFFERS_PROCESSED:
1860 if(Source->Looping || Source->SourceType != AL_STREAMING)
1862 /* Buffers on a looping source are in a perpetual state of
1863 * PENDING, so don't report any as PROCESSED */
1864 *values = 0;
1866 else
1868 const ALbufferlistitem *BufferList{Source->queue};
1869 const ALbufferlistitem *Current{nullptr};
1870 ALsizei played{0};
1872 ALvoice *voice{GetSourceVoice(Source, Context)};
1873 if(voice != nullptr)
1874 Current = voice->current_buffer.load(std::memory_order_relaxed);
1875 else if(Source->state == AL_INITIAL)
1876 Current = BufferList;
1878 while(BufferList && BufferList != Current)
1880 played += BufferList->num_buffers;
1881 BufferList = BufferList->next.load(std::memory_order_relaxed);
1883 *values = played;
1885 return AL_TRUE;
1887 case AL_SOURCE_TYPE:
1888 *values = Source->SourceType;
1889 return AL_TRUE;
1891 case AL_DIRECT_FILTER_GAINHF_AUTO:
1892 *values = Source->DryGainHFAuto;
1893 return AL_TRUE;
1895 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1896 *values = Source->WetGainAuto;
1897 return AL_TRUE;
1899 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1900 *values = Source->WetGainHFAuto;
1901 return AL_TRUE;
1903 case AL_DIRECT_CHANNELS_SOFT:
1904 *values = Source->DirectChannels;
1905 return AL_TRUE;
1907 case AL_DISTANCE_MODEL:
1908 *values = static_cast<int>(Source->mDistanceModel);
1909 return AL_TRUE;
1911 case AL_SOURCE_RESAMPLER_SOFT:
1912 *values = Source->Resampler;
1913 return AL_TRUE;
1915 case AL_SOURCE_SPATIALIZE_SOFT:
1916 *values = Source->Spatialize;
1917 return AL_TRUE;
1919 /* 1x float/double */
1920 case AL_CONE_INNER_ANGLE:
1921 case AL_CONE_OUTER_ANGLE:
1922 case AL_PITCH:
1923 case AL_GAIN:
1924 case AL_MIN_GAIN:
1925 case AL_MAX_GAIN:
1926 case AL_REFERENCE_DISTANCE:
1927 case AL_ROLLOFF_FACTOR:
1928 case AL_CONE_OUTER_GAIN:
1929 case AL_MAX_DISTANCE:
1930 case AL_SEC_OFFSET:
1931 case AL_SAMPLE_OFFSET:
1932 case AL_BYTE_OFFSET:
1933 case AL_DOPPLER_FACTOR:
1934 case AL_AIR_ABSORPTION_FACTOR:
1935 case AL_ROOM_ROLLOFF_FACTOR:
1936 case AL_CONE_OUTER_GAINHF:
1937 case AL_SOURCE_RADIUS:
1938 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1939 *values = (ALint)dvals[0];
1940 return err;
1942 /* 3x float/double */
1943 case AL_POSITION:
1944 case AL_VELOCITY:
1945 case AL_DIRECTION:
1946 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1948 values[0] = (ALint)dvals[0];
1949 values[1] = (ALint)dvals[1];
1950 values[2] = (ALint)dvals[2];
1952 return err;
1954 /* 6x float/double */
1955 case AL_ORIENTATION:
1956 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1958 values[0] = (ALint)dvals[0];
1959 values[1] = (ALint)dvals[1];
1960 values[2] = (ALint)dvals[2];
1961 values[3] = (ALint)dvals[3];
1962 values[4] = (ALint)dvals[4];
1963 values[5] = (ALint)dvals[5];
1965 return err;
1967 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1968 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1969 break; /* i64 only */
1970 case AL_SEC_OFFSET_LATENCY_SOFT:
1971 case AL_SEC_OFFSET_CLOCK_SOFT:
1972 break; /* Double only */
1973 case AL_STEREO_ANGLES:
1974 break; /* Float/double only */
1976 case AL_DIRECT_FILTER:
1977 case AL_AUXILIARY_SEND_FILTER:
1978 break; /* ??? */
1981 ERR("Unexpected property: 0x%04x\n", prop);
1982 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x",
1983 prop);
1986 ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values)
1988 ALCdevice *device = Context->Device;
1989 ClockLatency clocktime;
1990 std::chrono::nanoseconds srcclock;
1991 ALdouble dvals[6];
1992 ALint ivals[3];
1993 ALboolean err;
1995 switch(prop)
1997 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1998 /* Get the source offset with the clock time first. Then get the
1999 * clock time with the device latency. Order is important.
2001 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
2002 { std::lock_guard<almtx_t> _{device->BackendLock};
2003 clocktime = GetClockLatency(device);
2005 if(srcclock == clocktime.ClockTime)
2006 values[1] = clocktime.Latency.count();
2007 else
2009 /* If the clock time incremented, reduce the latency by that
2010 * much since it's that much closer to the source offset it got
2011 * earlier.
2013 auto diff = clocktime.ClockTime - srcclock;
2014 values[1] = (clocktime.Latency - std::min(clocktime.Latency, diff)).count();
2016 return AL_TRUE;
2018 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
2019 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
2020 values[1] = srcclock.count();
2021 return AL_TRUE;
2023 /* 1x float/double */
2024 case AL_CONE_INNER_ANGLE:
2025 case AL_CONE_OUTER_ANGLE:
2026 case AL_PITCH:
2027 case AL_GAIN:
2028 case AL_MIN_GAIN:
2029 case AL_MAX_GAIN:
2030 case AL_REFERENCE_DISTANCE:
2031 case AL_ROLLOFF_FACTOR:
2032 case AL_CONE_OUTER_GAIN:
2033 case AL_MAX_DISTANCE:
2034 case AL_SEC_OFFSET:
2035 case AL_SAMPLE_OFFSET:
2036 case AL_BYTE_OFFSET:
2037 case AL_DOPPLER_FACTOR:
2038 case AL_AIR_ABSORPTION_FACTOR:
2039 case AL_ROOM_ROLLOFF_FACTOR:
2040 case AL_CONE_OUTER_GAINHF:
2041 case AL_SOURCE_RADIUS:
2042 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
2043 *values = (ALint64)dvals[0];
2044 return err;
2046 /* 3x float/double */
2047 case AL_POSITION:
2048 case AL_VELOCITY:
2049 case AL_DIRECTION:
2050 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
2052 values[0] = (ALint64)dvals[0];
2053 values[1] = (ALint64)dvals[1];
2054 values[2] = (ALint64)dvals[2];
2056 return err;
2058 /* 6x float/double */
2059 case AL_ORIENTATION:
2060 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
2062 values[0] = (ALint64)dvals[0];
2063 values[1] = (ALint64)dvals[1];
2064 values[2] = (ALint64)dvals[2];
2065 values[3] = (ALint64)dvals[3];
2066 values[4] = (ALint64)dvals[4];
2067 values[5] = (ALint64)dvals[5];
2069 return err;
2071 /* 1x int */
2072 case AL_SOURCE_RELATIVE:
2073 case AL_LOOPING:
2074 case AL_SOURCE_STATE:
2075 case AL_BUFFERS_QUEUED:
2076 case AL_BUFFERS_PROCESSED:
2077 case AL_SOURCE_TYPE:
2078 case AL_DIRECT_FILTER_GAINHF_AUTO:
2079 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
2080 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
2081 case AL_DIRECT_CHANNELS_SOFT:
2082 case AL_DISTANCE_MODEL:
2083 case AL_SOURCE_RESAMPLER_SOFT:
2084 case AL_SOURCE_SPATIALIZE_SOFT:
2085 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
2086 *values = ivals[0];
2087 return err;
2089 /* 1x uint */
2090 case AL_BUFFER:
2091 case AL_DIRECT_FILTER:
2092 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
2093 *values = (ALuint)ivals[0];
2094 return err;
2096 /* 3x uint */
2097 case AL_AUXILIARY_SEND_FILTER:
2098 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
2100 values[0] = (ALuint)ivals[0];
2101 values[1] = (ALuint)ivals[1];
2102 values[2] = (ALuint)ivals[2];
2104 return err;
2106 case AL_SEC_OFFSET_LATENCY_SOFT:
2107 case AL_SEC_OFFSET_CLOCK_SOFT:
2108 break; /* Double only */
2109 case AL_STEREO_ANGLES:
2110 break; /* Float/double only */
2113 ERR("Unexpected property: 0x%04x\n", prop);
2114 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x",
2115 prop);
2118 } // namespace
2120 AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
2122 ContextRef context{GetContextRef()};
2123 if(UNLIKELY(!context)) return;
2125 if(n < 0)
2126 alSetError(context.get(), AL_INVALID_VALUE, "Generating %d sources", n);
2127 else if(n == 1)
2129 ALsource *source = AllocSource(context.get());
2130 if(source) sources[0] = source->id;
2132 else
2134 al::vector<ALuint> tempids(n);
2135 auto alloc_end = std::find_if_not(tempids.begin(), tempids.end(),
2136 [&context](ALuint &id) -> bool
2138 ALsource *source{AllocSource(context.get())};
2139 if(!source) return false;
2140 id = source->id;
2141 return true;
2144 if(alloc_end != tempids.end())
2145 alDeleteSources(std::distance(tempids.begin(), alloc_end), tempids.data());
2146 else
2147 std::copy(tempids.cbegin(), tempids.cend(), sources);
2152 AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
2154 ContextRef context{GetContextRef()};
2155 if(UNLIKELY(!context)) return;
2157 if(n < 0)
2158 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Deleting %d sources", n);
2160 std::lock_guard<almtx_t> _{context->SourceLock};
2162 /* Check that all Sources are valid */
2163 const ALuint *sources_end = sources + n;
2164 auto invsrc = std::find_if_not(sources, sources_end,
2165 [&context](ALuint sid) -> bool
2167 if(!LookupSource(context.get(), sid))
2169 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", sid);
2170 return false;
2172 return true;
2175 if(LIKELY(invsrc == sources_end))
2177 /* All good. Delete source IDs. */
2178 std::for_each(sources, sources_end,
2179 [&context](ALuint sid) -> void
2181 ALsource *src{LookupSource(context.get(), sid)};
2182 if(src) FreeSource(context.get(), src);
2189 AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
2191 ContextRef context{GetContextRef()};
2192 if(LIKELY(context))
2194 std::lock_guard<almtx_t> _{context->SourceLock};
2195 if(LookupSource(context.get(), source) != nullptr)
2196 return AL_TRUE;
2198 return AL_FALSE;
2202 AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
2204 ContextRef context{GetContextRef()};
2205 if(UNLIKELY(!context)) return;
2207 std::lock_guard<almtx_t> _{context->PropLock};
2208 std::lock_guard<almtx_t> __{context->SourceLock};
2209 ALsource *Source = LookupSource(context.get(), source);
2210 if(UNLIKELY(!Source))
2211 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2212 else if(FloatValsByProp(param) != 1)
2213 alSetError(context.get(), AL_INVALID_ENUM, "Invalid float property 0x%04x", param);
2214 else
2215 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), &value);
2218 AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
2220 ContextRef context{GetContextRef()};
2221 if(UNLIKELY(!context)) return;
2223 std::lock_guard<almtx_t> _{context->PropLock};
2224 std::lock_guard<almtx_t> __{context->SourceLock};
2225 ALsource *Source = LookupSource(context.get(), source);
2226 if(UNLIKELY(!Source))
2227 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2228 else if(FloatValsByProp(param) != 3)
2229 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param);
2230 else
2232 ALfloat fvals[3] = { value1, value2, value3 };
2233 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2237 AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
2239 ContextRef context{GetContextRef()};
2240 if(UNLIKELY(!context)) return;
2242 std::lock_guard<almtx_t> _{context->PropLock};
2243 std::lock_guard<almtx_t> __{context->SourceLock};
2244 ALsource *Source = LookupSource(context.get(), source);
2245 if(UNLIKELY(!Source))
2246 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2247 else if(!values)
2248 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2249 else if(FloatValsByProp(param) < 1)
2250 alSetError(context.get(), AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param);
2251 else
2252 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), values);
2256 AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
2258 ContextRef context{GetContextRef()};
2259 if(UNLIKELY(!context)) return;
2261 std::lock_guard<almtx_t> _{context->PropLock};
2262 std::lock_guard<almtx_t> __{context->SourceLock};
2263 ALsource *Source = LookupSource(context.get(), source);
2264 if(UNLIKELY(!Source))
2265 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2266 else if(DoubleValsByProp(param) != 1)
2267 alSetError(context.get(), AL_INVALID_ENUM, "Invalid double property 0x%04x", param);
2268 else
2270 ALfloat fval = (ALfloat)value;
2271 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), &fval);
2275 AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
2277 ContextRef context{GetContextRef()};
2278 if(UNLIKELY(!context)) return;
2280 std::lock_guard<almtx_t> _{context->PropLock};
2281 std::lock_guard<almtx_t> __{context->SourceLock};
2282 ALsource *Source = LookupSource(context.get(), source);
2283 if(UNLIKELY(!Source))
2284 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2285 else if(DoubleValsByProp(param) != 3)
2286 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param);
2287 else
2289 ALfloat fvals[3] = { (ALfloat)value1, (ALfloat)value2, (ALfloat)value3 };
2290 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2294 AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
2296 ContextRef context{GetContextRef()};
2297 if(UNLIKELY(!context)) return;
2299 std::lock_guard<almtx_t> _{context->PropLock};
2300 std::lock_guard<almtx_t> __{context->SourceLock};
2301 ALsource *Source = LookupSource(context.get(), source);
2302 if(UNLIKELY(!Source))
2303 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2304 else if(!values)
2305 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2306 else
2308 ALint count{DoubleValsByProp(param)};
2309 if(count < 1 || count > 6)
2310 alSetError(context.get(), AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param);
2311 else
2313 ALfloat fvals[6];
2314 ALint i;
2316 for(i = 0;i < count;i++)
2317 fvals[i] = (ALfloat)values[i];
2318 SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
2324 AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
2326 ContextRef context{GetContextRef()};
2327 if(UNLIKELY(!context)) return;
2329 std::lock_guard<almtx_t> _{context->PropLock};
2330 std::lock_guard<almtx_t> __{context->SourceLock};
2331 ALsource *Source = LookupSource(context.get(), source);
2332 if(UNLIKELY(!Source))
2333 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2334 else if(IntValsByProp(param) != 1)
2335 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer property 0x%04x", param);
2336 else
2337 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), &value);
2340 AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
2342 ContextRef context{GetContextRef()};
2343 if(UNLIKELY(!context)) return;
2345 std::lock_guard<almtx_t> _{context->PropLock};
2346 std::lock_guard<almtx_t> __{context->SourceLock};
2347 ALsource *Source = LookupSource(context.get(), source);
2348 if(UNLIKELY(!Source))
2349 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2350 else if(IntValsByProp(param) != 3)
2351 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param);
2352 else
2354 ALint ivals[3] = { value1, value2, value3 };
2355 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals);
2359 AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
2361 ContextRef context{GetContextRef()};
2362 if(UNLIKELY(!context)) return;
2364 std::lock_guard<almtx_t> _{context->PropLock};
2365 std::lock_guard<almtx_t> __{context->SourceLock};
2366 ALsource *Source = LookupSource(context.get(), source);
2367 if(UNLIKELY(!Source))
2368 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2369 else if(!values)
2370 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2371 else if(IntValsByProp(param) < 1)
2372 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param);
2373 else
2374 SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), values);
2378 AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
2380 ContextRef context{GetContextRef()};
2381 if(UNLIKELY(!context)) return;
2383 std::lock_guard<almtx_t> _{context->PropLock};
2384 std::lock_guard<almtx_t> __{context->SourceLock};
2385 ALsource *Source{LookupSource(context.get(), source)};
2386 if(UNLIKELY(!Source))
2387 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2388 else if(Int64ValsByProp(param) != 1)
2389 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param);
2390 else
2391 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), &value);
2394 AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
2396 ContextRef context{GetContextRef()};
2397 if(UNLIKELY(!context)) return;
2399 std::lock_guard<almtx_t> _{context->PropLock};
2400 std::lock_guard<almtx_t> __{context->SourceLock};
2401 ALsource *Source{LookupSource(context.get(), source)};
2402 if(UNLIKELY(!Source))
2403 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2404 else if(Int64ValsByProp(param) != 3)
2405 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param);
2406 else
2408 ALint64SOFT i64vals[3] = { value1, value2, value3 };
2409 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals);
2413 AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
2415 ContextRef context{GetContextRef()};
2416 if(UNLIKELY(!context)) return;
2418 std::lock_guard<almtx_t> _{context->PropLock};
2419 std::lock_guard<almtx_t> __{context->SourceLock};
2420 ALsource *Source{LookupSource(context.get(), source)};
2421 if(UNLIKELY(!Source))
2422 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2423 else if(!values)
2424 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2425 else if(Int64ValsByProp(param) < 1)
2426 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param);
2427 else
2428 SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), values);
2432 AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
2434 ContextRef context{GetContextRef()};
2435 if(UNLIKELY(!context)) return;
2437 std::lock_guard<almtx_t> _{context->SourceLock};
2438 ALsource *Source{LookupSource(context.get(), source)};
2439 if(UNLIKELY(!Source))
2440 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2441 else if(!value)
2442 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2443 else if(FloatValsByProp(param) != 1)
2444 alSetError(context.get(), AL_INVALID_ENUM, "Invalid float property 0x%04x", param);
2445 else
2447 ALdouble dval;
2448 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), &dval))
2449 *value = (ALfloat)dval;
2454 AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
2456 ContextRef context{GetContextRef()};
2457 if(UNLIKELY(!context)) return;
2459 std::lock_guard<almtx_t> _{context->SourceLock};
2460 ALsource *Source{LookupSource(context.get(), source)};
2461 if(UNLIKELY(!Source))
2462 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2463 else if(!(value1 && value2 && value3))
2464 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2465 else if(FloatValsByProp(param) != 3)
2466 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param);
2467 else
2469 ALdouble dvals[3];
2470 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2472 *value1 = (ALfloat)dvals[0];
2473 *value2 = (ALfloat)dvals[1];
2474 *value3 = (ALfloat)dvals[2];
2480 AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
2482 ContextRef context{GetContextRef()};
2483 if(UNLIKELY(!context)) return;
2485 std::lock_guard<almtx_t> _{context->SourceLock};
2486 ALsource *Source{LookupSource(context.get(), source)};
2487 if(UNLIKELY(!Source))
2488 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2489 else if(!values)
2490 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2491 else
2493 ALint count{FloatValsByProp(param)};
2494 if(count < 1 && count > 6)
2495 alSetError(context.get(), AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param);
2496 else
2498 ALdouble dvals[6];
2499 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2501 for(ALint i{0};i < count;i++)
2502 values[i] = (ALfloat)dvals[i];
2509 AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
2511 ContextRef context{GetContextRef()};
2512 if(UNLIKELY(!context)) return;
2514 std::lock_guard<almtx_t> _{context->SourceLock};
2515 ALsource *Source{LookupSource(context.get(), source)};
2516 if(UNLIKELY(!Source))
2517 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2518 else if(!value)
2519 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2520 else if(DoubleValsByProp(param) != 1)
2521 alSetError(context.get(), AL_INVALID_ENUM, "Invalid double property 0x%04x", param);
2522 else
2523 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), value);
2526 AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
2528 ContextRef context{GetContextRef()};
2529 if(UNLIKELY(!context)) return;
2531 std::lock_guard<almtx_t> _{context->SourceLock};
2532 ALsource *Source{LookupSource(context.get(), source)};
2533 if(UNLIKELY(!Source))
2534 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2535 else if(!(value1 && value2 && value3))
2536 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2537 else if(DoubleValsByProp(param) != 3)
2538 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param);
2539 else
2541 ALdouble dvals[3];
2542 if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
2544 *value1 = dvals[0];
2545 *value2 = dvals[1];
2546 *value3 = dvals[2];
2551 AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
2553 ContextRef context{GetContextRef()};
2554 if(UNLIKELY(!context)) return;
2556 std::lock_guard<almtx_t> _{context->SourceLock};
2557 ALsource *Source{LookupSource(context.get(), source)};
2558 if(UNLIKELY(!Source))
2559 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2560 else if(!values)
2561 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2562 else if(DoubleValsByProp(param) < 1)
2563 alSetError(context.get(), AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param);
2564 else
2565 GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), values);
2569 AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
2571 ContextRef context{GetContextRef()};
2572 if(UNLIKELY(!context)) return;
2574 std::lock_guard<almtx_t> _{context->SourceLock};
2575 ALsource *Source{LookupSource(context.get(), source)};
2576 if(UNLIKELY(!Source))
2577 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2578 else if(!value)
2579 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2580 else if(IntValsByProp(param) != 1)
2581 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer property 0x%04x", param);
2582 else
2583 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), value);
2587 AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
2589 ContextRef context{GetContextRef()};
2590 if(UNLIKELY(!context)) return;
2592 std::lock_guard<almtx_t> _{context->SourceLock};
2593 ALsource *Source{LookupSource(context.get(), source)};
2594 if(UNLIKELY(!Source))
2595 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2596 else if(!(value1 && value2 && value3))
2597 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2598 else if(IntValsByProp(param) != 3)
2599 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param);
2600 else
2602 ALint ivals[3];
2603 if(GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals))
2605 *value1 = ivals[0];
2606 *value2 = ivals[1];
2607 *value3 = ivals[2];
2613 AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
2615 ContextRef context{GetContextRef()};
2616 if(UNLIKELY(!context)) return;
2618 std::lock_guard<almtx_t> _{context->SourceLock};
2619 ALsource *Source{LookupSource(context.get(), source)};
2620 if(UNLIKELY(!Source))
2621 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2622 else if(!values)
2623 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2624 else if(IntValsByProp(param) < 1)
2625 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param);
2626 else
2627 GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), values);
2631 AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
2633 ContextRef context{GetContextRef()};
2634 if(UNLIKELY(!context)) return;
2636 std::lock_guard<almtx_t> _{context->SourceLock};
2637 ALsource *Source{LookupSource(context.get(), source)};
2638 if(UNLIKELY(!Source))
2639 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2640 else if(!value)
2641 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2642 else if(Int64ValsByProp(param) != 1)
2643 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param);
2644 else
2645 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), value);
2648 AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
2650 ContextRef context{GetContextRef()};
2651 if(UNLIKELY(!context)) return;
2653 std::lock_guard<almtx_t> _{context->SourceLock};
2654 ALsource *Source{LookupSource(context.get(), source)};
2655 if(UNLIKELY(!Source))
2656 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2657 else if(!(value1 && value2 && value3))
2658 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2659 else if(Int64ValsByProp(param) != 3)
2660 alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param);
2661 else
2663 ALint64 i64vals[3];
2664 if(GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals))
2666 *value1 = i64vals[0];
2667 *value2 = i64vals[1];
2668 *value3 = i64vals[2];
2673 AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
2675 ContextRef context{GetContextRef()};
2676 if(UNLIKELY(!context)) return;
2678 std::lock_guard<almtx_t> _{context->SourceLock};
2679 ALsource *Source{LookupSource(context.get(), source)};
2680 if(UNLIKELY(!Source))
2681 alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source);
2682 else if(!values)
2683 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
2684 else if(Int64ValsByProp(param) < 1)
2685 alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param);
2686 else
2687 GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), values);
2691 AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source)
2693 alSourcePlayv(1, &source);
2695 AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
2697 ContextRef context{GetContextRef()};
2698 if(UNLIKELY(!context)) return;
2700 if(n < 0)
2701 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Playing %d sources", n);
2702 if(n == 0) return;
2704 std::lock_guard<almtx_t> _{context->SourceLock};
2705 for(ALsizei i{0};i < n;i++)
2707 if(!LookupSource(context.get(), sources[i]))
2708 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]);
2711 ALCdevice *device{context->Device};
2712 ALCdevice_Lock(device);
2713 /* If the device is disconnected, go right to stopped. */
2714 if(!device->Connected.load(std::memory_order_acquire))
2716 /* TODO: Send state change event? */
2717 for(ALsizei i{0};i < n;i++)
2719 ALsource *source{LookupSource(context.get(), sources[i])};
2720 source->OffsetType = AL_NONE;
2721 source->Offset = 0.0;
2722 source->state = AL_STOPPED;
2724 ALCdevice_Unlock(device);
2725 return;
2728 while(n > context->MaxVoices-context->VoiceCount.load(std::memory_order_relaxed))
2730 ALsizei newcount = context->MaxVoices << 1;
2731 if(context->MaxVoices >= newcount)
2733 ALCdevice_Unlock(device);
2734 SETERR_RETURN(context.get(), AL_OUT_OF_MEMORY,,
2735 "Overflow increasing voice count %d -> %d", context->MaxVoices, newcount);
2737 AllocateVoices(context.get(), newcount, device->NumAuxSends);
2740 for(ALsizei i{0};i < n;i++)
2742 ALsource *source{LookupSource(context.get(), sources[i])};
2743 /* Check that there is a queue containing at least one valid, non zero
2744 * length buffer.
2746 ALbufferlistitem *BufferList{source->queue};
2747 while(BufferList && BufferList->max_samples == 0)
2748 BufferList = BufferList->next.load(std::memory_order_relaxed);
2750 /* If there's nothing to play, go right to stopped. */
2751 if(UNLIKELY(!BufferList))
2753 /* NOTE: A source without any playable buffers should not have an
2754 * ALvoice since it shouldn't be in a playing or paused state. So
2755 * there's no need to look up its voice and clear the source.
2757 ALenum oldstate{GetSourceState(source, nullptr)};
2758 source->OffsetType = AL_NONE;
2759 source->Offset = 0.0;
2760 if(oldstate != AL_STOPPED)
2762 source->state = AL_STOPPED;
2763 SendStateChangeEvent(context.get(), source->id, AL_STOPPED);
2765 continue;
2768 ALvoice *voice{GetSourceVoice(source, context.get())};
2769 switch(GetSourceState(source, voice))
2771 case AL_PLAYING:
2772 assert(voice != nullptr);
2773 /* A source that's already playing is restarted from the beginning. */
2774 voice->current_buffer.store(BufferList, std::memory_order_relaxed);
2775 voice->position.store(0u, std::memory_order_relaxed);
2776 voice->position_fraction.store(0, std::memory_order_release);
2777 continue;
2779 case AL_PAUSED:
2780 assert(voice != nullptr);
2781 /* A source that's paused simply resumes. */
2782 voice->Playing.store(true, std::memory_order_release);
2783 source->state = AL_PLAYING;
2784 SendStateChangeEvent(context.get(), source->id, AL_PLAYING);
2785 continue;
2787 default:
2788 break;
2791 /* Look for an unused voice to play this source with. */
2792 assert(voice == nullptr);
2793 auto voices_end = context->Voices + context->VoiceCount.load(std::memory_order_relaxed);
2794 auto voice_iter = std::find_if(context->Voices, voices_end,
2795 [](const ALvoice *voice) noexcept -> bool
2796 { return voice->Source.load(std::memory_order_relaxed) == nullptr; }
2798 auto vidx = static_cast<ALint>(std::distance(context->Voices, voice_iter));
2799 voice = *voice_iter;
2800 voice->Playing.store(false, std::memory_order_release);
2801 if(voice_iter == voices_end) context->VoiceCount.fetch_add(1, std::memory_order_acq_rel);
2803 source->PropsClean.test_and_set(std::memory_order_acquire);
2804 UpdateSourceProps(source, voice, context.get());
2806 /* A source that's not playing or paused has any offset applied when it
2807 * starts playing.
2809 if(source->Looping)
2810 voice->loop_buffer.store(source->queue, std::memory_order_relaxed);
2811 else
2812 voice->loop_buffer.store(nullptr, std::memory_order_relaxed);
2813 voice->current_buffer.store(BufferList, std::memory_order_relaxed);
2814 voice->position.store(0u, std::memory_order_relaxed);
2815 voice->position_fraction.store(0, std::memory_order_relaxed);
2816 bool start_fading{false};
2817 if(ApplyOffset(source, voice) != AL_FALSE)
2818 start_fading = voice->position.load(std::memory_order_relaxed) != 0 ||
2819 voice->position_fraction.load(std::memory_order_relaxed) != 0 ||
2820 voice->current_buffer.load(std::memory_order_relaxed) != BufferList;
2822 auto buffers_end = BufferList->buffers + BufferList->num_buffers;
2823 auto buffer = std::find_if(BufferList->buffers, buffers_end,
2824 [](const ALbuffer *buffer) noexcept -> bool
2825 { return buffer != nullptr; }
2827 if(buffer != buffers_end)
2829 voice->NumChannels = ChannelsFromFmt((*buffer)->FmtChannels);
2830 voice->SampleSize = BytesFromFmt((*buffer)->FmtType);
2833 /* Clear previous samples. */
2834 for(auto &samples : voice->PrevSamples)
2835 std::fill(std::begin(samples), std::end(samples), 0.0f);
2837 /* Clear the stepping value so the mixer knows not to mix this until
2838 * the update gets applied.
2840 voice->Step = 0;
2842 voice->Flags = start_fading ? VOICE_IS_FADING : 0;
2843 if(source->SourceType == AL_STATIC) voice->Flags |= VOICE_IS_STATIC;
2844 memset(voice->Direct.Params, 0, sizeof(voice->Direct.Params[0])*voice->NumChannels);
2845 for(ALsizei j{0};j < device->NumAuxSends;j++)
2846 memset(voice->Send[j].Params, 0, sizeof(voice->Send[j].Params[0])*voice->NumChannels);
2847 if(device->AvgSpeakerDist > 0.0f)
2849 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
2850 (device->AvgSpeakerDist * device->Frequency);
2851 for(ALsizei j{0};j < voice->NumChannels;j++)
2852 NfcFilterCreate(&voice->Direct.Params[j].NFCtrlFilter, 0.0f, w1);
2855 voice->Source.store(source, std::memory_order_relaxed);
2856 voice->Playing.store(true, std::memory_order_release);
2857 source->state = AL_PLAYING;
2858 source->VoiceIdx = vidx;
2860 SendStateChangeEvent(context.get(), source->id, AL_PLAYING);
2862 ALCdevice_Unlock(device);
2865 AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source)
2867 alSourcePausev(1, &source);
2869 AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
2871 ContextRef context{GetContextRef()};
2872 if(UNLIKELY(!context)) return;
2874 if(n < 0)
2875 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Pausing %d sources", n);
2876 if(n == 0) return;
2878 for(ALsizei i{0};i < n;i++)
2880 if(!LookupSource(context.get(), sources[i]))
2881 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]);
2884 ALCdevice *device{context->Device};
2885 ALCdevice_Lock(device);
2886 for(ALsizei i{0};i < n;i++)
2888 ALsource *source{LookupSource(context.get(), sources[i])};
2889 ALvoice *voice{GetSourceVoice(source, context.get())};
2890 if(voice) voice->Playing.store(false, std::memory_order_release);
2891 if(GetSourceState(source, voice) == AL_PLAYING)
2893 source->state = AL_PAUSED;
2894 SendStateChangeEvent(context.get(), source->id, AL_PAUSED);
2897 ALCdevice_Unlock(device);
2900 AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source)
2902 alSourceStopv(1, &source);
2904 AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
2906 ContextRef context{GetContextRef()};
2907 if(UNLIKELY(!context)) return;
2909 if(n < 0)
2910 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Stopping %d sources", n);
2911 if(n == 0) return;
2913 for(ALsizei i{0};i < n;i++)
2915 if(!LookupSource(context.get(), sources[i]))
2916 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]);
2919 ALCdevice *device{context->Device};
2920 ALCdevice_Lock(device);
2921 for(ALsizei i{0};i < n;i++)
2923 ALsource *source{LookupSource(context.get(), sources[i])};
2924 ALvoice *voice{GetSourceVoice(source, context.get())};
2925 if(voice != nullptr)
2927 voice->Source.store(nullptr, std::memory_order_relaxed);
2928 voice->Playing.store(false, std::memory_order_release);
2929 voice = nullptr;
2931 ALenum oldstate{GetSourceState(source, voice)};
2932 if(oldstate != AL_INITIAL && oldstate != AL_STOPPED)
2934 source->state = AL_STOPPED;
2935 SendStateChangeEvent(context.get(), source->id, AL_STOPPED);
2937 source->OffsetType = AL_NONE;
2938 source->Offset = 0.0;
2940 ALCdevice_Unlock(device);
2943 AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source)
2945 alSourceRewindv(1, &source);
2947 AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
2949 ContextRef context{GetContextRef()};
2950 if(UNLIKELY(!context)) return;
2952 if(n < 0)
2953 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Rewinding %d sources", n);
2954 if(n == 0) return;
2956 for(ALsizei i{0};i < n;i++)
2958 if(!LookupSource(context.get(), sources[i]))
2959 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]);
2962 ALCdevice *device{context->Device};
2963 ALCdevice_Lock(device);
2964 for(ALsizei i{0};i < n;i++)
2966 ALsource *source{LookupSource(context.get(), sources[i])};
2967 ALvoice *voice{GetSourceVoice(source, context.get())};
2968 if(voice != nullptr)
2970 voice->Source.store(nullptr, std::memory_order_relaxed);
2971 voice->Playing.store(false, std::memory_order_release);
2972 voice = nullptr;
2974 if(GetSourceState(source, voice) != AL_INITIAL)
2976 source->state = AL_INITIAL;
2977 SendStateChangeEvent(context.get(), source->id, AL_INITIAL);
2979 source->OffsetType = AL_NONE;
2980 source->Offset = 0.0;
2982 ALCdevice_Unlock(device);
2986 AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
2988 ContextRef context{GetContextRef()};
2989 if(UNLIKELY(!context)) return;
2991 if(nb < 0)
2992 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Queueing %d buffers", nb);
2993 if(nb == 0) return;
2995 std::lock_guard<almtx_t> _{context->SourceLock};
2996 ALsource *source{LookupSource(context.get(),src)};
2997 if(!source)
2998 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src);
3000 /* Can't queue on a Static Source */
3001 if(source->SourceType == AL_STATIC)
3002 SETERR_RETURN(context.get(), AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
3004 /* Check for a valid Buffer, for its frequency and format */
3005 ALCdevice *device{context->Device};
3006 ALbuffer *BufferFmt{nullptr};
3007 ALbufferlistitem *BufferList{source->queue};
3008 while(BufferList)
3010 for(ALsizei i{0};i < BufferList->num_buffers;i++)
3012 if((BufferFmt=BufferList->buffers[i]) != nullptr)
3013 break;
3015 if(BufferFmt) break;
3016 BufferList = BufferList->next.load(std::memory_order_relaxed);
3019 std::unique_lock<almtx_t> buflock{device->BufferLock};
3020 ALbufferlistitem *BufferListStart{nullptr};
3021 BufferList = nullptr;
3022 for(ALsizei i{0};i < nb;i++)
3024 ALbuffer *buffer{nullptr};
3025 if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr)
3027 alSetError(context.get(), AL_INVALID_NAME, "Queueing invalid buffer ID %u",
3028 buffers[i]);
3029 goto buffer_error;
3032 if(!BufferListStart)
3034 BufferListStart = static_cast<ALbufferlistitem*>(al_calloc(DEF_ALIGN,
3035 FAM_SIZE(ALbufferlistitem, buffers, 1)));
3036 BufferList = BufferListStart;
3038 else
3040 auto item = static_cast<ALbufferlistitem*>(al_calloc(DEF_ALIGN,
3041 FAM_SIZE(ALbufferlistitem, buffers, 1)));
3042 BufferList->next.store(item, std::memory_order_relaxed);
3043 BufferList = item;
3045 ATOMIC_INIT(&BufferList->next, static_cast<ALbufferlistitem*>(nullptr));
3046 BufferList->max_samples = buffer ? buffer->SampleLen : 0;
3047 BufferList->num_buffers = 1;
3048 BufferList->buffers[0] = buffer;
3049 if(!buffer) continue;
3051 IncrementRef(&buffer->ref);
3053 if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
3055 alSetError(context.get(), AL_INVALID_OPERATION,
3056 "Queueing non-persistently mapped buffer %u", buffer->id);
3057 goto buffer_error;
3060 if(BufferFmt == nullptr)
3061 BufferFmt = buffer;
3062 else if(BufferFmt->Frequency != buffer->Frequency ||
3063 BufferFmt->FmtChannels != buffer->FmtChannels ||
3064 BufferFmt->OriginalType != buffer->OriginalType)
3066 alSetError(context.get(), AL_INVALID_OPERATION,
3067 "Queueing buffer with mismatched format");
3069 buffer_error:
3070 /* A buffer failed (invalid ID or format), so unlock and release
3071 * each buffer we had. */
3072 while(BufferListStart)
3074 ALbufferlistitem *next = BufferListStart->next.load(std::memory_order_relaxed);
3075 for(i = 0;i < BufferListStart->num_buffers;i++)
3077 if((buffer=BufferListStart->buffers[i]) != nullptr)
3078 DecrementRef(&buffer->ref);
3080 al_free(BufferListStart);
3081 BufferListStart = next;
3083 return;
3086 /* All buffers good. */
3087 buflock.unlock();
3089 /* Source is now streaming */
3090 source->SourceType = AL_STREAMING;
3092 if(!(BufferList=source->queue))
3093 source->queue = BufferListStart;
3094 else
3096 ALbufferlistitem *next;
3097 while((next=BufferList->next.load(std::memory_order_relaxed)) != nullptr)
3098 BufferList = next;
3099 BufferList->next.store(BufferListStart, std::memory_order_release);
3103 AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers)
3105 ContextRef context{GetContextRef()};
3106 if(UNLIKELY(!context)) return;
3108 if(nb < 0)
3109 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Queueing %d buffer layers", nb);
3110 if(nb == 0) return;
3112 std::lock_guard<almtx_t> _{context->SourceLock};
3113 ALsource *source{LookupSource(context.get(),src)};
3114 if(!source)
3115 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src);
3117 /* Can't queue on a Static Source */
3118 if(source->SourceType == AL_STATIC)
3119 SETERR_RETURN(context.get(), AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
3121 /* Check for a valid Buffer, for its frequency and format */
3122 ALCdevice *device{context->Device};
3123 ALbuffer *BufferFmt{nullptr};
3124 ALbufferlistitem *BufferList{source->queue};
3125 while(BufferList)
3127 for(ALsizei i{0};i < BufferList->num_buffers;i++)
3129 if((BufferFmt=BufferList->buffers[i]) != nullptr)
3130 break;
3132 if(BufferFmt) break;
3133 BufferList = BufferList->next.load(std::memory_order_relaxed);
3136 std::unique_lock<almtx_t> buflock{device->BufferLock};
3137 auto BufferListStart = static_cast<ALbufferlistitem*>(al_calloc(DEF_ALIGN,
3138 FAM_SIZE(ALbufferlistitem, buffers, nb)));
3139 BufferList = BufferListStart;
3140 ATOMIC_INIT(&BufferList->next, static_cast<ALbufferlistitem*>(nullptr));
3141 BufferList->max_samples = 0;
3142 BufferList->num_buffers = 0;
3144 for(ALsizei i{0};i < nb;i++)
3146 ALbuffer *buffer{nullptr};
3147 if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr)
3149 alSetError(context.get(), AL_INVALID_NAME, "Queueing invalid buffer ID %u",
3150 buffers[i]);
3151 goto buffer_error;
3154 BufferList->buffers[BufferList->num_buffers++] = buffer;
3155 if(!buffer) continue;
3157 IncrementRef(&buffer->ref);
3159 BufferList->max_samples = maxi(BufferList->max_samples, buffer->SampleLen);
3161 if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
3163 alSetError(context.get(), AL_INVALID_OPERATION,
3164 "Queueing non-persistently mapped buffer %u", buffer->id);
3165 goto buffer_error;
3168 if(BufferFmt == nullptr)
3169 BufferFmt = buffer;
3170 else if(BufferFmt->Frequency != buffer->Frequency ||
3171 BufferFmt->FmtChannels != buffer->FmtChannels ||
3172 BufferFmt->OriginalType != buffer->OriginalType)
3174 alSetError(context.get(), AL_INVALID_OPERATION,
3175 "Queueing buffer with mismatched format");
3177 buffer_error:
3178 /* A buffer failed (invalid ID or format), so unlock and release
3179 * each buffer we had. */
3180 while(BufferListStart)
3182 ALbufferlistitem *next{BufferListStart->next.load(std::memory_order_relaxed)};
3183 for(i = 0;i < BufferListStart->num_buffers;i++)
3185 if((buffer=BufferListStart->buffers[i]) != nullptr)
3186 DecrementRef(&buffer->ref);
3188 al_free(BufferListStart);
3189 BufferListStart = next;
3191 return;
3194 /* All buffers good. */
3195 buflock.unlock();
3197 /* Source is now streaming */
3198 source->SourceType = AL_STREAMING;
3200 if(!(BufferList=source->queue))
3201 source->queue = BufferListStart;
3202 else
3204 ALbufferlistitem *next;
3205 while((next=BufferList->next.load(std::memory_order_relaxed)) != nullptr)
3206 BufferList = next;
3207 BufferList->next.store(BufferListStart, std::memory_order_release);
3211 AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
3213 ContextRef context{GetContextRef()};
3214 if(UNLIKELY(!context)) return;
3216 if(nb < 0)
3217 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing %d buffers", nb);
3218 if(nb == 0) return;
3220 std::lock_guard<almtx_t> _{context->SourceLock};
3221 ALsource *source{LookupSource(context.get(),src)};
3222 if(!source)
3223 SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src);
3225 if(source->Looping)
3226 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing from looping source %u", src);
3227 if(source->SourceType != AL_STREAMING)
3228 SETERR_RETURN(context.get(), AL_INVALID_VALUE,,
3229 "Unqueueing from a non-streaming source %u", src);
3231 /* Make sure enough buffers have been processed to unqueue. */
3232 ALbufferlistitem *BufferList{source->queue};
3233 ALvoice *voice{GetSourceVoice(source, context.get())};
3234 ALbufferlistitem *Current{nullptr};
3235 if(voice)
3236 Current = voice->current_buffer.load(std::memory_order_relaxed);
3237 else if(source->state == AL_INITIAL)
3238 Current = BufferList;
3239 if(BufferList == Current)
3240 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing pending buffers");
3242 ALsizei i{BufferList->num_buffers};
3243 while(i < nb)
3245 /* If the next bufferlist to check is NULL or is the current one, it's
3246 * trying to unqueue pending buffers.
3248 ALbufferlistitem *next{BufferList->next.load(std::memory_order_relaxed)};
3249 if(!next || next == Current)
3250 SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing pending buffers");
3251 BufferList = next;
3253 i += BufferList->num_buffers;
3256 while(nb > 0)
3258 ALbufferlistitem *head{source->queue};
3259 ALbufferlistitem *next{head->next.load(std::memory_order_relaxed)};
3260 for(i = 0;i < head->num_buffers && nb > 0;i++,nb--)
3262 ALbuffer *buffer{head->buffers[i]};
3263 if(!buffer)
3264 *(buffers++) = 0;
3265 else
3267 *(buffers++) = buffer->id;
3268 DecrementRef(&buffer->ref);
3271 if(i < head->num_buffers)
3273 /* This head has some buffers left over, so move them to the front
3274 * and update the sample and buffer count.
3276 ALsizei max_length{0};
3277 ALsizei j{0};
3278 while(i < head->num_buffers)
3280 ALbuffer *buffer{head->buffers[i++]};
3281 if(buffer) max_length = maxi(max_length, buffer->SampleLen);
3282 head->buffers[j++] = buffer;
3284 head->max_samples = max_length;
3285 head->num_buffers = j;
3286 break;
3289 /* Otherwise, free this item and set the source queue head to the next
3290 * one.
3292 al_free(head);
3293 source->queue = next;
3298 ALsource::ALsource(ALsizei num_sends)
3300 InnerAngle = 360.0f;
3301 OuterAngle = 360.0f;
3302 Pitch = 1.0f;
3303 Position[0] = 0.0f;
3304 Position[1] = 0.0f;
3305 Position[2] = 0.0f;
3306 Velocity[0] = 0.0f;
3307 Velocity[1] = 0.0f;
3308 Velocity[2] = 0.0f;
3309 Direction[0] = 0.0f;
3310 Direction[1] = 0.0f;
3311 Direction[2] = 0.0f;
3312 Orientation[0][0] = 0.0f;
3313 Orientation[0][1] = 0.0f;
3314 Orientation[0][2] = -1.0f;
3315 Orientation[1][0] = 0.0f;
3316 Orientation[1][1] = 1.0f;
3317 Orientation[1][2] = 0.0f;
3318 RefDistance = 1.0f;
3319 MaxDistance = FLT_MAX;
3320 RolloffFactor = 1.0f;
3321 Gain = 1.0f;
3322 MinGain = 0.0f;
3323 MaxGain = 1.0f;
3324 OuterGain = 0.0f;
3325 OuterGainHF = 1.0f;
3327 DryGainHFAuto = AL_TRUE;
3328 WetGainAuto = AL_TRUE;
3329 WetGainHFAuto = AL_TRUE;
3330 AirAbsorptionFactor = 0.0f;
3331 RoomRolloffFactor = 0.0f;
3332 DopplerFactor = 1.0f;
3333 HeadRelative = AL_FALSE;
3334 Looping = AL_FALSE;
3335 mDistanceModel = DistanceModel::Default;
3336 Resampler = ResamplerDefault;
3337 DirectChannels = AL_FALSE;
3338 Spatialize = SpatializeAuto;
3340 StereoPan[0] = DEG2RAD( 30.0f);
3341 StereoPan[1] = DEG2RAD(-30.0f);
3343 Radius = 0.0f;
3345 Direct.Gain = 1.0f;
3346 Direct.GainHF = 1.0f;
3347 Direct.HFReference = LOWPASSFREQREF;
3348 Direct.GainLF = 1.0f;
3349 Direct.LFReference = HIGHPASSFREQREF;
3350 Send.resize(num_sends);
3351 for(auto &send : Send)
3353 send.Slot = nullptr;
3354 send.Gain = 1.0f;
3355 send.GainHF = 1.0f;
3356 send.HFReference = LOWPASSFREQREF;
3357 send.GainLF = 1.0f;
3358 send.LFReference = HIGHPASSFREQREF;
3361 Offset = 0.0;
3362 OffsetType = AL_NONE;
3363 SourceType = AL_UNDETERMINED;
3364 state = AL_INITIAL;
3366 queue = nullptr;
3368 VoiceIdx = -1;
3371 ALsource::~ALsource()
3373 ALbufferlistitem *BufferList{queue};
3374 while(BufferList != nullptr)
3376 ALbufferlistitem *next{BufferList->next.load(std::memory_order_relaxed)};
3377 for(ALsizei i{0};i < BufferList->num_buffers;i++)
3379 if(BufferList->buffers[i])
3380 DecrementRef(&BufferList->buffers[i]->ref);
3382 al_free(BufferList);
3383 BufferList = next;
3385 queue = nullptr;
3387 std::for_each(Send.begin(), Send.end(),
3388 [](ALsource::SendData &send) -> void
3390 if(send.Slot)
3391 DecrementRef(&send.Slot->ref);
3392 send.Slot = nullptr;
3397 void UpdateAllSourceProps(ALCcontext *context)
3399 auto voices_end = context->Voices + context->VoiceCount.load(std::memory_order_relaxed);
3400 std::for_each(context->Voices, voices_end,
3401 [context](ALvoice *voice) -> void
3403 ALsource *source{voice->Source.load(std::memory_order_acquire)};
3404 if(source && !source->PropsClean.test_and_set(std::memory_order_acq_rel))
3405 UpdateSourceProps(source, voice, context);
3410 /* ReleaseALSources
3412 * Destroys all sources in the source map.
3414 ALvoid ReleaseALSources(ALCcontext *context)
3416 size_t leftover = 0;
3417 for(auto &sublist : context->SourceList)
3419 ALuint64 usemask = ~sublist.FreeMask;
3420 while(usemask)
3422 ALsizei idx{CTZ64(usemask)};
3423 ALsource *source{sublist.Sources + idx};
3425 source->~ALsource();
3426 ++leftover;
3428 usemask &= ~(U64(1) << idx);
3430 sublist.FreeMask = ~usemask;
3432 if(leftover > 0)
3433 WARN("(%p) Deleted " SZFMT " Source%s\n", context, leftover, (leftover==1)?"":"s");