From bb9d8db73c3536ccea5fbb928ae27810f7f63e0e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 22 Nov 2018 12:02:02 -0800 Subject: Clean up alSource.cpp some --- OpenAL32/alSource.cpp | 3892 ++++++++++++++++++++++++------------------------- 1 file changed, 1926 insertions(+), 1966 deletions(-) diff --git a/OpenAL32/alSource.cpp b/OpenAL32/alSource.cpp index 0739f677..382875d5 100644 --- a/OpenAL32/alSource.cpp +++ b/OpenAL32/alSource.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include "AL/al.h" @@ -45,188 +46,648 @@ #include "almalloc.h" -static ALsource *AllocSource(ALCcontext *context); -static void FreeSource(ALCcontext *context, ALsource *source); -static void UpdateSourceProps(ALsource *source, ALvoice *voice, ALCcontext *context); -static ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime); -static ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime); -static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context); -static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac); -static ALboolean ApplyOffset(ALsource *Source, ALvoice *voice); +namespace { -static inline ALsource *LookupSource(ALCcontext *context, ALuint id) +inline ALvoice *GetSourceVoice(ALsource *source, ALCcontext *context) { - ALuint lidx = (id-1) >> 6; - ALsizei slidx = (id-1) & 0x3f; - - if(UNLIKELY(lidx >= context->SourceList.size())) - return nullptr; - SourceSubList &sublist{context->SourceList[lidx]}; - if(UNLIKELY(sublist.FreeMask & (U64(1)<VoiceIdx}; + if(idx >= 0 && idx < context->VoiceCount) + { + ALvoice *voice{context->Voices[idx]}; + if(voice->Source.load(std::memory_order_acquire) == source) + return voice; + } + source->VoiceIdx = -1; + return nullptr; } -static inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) +void UpdateSourceProps(ALsource *source, ALvoice *voice, ALCcontext *context) { - ALuint lidx = (id-1) >> 6; - ALsizei slidx = (id-1) & 0x3f; - - if(UNLIKELY(lidx >= device->BufferList.size())) - return nullptr; - BufferSubList &sublist = device->BufferList[lidx]; - if(UNLIKELY(sublist.FreeMask & (U64(1)<FreeVoiceProps.load(std::memory_order_acquire)}; + if(!props) + props = static_cast(al_calloc(16, + FAM_SIZE(ALvoiceProps, Send, source->Send.size())) + ); + else + { + ALvoiceProps *next; + do { + next = props->next.load(std::memory_order_relaxed); + } while(context->FreeVoiceProps.compare_exchange_weak(props, next, + std::memory_order_acq_rel, std::memory_order_acquire) == 0); + } -static inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) -{ - ALuint lidx = (id-1) >> 6; - ALsizei slidx = (id-1) & 0x3f; + /* Copy in current property values. */ + props->Pitch = source->Pitch; + props->Gain = source->Gain; + props->OuterGain = source->OuterGain; + props->MinGain = source->MinGain; + props->MaxGain = source->MaxGain; + props->InnerAngle = source->InnerAngle; + props->OuterAngle = source->OuterAngle; + props->RefDistance = source->RefDistance; + props->MaxDistance = source->MaxDistance; + props->RolloffFactor = source->RolloffFactor; + std::copy_n(source->Position, 3, props->Position); + std::copy_n(source->Velocity, 3, props->Velocity); + std::copy_n(source->Direction, 3, props->Direction); + for(ALsizei i{0};i < 2;i++) + std::copy_n(source->Orientation[i], 3, props->Orientation[i]); + props->HeadRelative = source->HeadRelative; + props->mDistanceModel = source->mDistanceModel; + props->Resampler = source->Resampler; + props->DirectChannels = source->DirectChannels; + props->SpatializeMode = source->Spatialize; - if(UNLIKELY(lidx >= device->FilterList.size())) - return nullptr; - FilterSubList &sublist = device->FilterList[lidx]; - if(UNLIKELY(sublist.FreeMask & (U64(1)<DryGainHFAuto = source->DryGainHFAuto; + props->WetGainAuto = source->WetGainAuto; + props->WetGainHFAuto = source->WetGainHFAuto; + props->OuterGainHF = source->OuterGainHF; -static inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) -{ - --id; - if(UNLIKELY(id >= context->EffectSlotList.size())) - return nullptr; - return context->EffectSlotList[id].get(); -} + props->AirAbsorptionFactor = source->AirAbsorptionFactor; + props->RoomRolloffFactor = source->RoomRolloffFactor; + props->DopplerFactor = source->DopplerFactor; + std::copy_n(source->StereoPan, 2, props->StereoPan); -typedef enum SourceProp { - srcPitch = AL_PITCH, - srcGain = AL_GAIN, - srcMinGain = AL_MIN_GAIN, - srcMaxGain = AL_MAX_GAIN, - srcMaxDistance = AL_MAX_DISTANCE, - srcRolloffFactor = AL_ROLLOFF_FACTOR, - srcDopplerFactor = AL_DOPPLER_FACTOR, - srcConeOuterGain = AL_CONE_OUTER_GAIN, - srcSecOffset = AL_SEC_OFFSET, - srcSampleOffset = AL_SAMPLE_OFFSET, - srcByteOffset = AL_BYTE_OFFSET, - srcConeInnerAngle = AL_CONE_INNER_ANGLE, - srcConeOuterAngle = AL_CONE_OUTER_ANGLE, - srcRefDistance = AL_REFERENCE_DISTANCE, + props->Radius = source->Radius; - srcPosition = AL_POSITION, - srcVelocity = AL_VELOCITY, - srcDirection = AL_DIRECTION, + props->Direct.Gain = source->Direct.Gain; + props->Direct.GainHF = source->Direct.GainHF; + props->Direct.HFReference = source->Direct.HFReference; + props->Direct.GainLF = source->Direct.GainLF; + props->Direct.LFReference = source->Direct.LFReference; - srcSourceRelative = AL_SOURCE_RELATIVE, - srcLooping = AL_LOOPING, - srcBuffer = AL_BUFFER, - srcSourceState = AL_SOURCE_STATE, - srcBuffersQueued = AL_BUFFERS_QUEUED, - srcBuffersProcessed = AL_BUFFERS_PROCESSED, - srcSourceType = AL_SOURCE_TYPE, + for(size_t i{0u};i < source->Send.size();i++) + { + props->Send[i].Slot = source->Send[i].Slot; + props->Send[i].Gain = source->Send[i].Gain; + props->Send[i].GainHF = source->Send[i].GainHF; + props->Send[i].HFReference = source->Send[i].HFReference; + props->Send[i].GainLF = source->Send[i].GainLF; + props->Send[i].LFReference = source->Send[i].LFReference; + } - /* ALC_EXT_EFX */ - srcConeOuterGainHF = AL_CONE_OUTER_GAINHF, - srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR, - srcRoomRolloffFactor = AL_ROOM_ROLLOFF_FACTOR, - srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO, - srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, - srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, - srcDirectFilter = AL_DIRECT_FILTER, - srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER, + /* Set the new container for updating internal parameters. */ + props = voice->Update.exchange(props, std::memory_order_acq_rel); + if(props) + { + /* If there was an unused update container, put it back in the + * freelist. + */ + AtomicReplaceHead(context->FreeVoiceProps, props); + } +} - /* AL_SOFT_direct_channels */ - srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT, - /* AL_EXT_source_distance_model */ - srcDistanceModel = AL_DISTANCE_MODEL, +/* GetSourceSampleOffset + * + * Gets the current read offset for the given Source, in 32.32 fixed-point + * samples. The offset is relative to the start of the queue (not the start of + * the current buffer). + */ +ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime) +{ + ALCdevice *device{context->Device}; + const ALbufferlistitem *Current; + ALuint64 readPos; + ALuint refcount; + ALvoice *voice; - /* AL_SOFT_source_latency */ - srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT, - srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT, + do { + Current = nullptr; + readPos = 0; + while(((refcount=device->MixCount.load(std::memory_order_acquire))&1)) + althrd_yield(); + *clocktime = GetDeviceClockTime(device); - /* AL_EXT_STEREO_ANGLES */ - srcAngles = AL_STEREO_ANGLES, + voice = GetSourceVoice(Source, context); + if(voice) + { + Current = voice->current_buffer.load(std::memory_order_relaxed); - /* AL_EXT_SOURCE_RADIUS */ - srcRadius = AL_SOURCE_RADIUS, + readPos = (ALuint64)voice->position.load(std::memory_order_relaxed) << 32; + readPos |= (ALuint64)voice->position_fraction.load(std::memory_order_relaxed) << + (32-FRACTIONBITS); + } + std::atomic_thread_fence(std::memory_order_acquire); + } while(refcount != device->MixCount.load(std::memory_order_relaxed)); - /* AL_EXT_BFORMAT */ - srcOrientation = AL_ORIENTATION, + if(voice) + { + const ALbufferlistitem *BufferList{Source->queue}; + while(BufferList && BufferList != Current) + { + readPos += (ALuint64)BufferList->max_samples << 32; + BufferList = BufferList->next.load(std::memory_order_relaxed); + } + readPos = minu64(readPos, U64(0x7fffffffffffffff)); + } - /* AL_SOFT_source_resampler */ - srcResampler = AL_SOURCE_RESAMPLER_SOFT, + return (ALint64)readPos; +} - /* AL_SOFT_source_spatialize */ - srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT, +/* GetSourceSecOffset + * + * Gets the current read offset for the given Source, in seconds. The offset is + * relative to the start of the queue (not the start of the current buffer). + */ +ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime) +{ + ALCdevice *device{context->Device}; + const ALbufferlistitem *Current; + ALuint64 readPos; + ALuint refcount; + ALvoice *voice; - /* ALC_SOFT_device_clock */ - srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT, - srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT, -} SourceProp; + do { + Current = nullptr; + readPos = 0; + while(((refcount=device->MixCount.load(std::memory_order_acquire))&1)) + althrd_yield(); + *clocktime = GetDeviceClockTime(device); -static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values); -static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values); -static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values); + voice = GetSourceVoice(Source, context); + if(voice) + { + Current = voice->current_buffer.load(std::memory_order_relaxed); -static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values); -static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values); -static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values); + readPos = (ALuint64)voice->position.load(std::memory_order_relaxed) << FRACTIONBITS; + readPos |= voice->position_fraction.load(std::memory_order_relaxed); + } + std::atomic_thread_fence(std::memory_order_acquire); + } while(refcount != device->MixCount.load(std::memory_order_relaxed)); -static inline ALvoice *GetSourceVoice(ALsource *source, ALCcontext *context) -{ - ALint idx = source->VoiceIdx; - if(idx >= 0 && idx < context->VoiceCount) + ALdouble offset{0.0}; + if(voice) { - ALvoice *voice = context->Voices[idx]; - if(ATOMIC_LOAD(&voice->Source, almemory_order_acquire) == source) - return voice; - } - source->VoiceIdx = -1; - return NULL; -} + const ALbufferlistitem *BufferList{Source->queue}; + const ALbuffer *BufferFmt{nullptr}; + while(BufferList && BufferList != Current) + { + for(ALsizei i{0};!BufferFmt && i < BufferList->num_buffers;++i) + BufferFmt = BufferList->buffers[i]; + readPos += (ALuint64)BufferList->max_samples << FRACTIONBITS; + BufferList = BufferList->next.load(std::memory_order_relaxed); + } -/** - * Returns if the last known state for the source was playing or paused. Does - * not sync with the mixer voice. - */ -static inline bool IsPlayingOrPaused(ALsource *source) -{ return source->state == AL_PLAYING || source->state == AL_PAUSED; } + while(BufferList && !BufferFmt) + { + for(ALsizei i{0};!BufferFmt && i < BufferList->num_buffers;++i) + BufferFmt = BufferList->buffers[i]; + BufferList = BufferList->next.load(std::memory_order_relaxed); + } + assert(BufferFmt != nullptr); -/** - * Returns an updated source state using the matching voice's status (or lack - * thereof). - */ -static inline ALenum GetSourceState(ALsource *source, ALvoice *voice) -{ - if(!voice && source->state == AL_PLAYING) - source->state = AL_STOPPED; - return source->state; -} + offset = (ALdouble)readPos / (ALdouble)FRACTIONONE / + (ALdouble)BufferFmt->Frequency; + } -/** - * Returns if the source should specify an update, given the context's - * deferring state and the source's last known state. - */ -static inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context) -{ - return !context->DeferUpdates.load(std::memory_order_acquire) && - IsPlayingOrPaused(source); + return offset; } - -/** Can only be called while the mixer is locked! */ -static void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state) +/* GetSourceOffset + * + * Gets the current read offset for the given Source, in the appropriate format + * (Bytes, Samples or Seconds). The offset is relative to the start of the + * queue (not the start of the current buffer). + */ +ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) +{ + ALCdevice *device{context->Device}; + const ALbufferlistitem *Current; + ALuint readPos; + ALsizei readPosFrac; + ALuint refcount; + ALvoice *voice; + + do { + Current = nullptr; + readPos = readPosFrac = 0; + while(((refcount=device->MixCount.load(std::memory_order_acquire))&1)) + althrd_yield(); + voice = GetSourceVoice(Source, context); + if(voice) + { + Current = voice->current_buffer.load(std::memory_order_relaxed); + + readPos = voice->position.load(std::memory_order_relaxed); + readPosFrac = voice->position_fraction.load(std::memory_order_relaxed); + } + std::atomic_thread_fence(std::memory_order_acquire); + } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + + ALdouble offset{0.0}; + if(voice) + { + const ALbufferlistitem *BufferList{Source->queue}; + const ALbuffer *BufferFmt{nullptr}; + ALboolean readFin{AL_FALSE}; + ALuint totalBufferLen{0u}; + + while(BufferList) + { + for(ALsizei i{0};!BufferFmt && i < BufferList->num_buffers;++i) + BufferFmt = BufferList->buffers[i]; + + readFin |= (BufferList == Current); + totalBufferLen += BufferList->max_samples; + if(!readFin) readPos += BufferList->max_samples; + + BufferList = BufferList->next.load(std::memory_order_relaxed); + } + assert(BufferFmt != nullptr); + + if(Source->Looping) + readPos %= totalBufferLen; + else + { + /* Wrap back to 0 */ + if(readPos >= totalBufferLen) + readPos = readPosFrac = 0; + } + + offset = 0.0; + switch(name) + { + case AL_SEC_OFFSET: + offset = (readPos + (ALdouble)readPosFrac/FRACTIONONE) / BufferFmt->Frequency; + break; + + case AL_SAMPLE_OFFSET: + offset = readPos + (ALdouble)readPosFrac/FRACTIONONE; + break; + + case AL_BYTE_OFFSET: + if(BufferFmt->OriginalType == UserFmtIMA4) + { + ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4; + ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels); + ALuint FrameBlockSize = BufferFmt->OriginalAlign; + + /* Round down to nearest ADPCM block */ + offset = (ALdouble)(readPos / FrameBlockSize * BlockSize); + } + else if(BufferFmt->OriginalType == UserFmtMSADPCM) + { + ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7; + ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels); + ALuint FrameBlockSize = BufferFmt->OriginalAlign; + + /* Round down to nearest ADPCM block */ + offset = (ALdouble)(readPos / FrameBlockSize * BlockSize); + } + else + { + ALuint FrameSize = FrameSizeFromFmt(BufferFmt->FmtChannels, + BufferFmt->FmtType); + offset = (ALdouble)(readPos * FrameSize); + } + break; + } + } + + return offset; +} + + +/* GetSampleOffset + * + * Retrieves the sample offset into the Source's queue (from the Sample, Byte + * or Second offset supplied by the application). This takes into account the + * fact that the buffer format may have been modifed since. + */ +ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac) +{ + const ALbuffer *BufferFmt{nullptr}; + const ALbufferlistitem *BufferList; + + /* Find the first valid Buffer in the Queue */ + BufferList = Source->queue; + while(BufferList) + { + for(ALsizei i{0};i < BufferList->num_buffers && !BufferFmt;i++) + BufferFmt = BufferList->buffers[i]; + if(BufferFmt) break; + BufferList = BufferList->next.load(std::memory_order_relaxed); + } + if(!BufferFmt) + { + Source->OffsetType = AL_NONE; + Source->Offset = 0.0; + return AL_FALSE; + } + + ALdouble dbloff, dblfrac; + switch(Source->OffsetType) + { + case AL_BYTE_OFFSET: + /* Determine the ByteOffset (and ensure it is block aligned) */ + *offset = (ALuint)Source->Offset; + if(BufferFmt->OriginalType == UserFmtIMA4) + { + ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4; + *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels); + *offset *= BufferFmt->OriginalAlign; + } + else if(BufferFmt->OriginalType == UserFmtMSADPCM) + { + ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7; + *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels); + *offset *= BufferFmt->OriginalAlign; + } + else + *offset /= FrameSizeFromFmt(BufferFmt->FmtChannels, BufferFmt->FmtType); + *frac = 0; + break; + + case AL_SAMPLE_OFFSET: + dblfrac = modf(Source->Offset, &dbloff); + *offset = (ALuint)mind(dbloff, std::numeric_limits::max()); + *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0); + break; + + case AL_SEC_OFFSET: + dblfrac = modf(Source->Offset*BufferFmt->Frequency, &dbloff); + *offset = (ALuint)mind(dbloff, std::numeric_limits::max()); + *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0); + break; + } + Source->OffsetType = AL_NONE; + Source->Offset = 0.0; + + return AL_TRUE; +} + +/* ApplyOffset + * + * Apply the stored playback offset to the Source. This function will update + * the number of buffers "played" given the stored offset. + */ +ALboolean ApplyOffset(ALsource *Source, ALvoice *voice) +{ + /* Get sample frame offset */ + ALuint offset{0u}; + ALsizei frac{0}; + if(!GetSampleOffset(Source, &offset, &frac)) + return AL_FALSE; + + ALuint totalBufferLen{0u}; + ALbufferlistitem *BufferList{Source->queue}; + while(BufferList && totalBufferLen <= offset) + { + if((ALuint)BufferList->max_samples > offset-totalBufferLen) + { + /* Offset is in this buffer */ + voice->position.store(offset - totalBufferLen, std::memory_order_relaxed); + voice->position_fraction.store(frac, std::memory_order_relaxed); + voice->current_buffer.store(BufferList, std::memory_order_release); + return AL_TRUE; + } + totalBufferLen += BufferList->max_samples; + + BufferList = BufferList->next.load(std::memory_order_relaxed); + } + + /* Offset is out of range of the queue */ + return AL_FALSE; +} + + +ALsource *AllocSource(ALCcontext *context) +{ + ALCdevice *device{context->Device}; + std::lock_guard _{context->SourceLock}; + if(context->NumSources >= device->SourcesMax) + { + alSetError(context, AL_OUT_OF_MEMORY, "Exceeding %u source limit", device->SourcesMax); + return nullptr; + } + auto sublist = std::find_if(context->SourceList.begin(), context->SourceList.end(), + [](const SourceSubList &entry) -> bool + { return entry.FreeMask != 0; } + ); + ALsizei lidx = std::distance(context->SourceList.begin(), sublist); + ALsource *source; + ALsizei slidx; + if(LIKELY(sublist != context->SourceList.end())) + { + slidx = CTZ64(sublist->FreeMask); + source = sublist->Sources + slidx; + } + else + { + /* Don't allocate so many list entries that the 32-bit ID could + * overflow... + */ + if(UNLIKELY(context->SourceList.size() >= 1<<25)) + { + alSetError(context, AL_OUT_OF_MEMORY, "Too many sources allocated"); + return nullptr; + } + context->SourceList.emplace_back(); + sublist = context->SourceList.end() - 1; + + sublist->FreeMask = ~U64(0); + sublist->Sources = static_cast(al_calloc(16, sizeof(ALsource)*64)); + if(UNLIKELY(!sublist->Sources)) + { + context->SourceList.pop_back(); + alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate source batch"); + return nullptr; + } + + slidx = 0; + source = sublist->Sources + slidx; + } + + source = new (source) ALsource{device->NumAuxSends}; + + /* Add 1 to avoid source ID 0. */ + source->id = ((lidx<<6) | slidx) + 1; + + context->NumSources += 1; + sublist->FreeMask &= ~(U64(1)<id - 1; + ALsizei lidx = id >> 6; + ALsizei slidx = id & 0x3f; + + ALCdevice *device{context->Device}; + ALCdevice_Lock(device); + ALvoice *voice{GetSourceVoice(source, context)}; + if(voice) + { + voice->Source.store(nullptr, std::memory_order_relaxed); + voice->Playing.store(false, std::memory_order_release); + } + ALCdevice_Unlock(device); + + source->~ALsource(); + + context->SourceList[lidx].FreeMask |= U64(1) << slidx; + context->NumSources--; +} + + +inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept +{ + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= context->SourceList.size())) + return nullptr; + SourceSubList &sublist{context->SourceList[lidx]}; + if(UNLIKELY(sublist.FreeMask & (U64(1)<> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= device->BufferList.size())) + return nullptr; + BufferSubList &sublist = device->BufferList[lidx]; + if(UNLIKELY(sublist.FreeMask & (U64(1)<> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= device->FilterList.size())) + return nullptr; + FilterSubList &sublist = device->FilterList[lidx]; + if(UNLIKELY(sublist.FreeMask & (U64(1)<= context->EffectSlotList.size())) + return nullptr; + return context->EffectSlotList[id].get(); +} + + +enum SourceProp { + srcPitch = AL_PITCH, + srcGain = AL_GAIN, + srcMinGain = AL_MIN_GAIN, + srcMaxGain = AL_MAX_GAIN, + srcMaxDistance = AL_MAX_DISTANCE, + srcRolloffFactor = AL_ROLLOFF_FACTOR, + srcDopplerFactor = AL_DOPPLER_FACTOR, + srcConeOuterGain = AL_CONE_OUTER_GAIN, + srcSecOffset = AL_SEC_OFFSET, + srcSampleOffset = AL_SAMPLE_OFFSET, + srcByteOffset = AL_BYTE_OFFSET, + srcConeInnerAngle = AL_CONE_INNER_ANGLE, + srcConeOuterAngle = AL_CONE_OUTER_ANGLE, + srcRefDistance = AL_REFERENCE_DISTANCE, + + srcPosition = AL_POSITION, + srcVelocity = AL_VELOCITY, + srcDirection = AL_DIRECTION, + + srcSourceRelative = AL_SOURCE_RELATIVE, + srcLooping = AL_LOOPING, + srcBuffer = AL_BUFFER, + srcSourceState = AL_SOURCE_STATE, + srcBuffersQueued = AL_BUFFERS_QUEUED, + srcBuffersProcessed = AL_BUFFERS_PROCESSED, + srcSourceType = AL_SOURCE_TYPE, + + /* ALC_EXT_EFX */ + srcConeOuterGainHF = AL_CONE_OUTER_GAINHF, + srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR, + srcRoomRolloffFactor = AL_ROOM_ROLLOFF_FACTOR, + srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO, + srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, + srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, + srcDirectFilter = AL_DIRECT_FILTER, + srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER, + + /* AL_SOFT_direct_channels */ + srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT, + + /* AL_EXT_source_distance_model */ + srcDistanceModel = AL_DISTANCE_MODEL, + + /* AL_SOFT_source_latency */ + srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT, + srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT, + + /* AL_EXT_STEREO_ANGLES */ + srcAngles = AL_STEREO_ANGLES, + + /* AL_EXT_SOURCE_RADIUS */ + srcRadius = AL_SOURCE_RADIUS, + + /* AL_EXT_BFORMAT */ + srcOrientation = AL_ORIENTATION, + + /* AL_SOFT_source_resampler */ + srcResampler = AL_SOURCE_RESAMPLER_SOFT, + + /* AL_SOFT_source_spatialize */ + srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT, + + /* ALC_SOFT_device_clock */ + srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT, + srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT, +}; + +/** + * Returns if the last known state for the source was playing or paused. Does + * not sync with the mixer voice. + */ +inline bool IsPlayingOrPaused(ALsource *source) +{ return source->state == AL_PLAYING || source->state == AL_PAUSED; } + +/** + * Returns an updated source state using the matching voice's status (or lack + * thereof). + */ +inline ALenum GetSourceState(ALsource *source, ALvoice *voice) +{ + if(!voice && source->state == AL_PLAYING) + source->state = AL_STOPPED; + return source->state; +} + +/** + * Returns if the source should specify an update, given the context's + * deferring state and the source's last known state. + */ +inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context) +{ + return !context->DeferUpdates.load(std::memory_order_acquire) && + IsPlayingOrPaused(source); +} + + +/** Can only be called while the mixer is locked! */ +void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state) { AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange); ALbitfieldSOFT enabledevt; - enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire); + enabledevt = context->EnabledEvts.load(std::memory_order_acquire); if(!(enabledevt&EventType_SourceStateChange)) return; evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT; @@ -247,7 +708,7 @@ static void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state) } -static ALint FloatValsByProp(ALenum prop) +ALint FloatValsByProp(ALenum prop) { if(prop != (ALenum)((SourceProp)prop)) return 0; @@ -311,7 +772,7 @@ static ALint FloatValsByProp(ALenum prop) } return 0; } -static ALint DoubleValsByProp(ALenum prop) +ALint DoubleValsByProp(ALenum prop) { if(prop != (ALenum)((SourceProp)prop)) return 0; @@ -374,7 +835,7 @@ static ALint DoubleValsByProp(ALenum prop) return 0; } -static ALint IntValsByProp(ALenum prop) +ALint IntValsByProp(ALenum prop) { if(prop != (ALenum)((SourceProp)prop)) return 0; @@ -435,7 +896,7 @@ static ALint IntValsByProp(ALenum prop) } return 0; } -static ALint Int64ValsByProp(ALenum prop) +ALint Int64ValsByProp(ALenum prop) { if(prop != (ALenum)((SourceProp)prop)) return 0; @@ -499,6 +960,10 @@ static ALint Int64ValsByProp(ALenum prop) } +ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values); +ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values); +ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values); + #define CHECKVAL(x) do { \ if(!(x)) \ { \ @@ -510,13 +975,13 @@ static ALint Int64ValsByProp(ALenum prop) #define DO_UPDATEPROPS() do { \ ALvoice *voice; \ if(SourceShouldUpdate(Source, Context) && \ - (voice=GetSourceVoice(Source, Context)) != NULL) \ + (voice=GetSourceVoice(Source, Context)) != nullptr) \ UpdateSourceProps(Source, voice, Context); \ else \ Source->PropsClean.clear(std::memory_order_release); \ } while(0) -static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values) +ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values) { ALint ival; @@ -743,14 +1208,15 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp p SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source float property 0x%04x", prop); } -static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values) +ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values) { - ALCdevice *device = Context->Device; - ALbuffer *buffer = NULL; - ALfilter *filter = NULL; - ALeffectslot *slot = NULL; - ALbufferlistitem *oldlist; + ALCdevice *device{Context->Device}; + ALbuffer *buffer{nullptr}; + ALfilter *filter{nullptr}; + ALeffectslot *slot{nullptr}; + ALbufferlistitem *oldlist{nullptr}; std::unique_lock slotlock; + std::unique_lock buflock; ALfloat fvals[6]; switch(prop) @@ -776,57 +1242,47 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p Source->Looping = (ALboolean)*values; if(IsPlayingOrPaused(Source)) { - ALvoice *voice = GetSourceVoice(Source, Context); + ALvoice *voice{GetSourceVoice(Source, Context)}; if(voice) { if(Source->Looping) - ATOMIC_STORE(&voice->loop_buffer, Source->queue, almemory_order_release); + voice->loop_buffer.store(Source->queue, std::memory_order_release); else - ATOMIC_STORE(&voice->loop_buffer, static_cast(nullptr), - almemory_order_release); + voice->loop_buffer.store(nullptr, std::memory_order_release); /* If the source is playing, wait for the current mix to finish * to ensure it isn't currently looping back or reaching the * end. */ - while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1)) + while((device->MixCount.load(std::memory_order_acquire)&1)) althrd_yield(); } } return AL_TRUE; case AL_BUFFER: - LockBufferList(device); - if(!(*values == 0 || (buffer=LookupBuffer(device, *values)) != NULL)) - { - UnlockBufferList(device); + buflock = std::unique_lock{device->BufferLock}; + if(!(*values == 0 || (buffer=LookupBuffer(device, *values)) != nullptr)) SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid buffer ID %u", *values); - } if(buffer && buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) - { - UnlockBufferList(device); SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE, "Setting non-persistently mapped buffer %u", buffer->id); - } else { ALenum state = GetSourceState(Source, GetSourceVoice(Source, Context)); if(state == AL_PLAYING || state == AL_PAUSED) - { - UnlockBufferList(device); SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE, "Setting buffer on playing or paused source %u", Source->id); - } } oldlist = Source->queue; - if(buffer != NULL) + if(buffer != nullptr) { /* Add the selected buffer to a one-item queue */ - ALbufferlistitem *newlist = static_cast(al_calloc(DEF_ALIGN, + auto newlist = static_cast(al_calloc(DEF_ALIGN, FAM_SIZE(ALbufferlistitem, buffers, 1))); ATOMIC_INIT(&newlist->next, static_cast(nullptr)); newlist->max_samples = buffer->SampleLen; @@ -842,18 +1298,17 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p { /* Source is now Undetermined */ Source->SourceType = AL_UNDETERMINED; - Source->queue = NULL; + Source->queue = nullptr; } - UnlockBufferList(device); + buflock.unlock(); /* Delete all elements in the previous queue */ - while(oldlist != NULL) + while(oldlist != nullptr) { - ALsizei i; - ALbufferlistitem *temp = oldlist; - oldlist = ATOMIC_LOAD(&temp->next, almemory_order_relaxed); + ALbufferlistitem *temp{oldlist}; + oldlist = temp->next.load(std::memory_order_relaxed); - for(i = 0;i < temp->num_buffers;i++) + for(ALsizei i{0};i < temp->num_buffers;i++) { if(temp->buffers[i]) DecrementRef(&temp->buffers[i]->ref); @@ -872,10 +1327,8 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p if(IsPlayingOrPaused(Source)) { - ALvoice *voice; - ALCdevice_Lock(Context->Device); - voice = GetSourceVoice(Source, Context); + ALvoice *voice{GetSourceVoice(Source, Context)}; if(voice) { if(ApplyOffset(Source, voice) == AL_FALSE) @@ -891,7 +1344,7 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_DIRECT_FILTER: LockFilterList(device); - if(!(*values == 0 || (filter=LookupFilter(device, *values)) != NULL)) + if(!(*values == 0 || (filter=LookupFilter(device, *values)) != nullptr)) { UnlockFilterList(device); SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u", @@ -977,22 +1430,15 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_AUXILIARY_SEND_FILTER: slotlock = std::unique_lock{Context->EffectSlotLock}; - if(!(values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != NULL)) - { - slotlock.unlock(); + if(!(values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != nullptr)) SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid effect ID %u", values[0]); - } if((ALuint)values[1] >= (ALuint)device->NumAuxSends) - { - slotlock.unlock(); SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid send %u", values[1]); - } LockFilterList(device); - if(!(values[2] == 0 || (filter=LookupFilter(device, values[2])) != NULL)) + if(!(values[2] == 0 || (filter=LookupFilter(device, values[2])) != nullptr)) { UnlockFilterList(device); - slotlock.unlock(); SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u", values[2]); } @@ -1018,7 +1464,6 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p if(slot != Source->Send[values[1]].Slot && IsPlayingOrPaused(Source)) { - ALvoice *voice; /* Add refcount on the new slot, and release the previous slot */ if(slot) IncrementRef(&slot->ref); if(Source->Send[values[1]].Slot) @@ -1028,10 +1473,9 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p /* We must force an update if the auxiliary slot changed on an * active source, in case the slot is about to be deleted. */ - if((voice=GetSourceVoice(Source, Context)) != NULL) - UpdateSourceProps(Source, voice, Context); - else - Source->PropsClean.clear(std::memory_order_release); + ALvoice *voice{GetSourceVoice(Source, Context)}; + if(voice) UpdateSourceProps(Source, voice, Context); + else Source->PropsClean.clear(std::memory_order_release); } else { @@ -1096,7 +1540,7 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p prop); } -static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values) +ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values) { ALfloat fvals[6]; ALint ivals[3]; @@ -1202,9 +1646,13 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp #undef CHECKVAL -static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values) +ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values); +ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values); +ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values); + +ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values) { - ALCdevice *device = Context->Device; + ALCdevice *device{Context->Device}; ClockLatency clocktime; ALuint64 srcclock; ALint ivals[3]; @@ -1282,280 +1730,17 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p values[0] = Source->StereoPan[0]; values[1] = Source->StereoPan[1]; return AL_TRUE; - - case AL_SEC_OFFSET_LATENCY_SOFT: - /* Get the source offset with the clock time first. Then get the - * clock time with the device latency. Order is important. - */ - values[0] = GetSourceSecOffset(Source, Context, &srcclock); - { std::lock_guard _{device->BackendLock}; - clocktime = GetClockLatency(device); - } - if(srcclock == (ALuint64)clocktime.ClockTime) - values[1] = (ALdouble)clocktime.Latency / 1000000000.0; - else - { - /* If the clock time incremented, reduce the latency by that - * much since it's that much closer to the source offset it got - * earlier. - */ - ALuint64 diff = clocktime.ClockTime - srcclock; - values[1] = (ALdouble)(clocktime.Latency - minu64(clocktime.Latency, diff)) / - 1000000000.0; - } - return AL_TRUE; - - case AL_SEC_OFFSET_CLOCK_SOFT: - values[0] = GetSourceSecOffset(Source, Context, &srcclock); - values[1] = srcclock / 1000000000.0; - return AL_TRUE; - - case AL_POSITION: - values[0] = Source->Position[0]; - values[1] = Source->Position[1]; - values[2] = Source->Position[2]; - return AL_TRUE; - - case AL_VELOCITY: - values[0] = Source->Velocity[0]; - values[1] = Source->Velocity[1]; - values[2] = Source->Velocity[2]; - return AL_TRUE; - - case AL_DIRECTION: - values[0] = Source->Direction[0]; - values[1] = Source->Direction[1]; - values[2] = Source->Direction[2]; - return AL_TRUE; - - case AL_ORIENTATION: - values[0] = Source->Orientation[0][0]; - values[1] = Source->Orientation[0][1]; - values[2] = Source->Orientation[0][2]; - values[3] = Source->Orientation[1][0]; - values[4] = Source->Orientation[1][1]; - values[5] = Source->Orientation[1][2]; - return AL_TRUE; - - /* 1x int */ - case AL_SOURCE_RELATIVE: - case AL_LOOPING: - case AL_SOURCE_STATE: - case AL_BUFFERS_QUEUED: - case AL_BUFFERS_PROCESSED: - case AL_SOURCE_TYPE: - case AL_DIRECT_FILTER_GAINHF_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: - case AL_DIRECT_CHANNELS_SOFT: - case AL_DISTANCE_MODEL: - case AL_SOURCE_RESAMPLER_SOFT: - case AL_SOURCE_SPATIALIZE_SOFT: - if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE) - *values = (ALdouble)ivals[0]; - return err; - - case AL_BUFFER: - case AL_DIRECT_FILTER: - case AL_AUXILIARY_SEND_FILTER: - case AL_SAMPLE_OFFSET_LATENCY_SOFT: - case AL_SAMPLE_OFFSET_CLOCK_SOFT: - break; - } - - ERR("Unexpected property: 0x%04x\n", prop); - SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source double property 0x%04x", - prop); -} - -static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values) -{ - ALbufferlistitem *BufferList; - ALdouble dvals[6]; - ALboolean err; - - switch(prop) - { - case AL_SOURCE_RELATIVE: - *values = Source->HeadRelative; - return AL_TRUE; - - case AL_LOOPING: - *values = Source->Looping; - return AL_TRUE; - - case AL_BUFFER: - BufferList = (Source->SourceType == AL_STATIC) ? Source->queue : NULL; - *values = (BufferList && BufferList->num_buffers >= 1 && BufferList->buffers[0]) ? - BufferList->buffers[0]->id : 0; - return AL_TRUE; - - case AL_SOURCE_STATE: - *values = GetSourceState(Source, GetSourceVoice(Source, Context)); - return AL_TRUE; - - case AL_BUFFERS_QUEUED: - if(!(BufferList=Source->queue)) - *values = 0; - else - { - ALsizei count = 0; - do { - count += BufferList->num_buffers; - BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); - } while(BufferList != NULL); - *values = count; - } - return AL_TRUE; - - case AL_BUFFERS_PROCESSED: - if(Source->Looping || Source->SourceType != AL_STREAMING) - { - /* Buffers on a looping source are in a perpetual state of - * PENDING, so don't report any as PROCESSED */ - *values = 0; - } - else - { - const ALbufferlistitem *BufferList = Source->queue; - const ALbufferlistitem *Current = NULL; - ALsizei played = 0; - ALvoice *voice; - - if((voice=GetSourceVoice(Source, Context)) != NULL) - Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); - else if(Source->state == AL_INITIAL) - Current = BufferList; - - while(BufferList && BufferList != Current) - { - played += BufferList->num_buffers; - BufferList = BufferList->next.load(std::memory_order_relaxed); - } - *values = played; - } - return AL_TRUE; - - case AL_SOURCE_TYPE: - *values = Source->SourceType; - return AL_TRUE; - - case AL_DIRECT_FILTER_GAINHF_AUTO: - *values = Source->DryGainHFAuto; - return AL_TRUE; - - case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: - *values = Source->WetGainAuto; - return AL_TRUE; - - case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: - *values = Source->WetGainHFAuto; - return AL_TRUE; - - case AL_DIRECT_CHANNELS_SOFT: - *values = Source->DirectChannels; - return AL_TRUE; - - case AL_DISTANCE_MODEL: - *values = static_cast(Source->mDistanceModel); - return AL_TRUE; - - case AL_SOURCE_RESAMPLER_SOFT: - *values = Source->Resampler; - return AL_TRUE; - - case AL_SOURCE_SPATIALIZE_SOFT: - *values = Source->Spatialize; - return AL_TRUE; - - /* 1x float/double */ - case AL_CONE_INNER_ANGLE: - case AL_CONE_OUTER_ANGLE: - case AL_PITCH: - case AL_GAIN: - case AL_MIN_GAIN: - case AL_MAX_GAIN: - case AL_REFERENCE_DISTANCE: - case AL_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAIN: - case AL_MAX_DISTANCE: - case AL_SEC_OFFSET: - case AL_SAMPLE_OFFSET: - case AL_BYTE_OFFSET: - case AL_DOPPLER_FACTOR: - case AL_AIR_ABSORPTION_FACTOR: - case AL_ROOM_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAINHF: - case AL_SOURCE_RADIUS: - if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) - *values = (ALint)dvals[0]; - return err; - - /* 3x float/double */ - case AL_POSITION: - case AL_VELOCITY: - case AL_DIRECTION: - if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) - { - values[0] = (ALint)dvals[0]; - values[1] = (ALint)dvals[1]; - values[2] = (ALint)dvals[2]; - } - return err; - - /* 6x float/double */ - case AL_ORIENTATION: - if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) - { - values[0] = (ALint)dvals[0]; - values[1] = (ALint)dvals[1]; - values[2] = (ALint)dvals[2]; - values[3] = (ALint)dvals[3]; - values[4] = (ALint)dvals[4]; - values[5] = (ALint)dvals[5]; - } - return err; - - case AL_SAMPLE_OFFSET_LATENCY_SOFT: - case AL_SAMPLE_OFFSET_CLOCK_SOFT: - break; /* i64 only */ - case AL_SEC_OFFSET_LATENCY_SOFT: - case AL_SEC_OFFSET_CLOCK_SOFT: - break; /* Double only */ - case AL_STEREO_ANGLES: - break; /* Float/double only */ - - case AL_DIRECT_FILTER: - case AL_AUXILIARY_SEND_FILTER: - break; /* ??? */ - } - - ERR("Unexpected property: 0x%04x\n", prop); - SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x", - prop); -} - -static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values) -{ - ALCdevice *device = Context->Device; - ClockLatency clocktime; - ALuint64 srcclock; - ALdouble dvals[6]; - ALint ivals[3]; - ALboolean err; - - switch(prop) - { - case AL_SAMPLE_OFFSET_LATENCY_SOFT: + + case AL_SEC_OFFSET_LATENCY_SOFT: /* Get the source offset with the clock time first. Then get the * clock time with the device latency. Order is important. */ - values[0] = GetSourceSampleOffset(Source, Context, &srcclock); + values[0] = GetSourceSecOffset(Source, Context, &srcclock); { std::lock_guard _{device->BackendLock}; clocktime = GetClockLatency(device); } if(srcclock == (ALuint64)clocktime.ClockTime) - values[1] = clocktime.Latency; + values[1] = (ALdouble)clocktime.Latency / 1000000000.0; else { /* If the clock time incremented, reduce the latency by that @@ -1563,62 +1748,42 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp * earlier. */ ALuint64 diff = clocktime.ClockTime - srcclock; - values[1] = clocktime.Latency - minu64(clocktime.Latency, diff); + values[1] = (ALdouble)(clocktime.Latency - minu64(clocktime.Latency, diff)) / + 1000000000.0; } return AL_TRUE; - case AL_SAMPLE_OFFSET_CLOCK_SOFT: - values[0] = GetSourceSampleOffset(Source, Context, &srcclock); - values[1] = srcclock; + case AL_SEC_OFFSET_CLOCK_SOFT: + values[0] = GetSourceSecOffset(Source, Context, &srcclock); + values[1] = srcclock / 1000000000.0; return AL_TRUE; - /* 1x float/double */ - case AL_CONE_INNER_ANGLE: - case AL_CONE_OUTER_ANGLE: - case AL_PITCH: - case AL_GAIN: - case AL_MIN_GAIN: - case AL_MAX_GAIN: - case AL_REFERENCE_DISTANCE: - case AL_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAIN: - case AL_MAX_DISTANCE: - case AL_SEC_OFFSET: - case AL_SAMPLE_OFFSET: - case AL_BYTE_OFFSET: - case AL_DOPPLER_FACTOR: - case AL_AIR_ABSORPTION_FACTOR: - case AL_ROOM_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAINHF: - case AL_SOURCE_RADIUS: - if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) - *values = (ALint64)dvals[0]; - return err; - - /* 3x float/double */ case AL_POSITION: + values[0] = Source->Position[0]; + values[1] = Source->Position[1]; + values[2] = Source->Position[2]; + return AL_TRUE; + case AL_VELOCITY: + values[0] = Source->Velocity[0]; + values[1] = Source->Velocity[1]; + values[2] = Source->Velocity[2]; + return AL_TRUE; + case AL_DIRECTION: - if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) - { - values[0] = (ALint64)dvals[0]; - values[1] = (ALint64)dvals[1]; - values[2] = (ALint64)dvals[2]; - } - return err; + values[0] = Source->Direction[0]; + values[1] = Source->Direction[1]; + values[2] = Source->Direction[2]; + return AL_TRUE; - /* 6x float/double */ case AL_ORIENTATION: - if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) - { - values[0] = (ALint64)dvals[0]; - values[1] = (ALint64)dvals[1]; - values[2] = (ALint64)dvals[2]; - values[3] = (ALint64)dvals[3]; - values[4] = (ALint64)dvals[4]; - values[5] = (ALint64)dvals[5]; - } - return err; + values[0] = Source->Orientation[0][0]; + values[1] = Source->Orientation[0][1]; + values[2] = Source->Orientation[0][2]; + values[3] = Source->Orientation[1][0]; + values[4] = Source->Orientation[1][1]; + values[5] = Source->Orientation[1][2]; + return AL_TRUE; /* 1x int */ case AL_SOURCE_RELATIVE: @@ -1635,1817 +1800,1612 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp case AL_SOURCE_RESAMPLER_SOFT: case AL_SOURCE_SPATIALIZE_SOFT: if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE) - *values = ivals[0]; + *values = (ALdouble)ivals[0]; return err; - /* 1x uint */ case AL_BUFFER: case AL_DIRECT_FILTER: - if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE) - *values = (ALuint)ivals[0]; - return err; - - /* 3x uint */ case AL_AUXILIARY_SEND_FILTER: - if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE) - { - values[0] = (ALuint)ivals[0]; - values[1] = (ALuint)ivals[1]; - values[2] = (ALuint)ivals[2]; - } - return err; - - case AL_SEC_OFFSET_LATENCY_SOFT: - case AL_SEC_OFFSET_CLOCK_SOFT: - break; /* Double only */ - case AL_STEREO_ANGLES: - break; /* Float/double only */ + case AL_SAMPLE_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: + break; } ERR("Unexpected property: 0x%04x\n", prop); - SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x", + SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source double property 0x%04x", prop); } - -AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) +ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values) { - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + ALbufferlistitem *BufferList; + ALdouble dvals[6]; + ALboolean err; - if(n < 0) - alSetError(context.get(), AL_INVALID_VALUE, "Generating %d sources", n); - else if(n == 1) - { - ALsource *source = AllocSource(context.get()); - if(source) sources[0] = source->id; - } - else + switch(prop) { - al::vector tempids(n); - auto alloc_end = std::find_if_not(tempids.begin(), tempids.end(), - [&context](ALuint &id) -> bool - { - ALsource *source = AllocSource(context.get()); - if(!source) return false; - id = source->id; - return true; - } - ); - if(alloc_end != tempids.end()) - alDeleteSources(std::distance(tempids.begin(), alloc_end), tempids.data()); - else - std::copy(tempids.cbegin(), tempids.cend(), sources); - } -} - + case AL_SOURCE_RELATIVE: + *values = Source->HeadRelative; + return AL_TRUE; -AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + case AL_LOOPING: + *values = Source->Looping; + return AL_TRUE; - if(n < 0) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Deleting %d sources", n); + case AL_BUFFER: + BufferList = (Source->SourceType == AL_STATIC) ? Source->queue : nullptr; + *values = (BufferList && BufferList->num_buffers >= 1 && BufferList->buffers[0]) ? + BufferList->buffers[0]->id : 0; + return AL_TRUE; - std::lock_guard _{context->SourceLock}; + case AL_SOURCE_STATE: + *values = GetSourceState(Source, GetSourceVoice(Source, Context)); + return AL_TRUE; - /* Check that all Sources are valid */ - const ALuint *sources_end = sources + n; - auto invsrc = std::find_if_not(sources, sources_end, - [&context](ALuint sid) -> bool - { - if(!LookupSource(context.get(), sid)) + case AL_BUFFERS_QUEUED: + if(!(BufferList=Source->queue)) + *values = 0; + else { - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", sid); - return false; + ALsizei count = 0; + do { + count += BufferList->num_buffers; + BufferList = BufferList->next.load(std::memory_order_relaxed); + } while(BufferList != nullptr); + *values = count; } - return true; - } - ); - if(LIKELY(invsrc == sources_end)) - { - /* All good. Delete source IDs. */ - std::for_each(sources, sources_end, - [&context](ALuint sid) -> void + return AL_TRUE; + + case AL_BUFFERS_PROCESSED: + if(Source->Looping || Source->SourceType != AL_STREAMING) { - ALsource *src = LookupSource(context.get(), sid); - if(src) FreeSource(context.get(), src); + /* Buffers on a looping source are in a perpetual state of + * PENDING, so don't report any as PROCESSED */ + *values = 0; } - ); - } -} + else + { + const ALbufferlistitem *BufferList{Source->queue}; + const ALbufferlistitem *Current{nullptr}; + ALsizei played{0}; + ALvoice *voice{GetSourceVoice(Source, Context)}; + if(voice != nullptr) + Current = voice->current_buffer.load(std::memory_order_relaxed); + else if(Source->state == AL_INITIAL) + Current = BufferList; -AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) -{ - ContextRef context{GetContextRef()}; - if(LIKELY(context)) - { - std::lock_guard _{context->SourceLock}; - if(LookupSource(context.get(), source) != nullptr) + while(BufferList && BufferList != Current) + { + played += BufferList->num_buffers; + BufferList = BufferList->next.load(std::memory_order_relaxed); + } + *values = played; + } return AL_TRUE; - } - return AL_FALSE; -} - - -AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; - - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source = LookupSource(context.get(), source); - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(FloatValsByProp(param) != 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid float property 0x%04x", param); - else - SetSourcefv(Source, context.get(), static_cast(param), &value); -} - -AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; - - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source = LookupSource(context.get(), source); - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(FloatValsByProp(param) != 3) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param); - else - { - ALfloat fvals[3] = { value1, value2, value3 }; - SetSourcefv(Source, context.get(), static_cast(param), fvals); - } -} -AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + case AL_SOURCE_TYPE: + *values = Source->SourceType; + return AL_TRUE; - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source = LookupSource(context.get(), source); - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!values) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(FloatValsByProp(param) < 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param); - else - SetSourcefv(Source, context.get(), static_cast(param), values); -} + case AL_DIRECT_FILTER_GAINHF_AUTO: + *values = Source->DryGainHFAuto; + return AL_TRUE; + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + *values = Source->WetGainAuto; + return AL_TRUE; -AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + *values = Source->WetGainHFAuto; + return AL_TRUE; - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source = LookupSource(context.get(), source); - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(DoubleValsByProp(param) != 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid double property 0x%04x", param); - else - { - ALfloat fval = (ALfloat)value; - SetSourcefv(Source, context.get(), static_cast(param), &fval); - } -} + case AL_DIRECT_CHANNELS_SOFT: + *values = Source->DirectChannels; + return AL_TRUE; -AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + case AL_DISTANCE_MODEL: + *values = static_cast(Source->mDistanceModel); + return AL_TRUE; - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source = LookupSource(context.get(), source); - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(DoubleValsByProp(param) != 3) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param); - else - { - ALfloat fvals[3] = { (ALfloat)value1, (ALfloat)value2, (ALfloat)value3 }; - SetSourcefv(Source, context.get(), static_cast(param), fvals); - } -} + case AL_SOURCE_RESAMPLER_SOFT: + *values = Source->Resampler; + return AL_TRUE; -AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + case AL_SOURCE_SPATIALIZE_SOFT: + *values = Source->Spatialize; + return AL_TRUE; - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source = LookupSource(context.get(), source); - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!values) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else - { - ALint count{DoubleValsByProp(param)}; - if(count < 1 || count > 6) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param); - else - { - ALfloat fvals[6]; - ALint i; + /* 1x float/double */ + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_PITCH: + case AL_GAIN: + case AL_MIN_GAIN: + case AL_MAX_GAIN: + case AL_REFERENCE_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_CONE_OUTER_GAIN: + case AL_MAX_DISTANCE: + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + case AL_DOPPLER_FACTOR: + case AL_AIR_ABSORPTION_FACTOR: + case AL_ROOM_ROLLOFF_FACTOR: + case AL_CONE_OUTER_GAINHF: + case AL_SOURCE_RADIUS: + if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) + *values = (ALint)dvals[0]; + return err; - for(i = 0;i < count;i++) - fvals[i] = (ALfloat)values[i]; - SetSourcefv(Source, context.get(), static_cast(param), fvals); - } - } -} + /* 3x float/double */ + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) + { + values[0] = (ALint)dvals[0]; + values[1] = (ALint)dvals[1]; + values[2] = (ALint)dvals[2]; + } + return err; + + /* 6x float/double */ + case AL_ORIENTATION: + if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) + { + values[0] = (ALint)dvals[0]; + values[1] = (ALint)dvals[1]; + values[2] = (ALint)dvals[2]; + values[3] = (ALint)dvals[3]; + values[4] = (ALint)dvals[4]; + values[5] = (ALint)dvals[5]; + } + return err; + case AL_SAMPLE_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: + break; /* i64 only */ + case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: + break; /* Double only */ + case AL_STEREO_ANGLES: + break; /* Float/double only */ -AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + case AL_DIRECT_FILTER: + case AL_AUXILIARY_SEND_FILTER: + break; /* ??? */ + } - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source = LookupSource(context.get(), source); - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(IntValsByProp(param) != 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer property 0x%04x", param); - else - SetSourceiv(Source, context.get(), static_cast(param), &value); + ERR("Unexpected property: 0x%04x\n", prop); + SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x", + prop); } -AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) +ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values) { - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + ALCdevice *device = Context->Device; + ClockLatency clocktime; + ALuint64 srcclock; + ALdouble dvals[6]; + ALint ivals[3]; + ALboolean err; - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source = LookupSource(context.get(), source); - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(IntValsByProp(param) != 3) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param); - else + switch(prop) { - ALint ivals[3] = { value1, value2, value3 }; - SetSourceiv(Source, context.get(), static_cast(param), ivals); - } -} + case AL_SAMPLE_OFFSET_LATENCY_SOFT: + /* Get the source offset with the clock time first. Then get the + * clock time with the device latency. Order is important. + */ + values[0] = GetSourceSampleOffset(Source, Context, &srcclock); + { std::lock_guard _{device->BackendLock}; + clocktime = GetClockLatency(device); + } + if(srcclock == (ALuint64)clocktime.ClockTime) + values[1] = clocktime.Latency; + else + { + /* If the clock time incremented, reduce the latency by that + * much since it's that much closer to the source offset it got + * earlier. + */ + ALuint64 diff{clocktime.ClockTime - srcclock}; + values[1] = clocktime.Latency - minu64(clocktime.Latency, diff); + } + return AL_TRUE; -AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + case AL_SAMPLE_OFFSET_CLOCK_SOFT: + values[0] = GetSourceSampleOffset(Source, Context, &srcclock); + values[1] = srcclock; + return AL_TRUE; - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source = LookupSource(context.get(), source); - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!values) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(IntValsByProp(param) < 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param); - else - SetSourceiv(Source, context.get(), static_cast(param), values); -} + /* 1x float/double */ + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_PITCH: + case AL_GAIN: + case AL_MIN_GAIN: + case AL_MAX_GAIN: + case AL_REFERENCE_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_CONE_OUTER_GAIN: + case AL_MAX_DISTANCE: + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + case AL_DOPPLER_FACTOR: + case AL_AIR_ABSORPTION_FACTOR: + case AL_ROOM_ROLLOFF_FACTOR: + case AL_CONE_OUTER_GAINHF: + case AL_SOURCE_RADIUS: + if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) + *values = (ALint64)dvals[0]; + return err; + + /* 3x float/double */ + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) + { + values[0] = (ALint64)dvals[0]; + values[1] = (ALint64)dvals[1]; + values[2] = (ALint64)dvals[2]; + } + return err; + /* 6x float/double */ + case AL_ORIENTATION: + if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) + { + values[0] = (ALint64)dvals[0]; + values[1] = (ALint64)dvals[1]; + values[2] = (ALint64)dvals[2]; + values[3] = (ALint64)dvals[3]; + values[4] = (ALint64)dvals[4]; + values[5] = (ALint64)dvals[5]; + } + return err; -AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + /* 1x int */ + case AL_SOURCE_RELATIVE: + case AL_LOOPING: + case AL_SOURCE_STATE: + case AL_BUFFERS_QUEUED: + case AL_BUFFERS_PROCESSED: + case AL_SOURCE_TYPE: + case AL_DIRECT_FILTER_GAINHF_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + case AL_DIRECT_CHANNELS_SOFT: + case AL_DISTANCE_MODEL: + case AL_SOURCE_RESAMPLER_SOFT: + case AL_SOURCE_SPATIALIZE_SOFT: + if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE) + *values = ivals[0]; + return err; - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(Int64ValsByProp(param) != 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param); - else - SetSourcei64v(Source, context.get(), static_cast(param), &value); -} + /* 1x uint */ + case AL_BUFFER: + case AL_DIRECT_FILTER: + if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE) + *values = (ALuint)ivals[0]; + return err; -AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; + /* 3x uint */ + case AL_AUXILIARY_SEND_FILTER: + if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE) + { + values[0] = (ALuint)ivals[0]; + values[1] = (ALuint)ivals[1]; + values[2] = (ALuint)ivals[2]; + } + return err; - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(Int64ValsByProp(param) != 3) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param); - else - { - ALint64SOFT i64vals[3] = { value1, value2, value3 }; - SetSourcei64v(Source, context.get(), static_cast(param), i64vals); + case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: + break; /* Double only */ + case AL_STEREO_ANGLES: + break; /* Float/double only */ } -} - -AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; - std::lock_guard _{context->PropLock}; - std::lock_guard __{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!values) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(Int64ValsByProp(param) < 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param); - else - SetSourcei64v(Source, context.get(), static_cast(param), values); + ERR("Unexpected property: 0x%04x\n", prop); + SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x", + prop); } +} // namespace -AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value) +AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!value) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(FloatValsByProp(param) != 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid float property 0x%04x", param); - else + if(n < 0) + alSetError(context.get(), AL_INVALID_VALUE, "Generating %d sources", n); + else if(n == 1) { - ALdouble dval; - if(GetSourcedv(Source, context.get(), static_cast(param), &dval)) - *value = (ALfloat)dval; + ALsource *source = AllocSource(context.get()); + if(source) sources[0] = source->id; } -} - - -AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; - - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!(value1 && value2 && value3)) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(FloatValsByProp(param) != 3) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param); else { - ALdouble dvals[3]; - if(GetSourcedv(Source, context.get(), static_cast(param), dvals)) - { - *value1 = (ALfloat)dvals[0]; - *value2 = (ALfloat)dvals[1]; - *value3 = (ALfloat)dvals[2]; - } + al::vector tempids(n); + auto alloc_end = std::find_if_not(tempids.begin(), tempids.end(), + [&context](ALuint &id) -> bool + { + ALsource *source{AllocSource(context.get())}; + if(!source) return false; + id = source->id; + return true; + } + ); + if(alloc_end != tempids.end()) + alDeleteSources(std::distance(tempids.begin(), alloc_end), tempids.data()); + else + std::copy(tempids.cbegin(), tempids.cend(), sources); } } -AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values) +AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; + if(n < 0) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Deleting %d sources", n); + std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!values) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else - { - ALint count{FloatValsByProp(param)}; - if(count < 1 && count > 6) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param); - else + + /* Check that all Sources are valid */ + const ALuint *sources_end = sources + n; + auto invsrc = std::find_if_not(sources, sources_end, + [&context](ALuint sid) -> bool { - ALdouble dvals[6]; - if(GetSourcedv(Source, context.get(), static_cast(param), dvals)) + if(!LookupSource(context.get(), sid)) { - for(ALint i{0};i < count;i++) - values[i] = (ALfloat)dvals[i]; + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", sid); + return false; } + return true; } + ); + if(LIKELY(invsrc == sources_end)) + { + /* All good. Delete source IDs. */ + std::for_each(sources, sources_end, + [&context](ALuint sid) -> void + { + ALsource *src{LookupSource(context.get(), sid)}; + if(src) FreeSource(context.get(), src); + } + ); } } -AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; - - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!value) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(DoubleValsByProp(param) != 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid double property 0x%04x", param); - else - GetSourcedv(Source, context.get(), static_cast(param), value); -} - -AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) +AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) { ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; - - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!(value1 && value2 && value3)) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(DoubleValsByProp(param) != 3) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param); - else + if(LIKELY(context)) { - ALdouble dvals[3]; - if(GetSourcedv(Source, context.get(), static_cast(param), dvals)) - { - *value1 = dvals[0]; - *value2 = dvals[1]; - *value3 = dvals[2]; - } + std::lock_guard _{context->SourceLock}; + if(LookupSource(context.get(), source) != nullptr) + return AL_TRUE; } -} - -AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; - - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; - if(UNLIKELY(!Source)) - alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!values) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(DoubleValsByProp(param) < 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param); - else - GetSourcedv(Source, context.get(), static_cast(param), values); + return AL_FALSE; } -AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value) +AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source = LookupSource(context.get(), source); if(UNLIKELY(!Source)) alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!value) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(IntValsByProp(param) != 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer property 0x%04x", param); + else if(FloatValsByProp(param) != 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid float property 0x%04x", param); else - GetSourceiv(Source, context.get(), static_cast(param), value); + SetSourcefv(Source, context.get(), static_cast(param), &value); } - -AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) +AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source = LookupSource(context.get(), source); if(UNLIKELY(!Source)) alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!(value1 && value2 && value3)) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(IntValsByProp(param) != 3) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param); + else if(FloatValsByProp(param) != 3) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param); else { - ALint ivals[3]; - if(GetSourceiv(Source, context.get(), static_cast(param), ivals)) - { - *value1 = ivals[0]; - *value2 = ivals[1]; - *value3 = ivals[2]; - } + ALfloat fvals[3] = { value1, value2, value3 }; + SetSourcefv(Source, context.get(), static_cast(param), fvals); } } - -AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values) +AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source = LookupSource(context.get(), source); if(UNLIKELY(!Source)) alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(IntValsByProp(param) < 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param); + else if(FloatValsByProp(param) < 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param); else - GetSourceiv(Source, context.get(), static_cast(param), values); + SetSourcefv(Source, context.get(), static_cast(param), values); } -AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value) +AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source = LookupSource(context.get(), source); if(UNLIKELY(!Source)) alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!value) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(Int64ValsByProp(param) != 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param); + else if(DoubleValsByProp(param) != 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid double property 0x%04x", param); else - GetSourcei64v(Source, context.get(), static_cast(param), value); + { + ALfloat fval = (ALfloat)value; + SetSourcefv(Source, context.get(), static_cast(param), &fval); + } } -AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) +AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source = LookupSource(context.get(), source); if(UNLIKELY(!Source)) alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!(value1 && value2 && value3)) - alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(Int64ValsByProp(param) != 3) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param); + else if(DoubleValsByProp(param) != 3) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param); else { - ALint64 i64vals[3]; - if(GetSourcei64v(Source, context.get(), static_cast(param), i64vals)) - { - *value1 = i64vals[0]; - *value2 = i64vals[1]; - *value3 = i64vals[2]; - } + ALfloat fvals[3] = { (ALfloat)value1, (ALfloat)value2, (ALfloat)value3 }; + SetSourcefv(Source, context.get(), static_cast(param), fvals); } } -AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values) +AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - std::lock_guard _{context->SourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source = LookupSource(context.get(), source); if(UNLIKELY(!Source)) alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); - else if(Int64ValsByProp(param) < 1) - alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param); else - GetSourcei64v(Source, context.get(), static_cast(param), values); -} - - -AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source) -{ - alSourcePlayv(1, &source); -} -AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) -{ - ContextRef context{GetContextRef()}; - if(UNLIKELY(!context)) return; - - if(n < 0) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Playing %d sources", n); - if(n == 0) return; - - std::lock_guard _{context->SourceLock}; - for(ALsizei i{0};i < n;i++) - { - if(!LookupSource(context.get(), sources[i])) - SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]); - } - - ALCdevice *device{context->Device}; - ALCdevice_Lock(device); - /* If the device is disconnected, go right to stopped. */ - if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) - { - /* TODO: Send state change event? */ - for(ALsizei i{0};i < n;i++) - { - ALsource *source{LookupSource(context.get(), sources[i])}; - source->OffsetType = AL_NONE; - source->Offset = 0.0; - source->state = AL_STOPPED; - } - ALCdevice_Unlock(device); - return; - } - - while(n > context->MaxVoices-context->VoiceCount) - { - ALsizei newcount = context->MaxVoices << 1; - if(context->MaxVoices >= newcount) - { - ALCdevice_Unlock(device); - SETERR_RETURN(context.get(), AL_OUT_OF_MEMORY,, - "Overflow increasing voice count %d -> %d", context->MaxVoices, newcount); - } - AllocateVoices(context.get(), newcount, device->NumAuxSends); - } - - for(ALsizei i{0};i < n;i++) - { - ALsource *source{LookupSource(context.get(), sources[i])}; - /* Check that there is a queue containing at least one valid, non zero - * length buffer. - */ - ALbufferlistitem *BufferList{source->queue}; - while(BufferList && BufferList->max_samples == 0) - BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); - - /* If there's nothing to play, go right to stopped. */ - if(UNLIKELY(!BufferList)) - { - /* NOTE: A source without any playable buffers should not have an - * ALvoice since it shouldn't be in a playing or paused state. So - * there's no need to look up its voice and clear the source. - */ - ALenum oldstate = GetSourceState(source, NULL); - source->OffsetType = AL_NONE; - source->Offset = 0.0; - if(oldstate != AL_STOPPED) - { - source->state = AL_STOPPED; - SendStateChangeEvent(context.get(), source->id, AL_STOPPED); - } - continue; - } - - ALvoice *voice{GetSourceVoice(source, context.get())}; - switch(GetSourceState(source, voice)) - { - case AL_PLAYING: - assert(voice != NULL); - /* A source that's already playing is restarted from the beginning. */ - ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_relaxed); - ATOMIC_STORE(&voice->position, 0u, almemory_order_relaxed); - ATOMIC_STORE(&voice->position_fraction, 0, almemory_order_release); - continue; - - case AL_PAUSED: - assert(voice != NULL); - /* A source that's paused simply resumes. */ - ATOMIC_STORE(&voice->Playing, true, almemory_order_release); - source->state = AL_PLAYING; - SendStateChangeEvent(context.get(), source->id, AL_PLAYING); - continue; - - default: - break; - } - - /* Look for an unused voice to play this source with. */ - assert(voice == NULL); - ALint vidx{-1}; - for(ALsizei j{0};j < context->VoiceCount;j++) - { - if(ATOMIC_LOAD(&context->Voices[j]->Source, almemory_order_acquire) == NULL) - { - vidx = j; - break; - } - } - if(vidx == -1) - vidx = context->VoiceCount++; - voice = context->Voices[vidx]; - voice->Playing.store(false, std::memory_order_release); - - source->PropsClean.test_and_set(std::memory_order_acquire); - UpdateSourceProps(source, voice, context.get()); - - /* A source that's not playing or paused has any offset applied when it - * starts playing. - */ - if(source->Looping) - ATOMIC_STORE(&voice->loop_buffer, source->queue, almemory_order_relaxed); - else - ATOMIC_STORE(&voice->loop_buffer, static_cast(nullptr), - almemory_order_relaxed); - ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_relaxed); - ATOMIC_STORE(&voice->position, 0u, almemory_order_relaxed); - ATOMIC_STORE(&voice->position_fraction, 0, almemory_order_relaxed); - bool start_fading{false}; - if(ApplyOffset(source, voice) != AL_FALSE) - start_fading = ATOMIC_LOAD(&voice->position, almemory_order_relaxed) != 0 || - ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed) != 0 || - ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed) != BufferList; - - for(ALsizei j{0};j < BufferList->num_buffers;j++) - { - ALbuffer *buffer = BufferList->buffers[j]; - if(buffer) - { - voice->NumChannels = ChannelsFromFmt(buffer->FmtChannels); - voice->SampleSize = BytesFromFmt(buffer->FmtType); - break; - } - } - - /* Clear previous samples. */ - memset(voice->PrevSamples, 0, sizeof(voice->PrevSamples)); - - /* Clear the stepping value so the mixer knows not to mix this until - * the update gets applied. - */ - voice->Step = 0; - - voice->Flags = start_fading ? VOICE_IS_FADING : 0; - if(source->SourceType == AL_STATIC) voice->Flags |= VOICE_IS_STATIC; - memset(voice->Direct.Params, 0, sizeof(voice->Direct.Params[0])*voice->NumChannels); - for(ALsizei j{0};j < device->NumAuxSends;j++) - memset(voice->Send[j].Params, 0, sizeof(voice->Send[j].Params[0])*voice->NumChannels); - if(device->AvgSpeakerDist > 0.0f) - { - ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC / - (device->AvgSpeakerDist * device->Frequency); - for(ALsizei j{0};j < voice->NumChannels;j++) - NfcFilterCreate(&voice->Direct.Params[j].NFCtrlFilter, 0.0f, w1); - } - - ATOMIC_STORE(&voice->Source, source, almemory_order_relaxed); - ATOMIC_STORE(&voice->Playing, true, almemory_order_release); - source->state = AL_PLAYING; - source->VoiceIdx = vidx; + { + ALint count{DoubleValsByProp(param)}; + if(count < 1 || count > 6) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param); + else + { + ALfloat fvals[6]; + ALint i; - SendStateChangeEvent(context.get(), source->id, AL_PLAYING); + for(i = 0;i < count;i++) + fvals[i] = (ALfloat)values[i]; + SetSourcefv(Source, context.get(), static_cast(param), fvals); + } } - ALCdevice_Unlock(device); } -AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source) -{ - alSourcePausev(1, &source); -} -AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) + +AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - if(n < 0) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Pausing %d sources", n); - if(n == 0) return; + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source = LookupSource(context.get(), source); + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(IntValsByProp(param) != 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer property 0x%04x", param); + else + SetSourceiv(Source, context.get(), static_cast(param), &value); +} - for(ALsizei i{0};i < n;i++) - { - if(!LookupSource(context.get(), sources[i])) - SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]); - } +AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - ALCdevice *device{context->Device}; - ALCdevice_Lock(device); - for(ALsizei i{0};i < n;i++) + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source = LookupSource(context.get(), source); + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(IntValsByProp(param) != 3) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param); + else { - ALsource *source{LookupSource(context.get(), sources[i])}; - ALvoice *voice{GetSourceVoice(source, context.get())}; - if(!voice) voice->Playing.store(false, std::memory_order_release); - if(GetSourceState(source, voice) == AL_PLAYING) - { - source->state = AL_PAUSED; - SendStateChangeEvent(context.get(), source->id, AL_PAUSED); - } + ALint ivals[3] = { value1, value2, value3 }; + SetSourceiv(Source, context.get(), static_cast(param), ivals); } - ALCdevice_Unlock(device); } -AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source) +AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values) { - alSourceStopv(1, &source); + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source = LookupSource(context.get(), source); + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!values) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(IntValsByProp(param) < 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param); + else + SetSourceiv(Source, context.get(), static_cast(param), values); } -AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) + + +AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - if(n < 0) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Stopping %d sources", n); - if(n == 0) return; + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(Int64ValsByProp(param) != 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param); + else + SetSourcei64v(Source, context.get(), static_cast(param), &value); +} - for(ALsizei i{0};i < n;i++) - { - if(!LookupSource(context.get(), sources[i])) - SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]); - } +AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - ALCdevice *device{context->Device}; - ALCdevice_Lock(device); - for(ALsizei i{0};i < n;i++) + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(Int64ValsByProp(param) != 3) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param); + else { - ALsource *source{LookupSource(context.get(), sources[i])}; - ALvoice *voice{GetSourceVoice(source, context.get())}; - if(voice != nullptr) - { - voice->Source.store(nullptr, std::memory_order_relaxed); - voice->Playing.store(false, std::memory_order_release); - voice = nullptr; - } - ALenum oldstate{GetSourceState(source, voice)}; - if(oldstate != AL_INITIAL && oldstate != AL_STOPPED) - { - source->state = AL_STOPPED; - SendStateChangeEvent(context.get(), source->id, AL_STOPPED); - } - source->OffsetType = AL_NONE; - source->Offset = 0.0; + ALint64SOFT i64vals[3] = { value1, value2, value3 }; + SetSourcei64v(Source, context.get(), static_cast(param), i64vals); } - ALCdevice_Unlock(device); } -AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source) -{ - alSourceRewindv(1, &source); -} -AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) +AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - if(n < 0) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Rewinding %d sources", n); - if(n == 0) return; - - for(ALsizei i{0};i < n;i++) - { - if(!LookupSource(context.get(), sources[i])) - SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]); - } - - ALCdevice *device{context->Device}; - ALCdevice_Lock(device); - for(ALsizei i{0};i < n;i++) - { - ALsource *source{LookupSource(context.get(), sources[i])}; - ALvoice *voice{GetSourceVoice(source, context.get())}; - if(voice != nullptr) - { - voice->Source.store(nullptr, std::memory_order_relaxed); - voice->Playing.store(false, std::memory_order_release); - voice = nullptr; - } - if(GetSourceState(source, voice) != AL_INITIAL) - { - source->state = AL_INITIAL; - SendStateChangeEvent(context.get(), source->id, AL_INITIAL); - } - source->OffsetType = AL_NONE; - source->Offset = 0.0; - } - ALCdevice_Unlock(device); + std::lock_guard _{context->PropLock}; + std::lock_guard __{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!values) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(Int64ValsByProp(param) < 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param); + else + SetSourcei64v(Source, context.get(), static_cast(param), values); } -AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers) +AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - if(nb < 0) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Queueing %d buffers", nb); - if(nb == 0) return; - std::lock_guard _{context->SourceLock}; - ALsource *source{LookupSource(context.get(),src)}; - if(!source) - SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src); - - if(source->SourceType == AL_STATIC) + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!value) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(FloatValsByProp(param) != 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid float property 0x%04x", param); + else { - /* Can't queue on a Static Source */ - SETERR_RETURN(context.get(), AL_INVALID_OPERATION,, "Queueing onto static source %u", src); + ALdouble dval; + if(GetSourcedv(Source, context.get(), static_cast(param), &dval)) + *value = (ALfloat)dval; } +} - /* Check for a valid Buffer, for its frequency and format */ - ALCdevice *device{context->Device}; - ALbuffer *BufferFmt{nullptr}; - ALbufferlistitem *BufferList{source->queue}; - while(BufferList) - { - for(ALsizei i{0};i < BufferList->num_buffers;i++) - { - if((BufferFmt=BufferList->buffers[i]) != nullptr) - break; - } - if(BufferFmt) break; - BufferList = BufferList->next.load(std::memory_order_relaxed); - } - std::unique_lock buflock{device->BufferLock}; - ALbufferlistitem *BufferListStart{nullptr}; - BufferList = nullptr; - for(ALsizei i{0};i < nb;i++) - { - ALbuffer *buffer{nullptr}; - if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == NULL) - SETERR_GOTO(context.get(), AL_INVALID_NAME, buffer_error, - "Queueing invalid buffer ID %u", buffers[i]); +AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - if(!BufferListStart) - { - BufferListStart = static_cast(al_calloc(DEF_ALIGN, - FAM_SIZE(ALbufferlistitem, buffers, 1))); - BufferList = BufferListStart; - } - else - { - auto item = static_cast(al_calloc(DEF_ALIGN, - FAM_SIZE(ALbufferlistitem, buffers, 1))); - BufferList->next.store(item, std::memory_order_relaxed); - BufferList = item; + std::lock_guard _{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!(value1 && value2 && value3)) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(FloatValsByProp(param) != 3) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param); + else + { + ALdouble dvals[3]; + if(GetSourcedv(Source, context.get(), static_cast(param), dvals)) + { + *value1 = (ALfloat)dvals[0]; + *value2 = (ALfloat)dvals[1]; + *value3 = (ALfloat)dvals[2]; } - ATOMIC_INIT(&BufferList->next, static_cast(nullptr)); - BufferList->max_samples = buffer ? buffer->SampleLen : 0; - BufferList->num_buffers = 1; - BufferList->buffers[0] = buffer; - if(!buffer) continue; + } +} - IncrementRef(&buffer->ref); - if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) - SETERR_GOTO(context.get(), AL_INVALID_OPERATION, buffer_error, - "Queueing non-persistently mapped buffer %u", buffer->id); +AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - if(BufferFmt == NULL) - BufferFmt = buffer; - else if(BufferFmt->Frequency != buffer->Frequency || - BufferFmt->FmtChannels != buffer->FmtChannels || - BufferFmt->OriginalType != buffer->OriginalType) + std::lock_guard _{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!values) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else + { + ALint count{FloatValsByProp(param)}; + if(count < 1 && count > 6) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param); + else { - alSetError(context.get(), AL_INVALID_OPERATION, - "Queueing buffer with mismatched format"); - - buffer_error: - /* A buffer failed (invalid ID or format), so unlock and release - * each buffer we had. */ - while(BufferListStart) + ALdouble dvals[6]; + if(GetSourcedv(Source, context.get(), static_cast(param), dvals)) { - ALbufferlistitem *next = BufferListStart->next.load(std::memory_order_relaxed); - for(i = 0;i < BufferListStart->num_buffers;i++) - { - if((buffer=BufferListStart->buffers[i]) != NULL) - DecrementRef(&buffer->ref); - } - al_free(BufferListStart); - BufferListStart = next; + for(ALint i{0};i < count;i++) + values[i] = (ALfloat)dvals[i]; } - return; } } - /* All buffers good. */ - buflock.unlock(); +} - /* Source is now streaming */ - source->SourceType = AL_STREAMING; - if(!(BufferList=source->queue)) - source->queue = BufferListStart; +AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + std::lock_guard _{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!value) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(DoubleValsByProp(param) != 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid double property 0x%04x", param); + else + GetSourcedv(Source, context.get(), static_cast(param), value); +} + +AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + std::lock_guard _{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!(value1 && value2 && value3)) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(DoubleValsByProp(param) != 3) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param); else { - ALbufferlistitem *next; - while((next=BufferList->next.load(std::memory_order_relaxed)) != nullptr) - BufferList = next; - BufferList->next.store(BufferListStart, std::memory_order_release); + ALdouble dvals[3]; + if(GetSourcedv(Source, context.get(), static_cast(param), dvals)) + { + *value1 = dvals[0]; + *value2 = dvals[1]; + *value3 = dvals[2]; + } } } -AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers) +AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - if(nb < 0) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Queueing %d buffer layers", nb); - if(nb == 0) return; + std::lock_guard _{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!values) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(DoubleValsByProp(param) < 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param); + else + GetSourcedv(Source, context.get(), static_cast(param), values); +} + + +AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; std::lock_guard _{context->SourceLock}; - ALsource *source{LookupSource(context.get(),src)}; - if(!source) - SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src); + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!value) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(IntValsByProp(param) != 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer property 0x%04x", param); + else + GetSourceiv(Source, context.get(), static_cast(param), value); +} - if(source->SourceType == AL_STATIC) - { - /* Can't queue on a Static Source */ - SETERR_RETURN(context.get(), AL_INVALID_OPERATION,, "Queueing onto static source %u", src); - } - /* Check for a valid Buffer, for its frequency and format */ - ALCdevice *device{context->Device}; - ALbuffer *BufferFmt{nullptr}; - ALbufferlistitem *BufferList{source->queue}; - while(BufferList) +AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + std::lock_guard _{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!(value1 && value2 && value3)) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(IntValsByProp(param) != 3) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param); + else { - for(ALsizei i{0};i < BufferList->num_buffers;i++) + ALint ivals[3]; + if(GetSourceiv(Source, context.get(), static_cast(param), ivals)) { - if((BufferFmt=BufferList->buffers[i]) != nullptr) - break; + *value1 = ivals[0]; + *value2 = ivals[1]; + *value3 = ivals[2]; } - if(BufferFmt) break; - BufferList = BufferList->next.load(std::memory_order_relaxed); } +} - std::unique_lock buflock{device->BufferLock}; - auto BufferListStart = static_cast(al_calloc(DEF_ALIGN, - FAM_SIZE(ALbufferlistitem, buffers, nb))); - BufferList = BufferListStart; - ATOMIC_INIT(&BufferList->next, static_cast(nullptr)); - BufferList->max_samples = 0; - BufferList->num_buffers = 0; - - for(ALsizei i{0};i < nb;i++) - { - ALbuffer *buffer{nullptr}; - if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr) - SETERR_GOTO(context.get(), AL_INVALID_NAME, buffer_error, - "Queueing invalid buffer ID %u", buffers[i]); - - BufferList->buffers[BufferList->num_buffers++] = buffer; - if(!buffer) continue; - IncrementRef(&buffer->ref); +AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - BufferList->max_samples = maxi(BufferList->max_samples, buffer->SampleLen); + std::lock_guard _{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!values) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(IntValsByProp(param) < 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param); + else + GetSourceiv(Source, context.get(), static_cast(param), values); +} - if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) - SETERR_GOTO(context.get(), AL_INVALID_OPERATION, buffer_error, - "Queueing non-persistently mapped buffer %u", buffer->id); - if(BufferFmt == NULL) - BufferFmt = buffer; - else if(BufferFmt->Frequency != buffer->Frequency || - BufferFmt->FmtChannels != buffer->FmtChannels || - BufferFmt->OriginalType != buffer->OriginalType) - { - alSetError(context.get(), AL_INVALID_OPERATION, - "Queueing buffer with mismatched format"); +AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - buffer_error: - /* A buffer failed (invalid ID or format), so unlock and release - * each buffer we had. */ - while(BufferListStart) - { - ALbufferlistitem *next = ATOMIC_LOAD(&BufferListStart->next, - almemory_order_relaxed); - for(i = 0;i < BufferListStart->num_buffers;i++) - { - if((buffer=BufferListStart->buffers[i]) != NULL) - DecrementRef(&buffer->ref); - } - al_free(BufferListStart); - BufferListStart = next; - } - return; - } - } - /* All buffers good. */ - buflock.unlock(); + std::lock_guard _{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!value) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(Int64ValsByProp(param) != 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param); + else + GetSourcei64v(Source, context.get(), static_cast(param), value); +} - /* Source is now streaming */ - source->SourceType = AL_STREAMING; +AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - if(!(BufferList=source->queue)) - source->queue = BufferListStart; + std::lock_guard _{context->SourceLock}; + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!(value1 && value2 && value3)) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(Int64ValsByProp(param) != 3) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param); else { - ALbufferlistitem *next; - while((next=ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed)) != NULL) - BufferList = next; - ATOMIC_STORE(&BufferList->next, BufferListStart, almemory_order_release); + ALint64 i64vals[3]; + if(GetSourcei64v(Source, context.get(), static_cast(param), i64vals)) + { + *value1 = i64vals[0]; + *value2 = i64vals[1]; + *value3 = i64vals[2]; + } } } -AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers) +AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values) { ContextRef context{GetContextRef()}; if(UNLIKELY(!context)) return; - if(nb < 0) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing %d buffers", nb); - if(nb == 0) return; - std::lock_guard _{context->SourceLock}; - ALsource *source{LookupSource(context.get(),src)}; - if(!source) - SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src); + ALsource *Source{LookupSource(context.get(), source)}; + if(UNLIKELY(!Source)) + alSetError(context.get(), AL_INVALID_NAME, "Invalid source ID %u", source); + else if(!values) + alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer"); + else if(Int64ValsByProp(param) < 1) + alSetError(context.get(), AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param); + else + GetSourcei64v(Source, context.get(), static_cast(param), values); +} - if(source->Looping) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing from looping source %u", src); - if(source->SourceType != AL_STREAMING) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, - "Unqueueing from a non-streaming source %u", src); - /* Make sure enough buffers have been processed to unqueue. */ - ALbufferlistitem *BufferList{source->queue}; - ALvoice *voice{GetSourceVoice(source, context.get())}; - ALbufferlistitem *Current{nullptr}; - if(voice) - Current = voice->current_buffer.load(std::memory_order_relaxed); - else if(source->state == AL_INITIAL) - Current = BufferList; - if(BufferList == Current) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing pending buffers"); +AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source) +{ + alSourcePlayv(1, &source); +} +AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - ALsizei i{BufferList->num_buffers}; - while(i < nb) + if(n < 0) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Playing %d sources", n); + if(n == 0) return; + + std::lock_guard _{context->SourceLock}; + for(ALsizei i{0};i < n;i++) { - /* If the next bufferlist to check is NULL or is the current one, it's - * trying to unqueue pending buffers. - */ - ALbufferlistitem *next = BufferList->next.load(std::memory_order_relaxed); - if(!next || next == Current) - SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing pending buffers"); - BufferList = next; + if(!LookupSource(context.get(), sources[i])) + SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]); + } - i += BufferList->num_buffers; + ALCdevice *device{context->Device}; + ALCdevice_Lock(device); + /* If the device is disconnected, go right to stopped. */ + if(!device->Connected.load(std::memory_order_acquire)) + { + /* TODO: Send state change event? */ + for(ALsizei i{0};i < n;i++) + { + ALsource *source{LookupSource(context.get(), sources[i])}; + source->OffsetType = AL_NONE; + source->Offset = 0.0; + source->state = AL_STOPPED; + } + ALCdevice_Unlock(device); + return; } - while(nb > 0) + while(n > context->MaxVoices-context->VoiceCount) { - ALbufferlistitem *head = source->queue; - ALbufferlistitem *next = head->next.load(std::memory_order_relaxed); - for(i = 0;i < head->num_buffers && nb > 0;i++,nb--) + ALsizei newcount = context->MaxVoices << 1; + if(context->MaxVoices >= newcount) { - ALbuffer *buffer = head->buffers[i]; - if(!buffer) - *(buffers++) = 0; - else - { - *(buffers++) = buffer->id; - DecrementRef(&buffer->ref); - } + ALCdevice_Unlock(device); + SETERR_RETURN(context.get(), AL_OUT_OF_MEMORY,, + "Overflow increasing voice count %d -> %d", context->MaxVoices, newcount); } - if(i < head->num_buffers) + AllocateVoices(context.get(), newcount, device->NumAuxSends); + } + + for(ALsizei i{0};i < n;i++) + { + ALsource *source{LookupSource(context.get(), sources[i])}; + /* Check that there is a queue containing at least one valid, non zero + * length buffer. + */ + ALbufferlistitem *BufferList{source->queue}; + while(BufferList && BufferList->max_samples == 0) + BufferList = BufferList->next.load(std::memory_order_relaxed); + + /* If there's nothing to play, go right to stopped. */ + if(UNLIKELY(!BufferList)) { - /* This head has some buffers left over, so move them to the front - * and update the sample and buffer count. + /* NOTE: A source without any playable buffers should not have an + * ALvoice since it shouldn't be in a playing or paused state. So + * there's no need to look up its voice and clear the source. */ - ALsizei max_length = 0; - ALsizei j = 0; - while(i < head->num_buffers) + ALenum oldstate{GetSourceState(source, nullptr)}; + source->OffsetType = AL_NONE; + source->Offset = 0.0; + if(oldstate != AL_STOPPED) { - ALbuffer *buffer = head->buffers[i++]; - if(buffer) max_length = maxi(max_length, buffer->SampleLen); - head->buffers[j++] = buffer; + source->state = AL_STOPPED; + SendStateChangeEvent(context.get(), source->id, AL_STOPPED); } - head->max_samples = max_length; - head->num_buffers = j; - break; + continue; } - /* Otherwise, free this item and set the source queue head to the next - * one. - */ - al_free(head); - source->queue = next; - } -} + ALvoice *voice{GetSourceVoice(source, context.get())}; + switch(GetSourceState(source, voice)) + { + case AL_PLAYING: + assert(voice != nullptr); + /* A source that's already playing is restarted from the beginning. */ + voice->current_buffer.store(BufferList, std::memory_order_relaxed); + voice->position.store(0u, std::memory_order_relaxed); + voice->position_fraction.store(0, std::memory_order_release); + continue; + case AL_PAUSED: + assert(voice != nullptr); + /* A source that's paused simply resumes. */ + voice->Playing.store(true, std::memory_order_release); + source->state = AL_PLAYING; + SendStateChangeEvent(context.get(), source->id, AL_PLAYING); + continue; -ALsource::ALsource(ALsizei num_sends) -{ - InnerAngle = 360.0f; - OuterAngle = 360.0f; - Pitch = 1.0f; - Position[0] = 0.0f; - Position[1] = 0.0f; - Position[2] = 0.0f; - Velocity[0] = 0.0f; - Velocity[1] = 0.0f; - Velocity[2] = 0.0f; - Direction[0] = 0.0f; - Direction[1] = 0.0f; - Direction[2] = 0.0f; - Orientation[0][0] = 0.0f; - Orientation[0][1] = 0.0f; - Orientation[0][2] = -1.0f; - Orientation[1][0] = 0.0f; - Orientation[1][1] = 1.0f; - Orientation[1][2] = 0.0f; - RefDistance = 1.0f; - MaxDistance = FLT_MAX; - RolloffFactor = 1.0f; - Gain = 1.0f; - MinGain = 0.0f; - MaxGain = 1.0f; - OuterGain = 0.0f; - OuterGainHF = 1.0f; + default: + break; + } - DryGainHFAuto = AL_TRUE; - WetGainAuto = AL_TRUE; - WetGainHFAuto = AL_TRUE; - AirAbsorptionFactor = 0.0f; - RoomRolloffFactor = 0.0f; - DopplerFactor = 1.0f; - HeadRelative = AL_FALSE; - Looping = AL_FALSE; - mDistanceModel = DistanceModel::Default; - Resampler = ResamplerDefault; - DirectChannels = AL_FALSE; - Spatialize = SpatializeAuto; + /* Look for an unused voice to play this source with. */ + assert(voice == nullptr); + ALint vidx{-1}; + for(ALsizei j{0};j < context->VoiceCount;j++) + { + if(context->Voices[j]->Source.load(std::memory_order_acquire) == nullptr) + { + vidx = j; + break; + } + } + if(vidx == -1) + vidx = context->VoiceCount++; + voice = context->Voices[vidx]; + voice->Playing.store(false, std::memory_order_release); - StereoPan[0] = DEG2RAD( 30.0f); - StereoPan[1] = DEG2RAD(-30.0f); + source->PropsClean.test_and_set(std::memory_order_acquire); + UpdateSourceProps(source, voice, context.get()); - Radius = 0.0f; + /* A source that's not playing or paused has any offset applied when it + * starts playing. + */ + if(source->Looping) + voice->loop_buffer.store(source->queue, std::memory_order_relaxed); + else + voice->loop_buffer.store(nullptr, std::memory_order_relaxed); + voice->current_buffer.store(BufferList, std::memory_order_relaxed); + voice->position.store(0u, std::memory_order_relaxed); + voice->position_fraction.store(0, std::memory_order_relaxed); + bool start_fading{false}; + if(ApplyOffset(source, voice) != AL_FALSE) + start_fading = voice->position.load(std::memory_order_relaxed) != 0 || + voice->position_fraction.load(std::memory_order_relaxed) != 0 || + voice->current_buffer.load(std::memory_order_relaxed) != BufferList; - Direct.Gain = 1.0f; - Direct.GainHF = 1.0f; - Direct.HFReference = LOWPASSFREQREF; - Direct.GainLF = 1.0f; - Direct.LFReference = HIGHPASSFREQREF; - Send.resize(num_sends); - for(auto &send : Send) - { - send.Slot = nullptr; - send.Gain = 1.0f; - send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; - send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; - } + for(ALsizei j{0};j < BufferList->num_buffers;j++) + { + ALbuffer *buffer = BufferList->buffers[j]; + if(buffer) + { + voice->NumChannels = ChannelsFromFmt(buffer->FmtChannels); + voice->SampleSize = BytesFromFmt(buffer->FmtType); + break; + } + } - Offset = 0.0; - OffsetType = AL_NONE; - SourceType = AL_UNDETERMINED; - state = AL_INITIAL; + /* Clear previous samples. */ + memset(voice->PrevSamples, 0, sizeof(voice->PrevSamples)); + + /* Clear the stepping value so the mixer knows not to mix this until + * the update gets applied. + */ + voice->Step = 0; - queue = NULL; + voice->Flags = start_fading ? VOICE_IS_FADING : 0; + if(source->SourceType == AL_STATIC) voice->Flags |= VOICE_IS_STATIC; + memset(voice->Direct.Params, 0, sizeof(voice->Direct.Params[0])*voice->NumChannels); + for(ALsizei j{0};j < device->NumAuxSends;j++) + memset(voice->Send[j].Params, 0, sizeof(voice->Send[j].Params[0])*voice->NumChannels); + if(device->AvgSpeakerDist > 0.0f) + { + ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC / + (device->AvgSpeakerDist * device->Frequency); + for(ALsizei j{0};j < voice->NumChannels;j++) + NfcFilterCreate(&voice->Direct.Params[j].NFCtrlFilter, 0.0f, w1); + } - VoiceIdx = -1; + voice->Source.store(source, std::memory_order_relaxed); + voice->Playing.store(true, std::memory_order_release); + source->state = AL_PLAYING; + source->VoiceIdx = vidx; + + SendStateChangeEvent(context.get(), source->id, AL_PLAYING); + } + ALCdevice_Unlock(device); } -ALsource::~ALsource() +AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source) { - ALbufferlistitem *BufferList{queue}; - while(BufferList != NULL) + alSourcePausev(1, &source); +} +AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + if(n < 0) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Pausing %d sources", n); + if(n == 0) return; + + for(ALsizei i{0};i < n;i++) { - ALbufferlistitem *next = BufferList->next.load(std::memory_order_relaxed); - for(ALsizei i{0};i < BufferList->num_buffers;i++) - { - if(BufferList->buffers[i]) - DecrementRef(&BufferList->buffers[i]->ref); - } - al_free(BufferList); - BufferList = next; + if(!LookupSource(context.get(), sources[i])) + SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]); } - queue = nullptr; - std::for_each(Send.begin(), Send.end(), - [](ALsource::SendData &send) -> void + ALCdevice *device{context->Device}; + ALCdevice_Lock(device); + for(ALsizei i{0};i < n;i++) + { + ALsource *source{LookupSource(context.get(), sources[i])}; + ALvoice *voice{GetSourceVoice(source, context.get())}; + if(!voice) voice->Playing.store(false, std::memory_order_release); + if(GetSourceState(source, voice) == AL_PLAYING) { - if(send.Slot) - DecrementRef(&send.Slot->ref); - send.Slot = nullptr; + source->state = AL_PAUSED; + SendStateChangeEvent(context.get(), source->id, AL_PAUSED); } - ); + } + ALCdevice_Unlock(device); } -static void UpdateSourceProps(ALsource *source, ALvoice *voice, ALCcontext *context) +AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source) { - /* Get an unused property container, or allocate a new one as needed. */ - ALvoiceProps *props{context->FreeVoiceProps.load(std::memory_order_acquire)}; - if(!props) - props = static_cast(al_calloc(16, - FAM_SIZE(struct ALvoiceProps, Send, source->Send.size()))); - else + alSourceStopv(1, &source); +} +AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + if(n < 0) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Stopping %d sources", n); + if(n == 0) return; + + for(ALsizei i{0};i < n;i++) { - struct ALvoiceProps *next; - do { - next = props->next.load(std::memory_order_relaxed); - } while(context->FreeVoiceProps.compare_exchange_weak(props, next, - std::memory_order_acq_rel, std::memory_order_acquire) == 0); + if(!LookupSource(context.get(), sources[i])) + SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]); } - /* Copy in current property values. */ - props->Pitch = source->Pitch; - props->Gain = source->Gain; - props->OuterGain = source->OuterGain; - props->MinGain = source->MinGain; - props->MaxGain = source->MaxGain; - props->InnerAngle = source->InnerAngle; - props->OuterAngle = source->OuterAngle; - props->RefDistance = source->RefDistance; - props->MaxDistance = source->MaxDistance; - props->RolloffFactor = source->RolloffFactor; - for(ALsizei i{0};i < 3;i++) - props->Position[i] = source->Position[i]; - for(ALsizei i{0};i < 3;i++) - props->Velocity[i] = source->Velocity[i]; - for(ALsizei i{0};i < 3;i++) - props->Direction[i] = source->Direction[i]; - for(ALsizei i{0};i < 2;i++) + ALCdevice *device{context->Device}; + ALCdevice_Lock(device); + for(ALsizei i{0};i < n;i++) { - for(ALsizei j{0};j < 3;j++) - props->Orientation[i][j] = source->Orientation[i][j]; + ALsource *source{LookupSource(context.get(), sources[i])}; + ALvoice *voice{GetSourceVoice(source, context.get())}; + if(voice != nullptr) + { + voice->Source.store(nullptr, std::memory_order_relaxed); + voice->Playing.store(false, std::memory_order_release); + voice = nullptr; + } + ALenum oldstate{GetSourceState(source, voice)}; + if(oldstate != AL_INITIAL && oldstate != AL_STOPPED) + { + source->state = AL_STOPPED; + SendStateChangeEvent(context.get(), source->id, AL_STOPPED); + } + source->OffsetType = AL_NONE; + source->Offset = 0.0; } - props->HeadRelative = source->HeadRelative; - props->mDistanceModel = source->mDistanceModel; - props->Resampler = source->Resampler; - props->DirectChannels = source->DirectChannels; - props->SpatializeMode = source->Spatialize; - - props->DryGainHFAuto = source->DryGainHFAuto; - props->WetGainAuto = source->WetGainAuto; - props->WetGainHFAuto = source->WetGainHFAuto; - props->OuterGainHF = source->OuterGainHF; - - props->AirAbsorptionFactor = source->AirAbsorptionFactor; - props->RoomRolloffFactor = source->RoomRolloffFactor; - props->DopplerFactor = source->DopplerFactor; - - props->StereoPan[0] = source->StereoPan[0]; - props->StereoPan[1] = source->StereoPan[1]; + ALCdevice_Unlock(device); +} - props->Radius = source->Radius; +AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source) +{ + alSourceRewindv(1, &source); +} +AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - props->Direct.Gain = source->Direct.Gain; - props->Direct.GainHF = source->Direct.GainHF; - props->Direct.HFReference = source->Direct.HFReference; - props->Direct.GainLF = source->Direct.GainLF; - props->Direct.LFReference = source->Direct.LFReference; + if(n < 0) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Rewinding %d sources", n); + if(n == 0) return; - for(size_t i{0u};i < source->Send.size();i++) + for(ALsizei i{0};i < n;i++) { - props->Send[i].Slot = source->Send[i].Slot; - props->Send[i].Gain = source->Send[i].Gain; - props->Send[i].GainHF = source->Send[i].GainHF; - props->Send[i].HFReference = source->Send[i].HFReference; - props->Send[i].GainLF = source->Send[i].GainLF; - props->Send[i].LFReference = source->Send[i].LFReference; + if(!LookupSource(context.get(), sources[i])) + SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", sources[i]); } - /* Set the new container for updating internal parameters. */ - props = voice->Update.exchange(props, std::memory_order_acq_rel); - if(props) + ALCdevice *device{context->Device}; + ALCdevice_Lock(device); + for(ALsizei i{0};i < n;i++) { - /* If there was an unused update container, put it back in the - * freelist. - */ - AtomicReplaceHead(context->FreeVoiceProps, props); + ALsource *source{LookupSource(context.get(), sources[i])}; + ALvoice *voice{GetSourceVoice(source, context.get())}; + if(voice != nullptr) + { + voice->Source.store(nullptr, std::memory_order_relaxed); + voice->Playing.store(false, std::memory_order_release); + voice = nullptr; + } + if(GetSourceState(source, voice) != AL_INITIAL) + { + source->state = AL_INITIAL; + SendStateChangeEvent(context.get(), source->id, AL_INITIAL); + } + source->OffsetType = AL_NONE; + source->Offset = 0.0; } + ALCdevice_Unlock(device); } -void UpdateAllSourceProps(ALCcontext *context) + +AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers) { - for(ALsizei i{0};i < context->VoiceCount;++i) + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + if(nb < 0) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Queueing %d buffers", nb); + if(nb == 0) return; + + std::lock_guard _{context->SourceLock}; + ALsource *source{LookupSource(context.get(),src)}; + if(!source) + SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src); + + /* Can't queue on a Static Source */ + if(source->SourceType == AL_STATIC) + SETERR_RETURN(context.get(), AL_INVALID_OPERATION,, "Queueing onto static source %u", src); + + /* Check for a valid Buffer, for its frequency and format */ + ALCdevice *device{context->Device}; + ALbuffer *BufferFmt{nullptr}; + ALbufferlistitem *BufferList{source->queue}; + while(BufferList) { - ALvoice *voice = context->Voices[i]; - ALsource *source = voice->Source.load(std::memory_order_acquire); - if(source && !source->PropsClean.test_and_set(std::memory_order_acq_rel)) - UpdateSourceProps(source, voice, context); + for(ALsizei i{0};i < BufferList->num_buffers;i++) + { + if((BufferFmt=BufferList->buffers[i]) != nullptr) + break; + } + if(BufferFmt) break; + BufferList = BufferList->next.load(std::memory_order_relaxed); } -} + std::unique_lock buflock{device->BufferLock}; + ALbufferlistitem *BufferListStart{nullptr}; + BufferList = nullptr; + for(ALsizei i{0};i < nb;i++) + { + ALbuffer *buffer{nullptr}; + if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr) + { + alSetError(context.get(), AL_INVALID_NAME, "Queueing invalid buffer ID %u", + buffers[i]); + goto buffer_error; + } -/* GetSourceSampleOffset - * - * Gets the current read offset for the given Source, in 32.32 fixed-point - * samples. The offset is relative to the start of the queue (not the start of - * the current buffer). - */ -static ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime) -{ - ALCdevice *device = context->Device; - const ALbufferlistitem *Current; - ALuint64 readPos; - ALuint refcount; - ALvoice *voice; + if(!BufferListStart) + { + BufferListStart = static_cast(al_calloc(DEF_ALIGN, + FAM_SIZE(ALbufferlistitem, buffers, 1))); + BufferList = BufferListStart; + } + else + { + auto item = static_cast(al_calloc(DEF_ALIGN, + FAM_SIZE(ALbufferlistitem, buffers, 1))); + BufferList->next.store(item, std::memory_order_relaxed); + BufferList = item; + } + ATOMIC_INIT(&BufferList->next, static_cast(nullptr)); + BufferList->max_samples = buffer ? buffer->SampleLen : 0; + BufferList->num_buffers = 1; + BufferList->buffers[0] = buffer; + if(!buffer) continue; - do { - Current = NULL; - readPos = 0; - while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) - althrd_yield(); - *clocktime = GetDeviceClockTime(device); + IncrementRef(&buffer->ref); - voice = GetSourceVoice(Source, context); - if(voice) + if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) { - Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); - - readPos = (ALuint64)ATOMIC_LOAD(&voice->position, almemory_order_relaxed) << 32; - readPos |= (ALuint64)ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed) << - (32-FRACTIONBITS); + alSetError(context.get(), AL_INVALID_OPERATION, + "Queueing non-persistently mapped buffer %u", buffer->id); + goto buffer_error; } - std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed)); - if(voice) - { - const ALbufferlistitem *BufferList = Source->queue; - while(BufferList && BufferList != Current) + if(BufferFmt == nullptr) + BufferFmt = buffer; + else if(BufferFmt->Frequency != buffer->Frequency || + BufferFmt->FmtChannels != buffer->FmtChannels || + BufferFmt->OriginalType != buffer->OriginalType) { - readPos += (ALuint64)BufferList->max_samples << 32; - BufferList = BufferList->next.load(std::memory_order_relaxed); + alSetError(context.get(), AL_INVALID_OPERATION, + "Queueing buffer with mismatched format"); + + buffer_error: + /* A buffer failed (invalid ID or format), so unlock and release + * each buffer we had. */ + while(BufferListStart) + { + ALbufferlistitem *next = BufferListStart->next.load(std::memory_order_relaxed); + for(i = 0;i < BufferListStart->num_buffers;i++) + { + if((buffer=BufferListStart->buffers[i]) != nullptr) + DecrementRef(&buffer->ref); + } + al_free(BufferListStart); + BufferListStart = next; + } + return; } - readPos = minu64(readPos, U64(0x7fffffffffffffff)); } + /* All buffers good. */ + buflock.unlock(); - return (ALint64)readPos; + /* Source is now streaming */ + source->SourceType = AL_STREAMING; + + if(!(BufferList=source->queue)) + source->queue = BufferListStart; + else + { + ALbufferlistitem *next; + while((next=BufferList->next.load(std::memory_order_relaxed)) != nullptr) + BufferList = next; + BufferList->next.store(BufferListStart, std::memory_order_release); + } } -/* GetSourceSecOffset - * - * Gets the current read offset for the given Source, in seconds. The offset is - * relative to the start of the queue (not the start of the current buffer). - */ -static ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime) +AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers) { - ALCdevice *device = context->Device; - const ALbufferlistitem *Current; - ALuint64 readPos; - ALuint refcount; - ALdouble offset; - ALvoice *voice; + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; - do { - Current = NULL; - readPos = 0; - while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) - althrd_yield(); - *clocktime = GetDeviceClockTime(device); + if(nb < 0) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Queueing %d buffer layers", nb); + if(nb == 0) return; - voice = GetSourceVoice(Source, context); - if(voice) - { - Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); + std::lock_guard _{context->SourceLock}; + ALsource *source{LookupSource(context.get(),src)}; + if(!source) + SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src); - readPos = (ALuint64)ATOMIC_LOAD(&voice->position, almemory_order_relaxed) << - FRACTIONBITS; - readPos |= ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed); - } - std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed)); + /* Can't queue on a Static Source */ + if(source->SourceType == AL_STATIC) + SETERR_RETURN(context.get(), AL_INVALID_OPERATION,, "Queueing onto static source %u", src); - offset = 0.0; - if(voice) + /* Check for a valid Buffer, for its frequency and format */ + ALCdevice *device{context->Device}; + ALbuffer *BufferFmt{nullptr}; + ALbufferlistitem *BufferList{source->queue}; + while(BufferList) { - const ALbufferlistitem *BufferList = Source->queue; - const ALbuffer *BufferFmt = NULL; - while(BufferList && BufferList != Current) - { - ALsizei i = 0; - while(!BufferFmt && i < BufferList->num_buffers) - BufferFmt = BufferList->buffers[i++]; - readPos += (ALuint64)BufferList->max_samples << FRACTIONBITS; - BufferList = BufferList->next.load(std::memory_order_relaxed); - } - - while(BufferList && !BufferFmt) + for(ALsizei i{0};i < BufferList->num_buffers;i++) { - ALsizei i = 0; - while(!BufferFmt && i < BufferList->num_buffers) - BufferFmt = BufferList->buffers[i++]; - BufferList = BufferList->next.load(std::memory_order_relaxed); + if((BufferFmt=BufferList->buffers[i]) != nullptr) + break; } - assert(BufferFmt != NULL); - - offset = (ALdouble)readPos / (ALdouble)FRACTIONONE / - (ALdouble)BufferFmt->Frequency; + if(BufferFmt) break; + BufferList = BufferList->next.load(std::memory_order_relaxed); } - return offset; -} - -/* GetSourceOffset - * - * Gets the current read offset for the given Source, in the appropriate format - * (Bytes, Samples or Seconds). The offset is relative to the start of the - * queue (not the start of the current buffer). - */ -static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) -{ - ALCdevice *device = context->Device; - const ALbufferlistitem *Current; - ALuint readPos; - ALsizei readPosFrac; - ALuint refcount; - ALdouble offset; - ALvoice *voice; + std::unique_lock buflock{device->BufferLock}; + auto BufferListStart = static_cast(al_calloc(DEF_ALIGN, + FAM_SIZE(ALbufferlistitem, buffers, nb))); + BufferList = BufferListStart; + ATOMIC_INIT(&BufferList->next, static_cast(nullptr)); + BufferList->max_samples = 0; + BufferList->num_buffers = 0; - do { - Current = NULL; - readPos = readPosFrac = 0; - while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) - althrd_yield(); - voice = GetSourceVoice(Source, context); - if(voice) + for(ALsizei i{0};i < nb;i++) + { + ALbuffer *buffer{nullptr}; + if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr) { - Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); - - readPos = ATOMIC_LOAD(&voice->position, almemory_order_relaxed); - readPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed); + alSetError(context.get(), AL_INVALID_NAME, "Queueing invalid buffer ID %u", + buffers[i]); + goto buffer_error; } - std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed)); - - offset = 0.0; - if(voice) - { - const ALbufferlistitem *BufferList = Source->queue; - const ALbuffer *BufferFmt = NULL; - ALboolean readFin = AL_FALSE; - ALuint totalBufferLen = 0; - while(BufferList != NULL) - { - ALsizei i = 0; - while(!BufferFmt && i < BufferList->num_buffers) - BufferFmt = BufferList->buffers[i++]; + BufferList->buffers[BufferList->num_buffers++] = buffer; + if(!buffer) continue; - readFin |= (BufferList == Current); - totalBufferLen += BufferList->max_samples; - if(!readFin) readPos += BufferList->max_samples; + IncrementRef(&buffer->ref); - BufferList = BufferList->next.load(std::memory_order_relaxed); - } - assert(BufferFmt != NULL); + BufferList->max_samples = maxi(BufferList->max_samples, buffer->SampleLen); - if(Source->Looping) - readPos %= totalBufferLen; - else + if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) { - /* Wrap back to 0 */ - if(readPos >= totalBufferLen) - readPos = readPosFrac = 0; + alSetError(context.get(), AL_INVALID_OPERATION, + "Queueing non-persistently mapped buffer %u", buffer->id); + goto buffer_error; } - offset = 0.0; - switch(name) + if(BufferFmt == nullptr) + BufferFmt = buffer; + else if(BufferFmt->Frequency != buffer->Frequency || + BufferFmt->FmtChannels != buffer->FmtChannels || + BufferFmt->OriginalType != buffer->OriginalType) { - case AL_SEC_OFFSET: - offset = (readPos + (ALdouble)readPosFrac/FRACTIONONE) / BufferFmt->Frequency; - break; - - case AL_SAMPLE_OFFSET: - offset = readPos + (ALdouble)readPosFrac/FRACTIONONE; - break; - - case AL_BYTE_OFFSET: - if(BufferFmt->OriginalType == UserFmtIMA4) - { - ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4; - ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels); - ALuint FrameBlockSize = BufferFmt->OriginalAlign; - - /* Round down to nearest ADPCM block */ - offset = (ALdouble)(readPos / FrameBlockSize * BlockSize); - } - else if(BufferFmt->OriginalType == UserFmtMSADPCM) - { - ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7; - ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels); - ALuint FrameBlockSize = BufferFmt->OriginalAlign; + alSetError(context.get(), AL_INVALID_OPERATION, + "Queueing buffer with mismatched format"); - /* Round down to nearest ADPCM block */ - offset = (ALdouble)(readPos / FrameBlockSize * BlockSize); - } - else + buffer_error: + /* A buffer failed (invalid ID or format), so unlock and release + * each buffer we had. */ + while(BufferListStart) + { + ALbufferlistitem *next{BufferListStart->next.load(std::memory_order_relaxed)}; + for(i = 0;i < BufferListStart->num_buffers;i++) { - ALuint FrameSize = FrameSizeFromFmt(BufferFmt->FmtChannels, - BufferFmt->FmtType); - offset = (ALdouble)(readPos * FrameSize); + if((buffer=BufferListStart->buffers[i]) != nullptr) + DecrementRef(&buffer->ref); } - break; + al_free(BufferListStart); + BufferListStart = next; + } + return; } } + /* All buffers good. */ + buflock.unlock(); - return offset; + /* Source is now streaming */ + source->SourceType = AL_STREAMING; + + if(!(BufferList=source->queue)) + source->queue = BufferListStart; + else + { + ALbufferlistitem *next; + while((next=BufferList->next.load(std::memory_order_relaxed)) != nullptr) + BufferList = next; + BufferList->next.store(BufferListStart, std::memory_order_release); + } } +AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers) +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + if(nb < 0) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing %d buffers", nb); + if(nb == 0) return; + + std::lock_guard _{context->SourceLock}; + ALsource *source{LookupSource(context.get(),src)}; + if(!source) + SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid source ID %u", src); + + if(source->Looping) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing from looping source %u", src); + if(source->SourceType != AL_STREAMING) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, + "Unqueueing from a non-streaming source %u", src); + + /* Make sure enough buffers have been processed to unqueue. */ + ALbufferlistitem *BufferList{source->queue}; + ALvoice *voice{GetSourceVoice(source, context.get())}; + ALbufferlistitem *Current{nullptr}; + if(voice) + Current = voice->current_buffer.load(std::memory_order_relaxed); + else if(source->state == AL_INITIAL) + Current = BufferList; + if(BufferList == Current) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing pending buffers"); -/* ApplyOffset - * - * Apply the stored playback offset to the Source. This function will update - * the number of buffers "played" given the stored offset. - */ -static ALboolean ApplyOffset(ALsource *Source, ALvoice *voice) -{ - ALbufferlistitem *BufferList; - ALuint totalBufferLen; - ALuint offset = 0; - ALsizei frac = 0; + ALsizei i{BufferList->num_buffers}; + while(i < nb) + { + /* If the next bufferlist to check is NULL or is the current one, it's + * trying to unqueue pending buffers. + */ + ALbufferlistitem *next{BufferList->next.load(std::memory_order_relaxed)}; + if(!next || next == Current) + SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Unqueueing pending buffers"); + BufferList = next; - /* Get sample frame offset */ - if(!GetSampleOffset(Source, &offset, &frac)) - return AL_FALSE; + i += BufferList->num_buffers; + } - totalBufferLen = 0; - BufferList = Source->queue; - while(BufferList && totalBufferLen <= offset) + while(nb > 0) { - if((ALuint)BufferList->max_samples > offset-totalBufferLen) + ALbufferlistitem *head{source->queue}; + ALbufferlistitem *next{head->next.load(std::memory_order_relaxed)}; + for(i = 0;i < head->num_buffers && nb > 0;i++,nb--) { - /* Offset is in this buffer */ - ATOMIC_STORE(&voice->position, offset - totalBufferLen, almemory_order_relaxed); - ATOMIC_STORE(&voice->position_fraction, frac, almemory_order_relaxed); - ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_release); - return AL_TRUE; + ALbuffer *buffer{head->buffers[i]}; + if(!buffer) + *(buffers++) = 0; + else + { + *(buffers++) = buffer->id; + DecrementRef(&buffer->ref); + } + } + if(i < head->num_buffers) + { + /* This head has some buffers left over, so move them to the front + * and update the sample and buffer count. + */ + ALsizei max_length{0}; + ALsizei j{0}; + while(i < head->num_buffers) + { + ALbuffer *buffer{head->buffers[i++]}; + if(buffer) max_length = maxi(max_length, buffer->SampleLen); + head->buffers[j++] = buffer; + } + head->max_samples = max_length; + head->num_buffers = j; + break; } - totalBufferLen += BufferList->max_samples; - BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + /* Otherwise, free this item and set the source queue head to the next + * one. + */ + al_free(head); + source->queue = next; } - - /* Offset is out of range of the queue */ - return AL_FALSE; } -/* GetSampleOffset - * - * Retrieves the sample offset into the Source's queue (from the Sample, Byte - * or Second offset supplied by the application). This takes into account the - * fact that the buffer format may have been modifed since. - */ -static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac) +ALsource::ALsource(ALsizei num_sends) { - const ALbuffer *BufferFmt = NULL; - const ALbufferlistitem *BufferList; - ALdouble dbloff, dblfrac; + InnerAngle = 360.0f; + OuterAngle = 360.0f; + Pitch = 1.0f; + Position[0] = 0.0f; + Position[1] = 0.0f; + Position[2] = 0.0f; + Velocity[0] = 0.0f; + Velocity[1] = 0.0f; + Velocity[2] = 0.0f; + Direction[0] = 0.0f; + Direction[1] = 0.0f; + Direction[2] = 0.0f; + Orientation[0][0] = 0.0f; + Orientation[0][1] = 0.0f; + Orientation[0][2] = -1.0f; + Orientation[1][0] = 0.0f; + Orientation[1][1] = 1.0f; + Orientation[1][2] = 0.0f; + RefDistance = 1.0f; + MaxDistance = FLT_MAX; + RolloffFactor = 1.0f; + Gain = 1.0f; + MinGain = 0.0f; + MaxGain = 1.0f; + OuterGain = 0.0f; + OuterGainHF = 1.0f; - /* Find the first valid Buffer in the Queue */ - BufferList = Source->queue; - while(BufferList) - { - ALsizei i; - for(i = 0;i < BufferList->num_buffers && !BufferFmt;i++) - BufferFmt = BufferList->buffers[i]; - if(BufferFmt) break; - BufferList = BufferList->next.load(std::memory_order_relaxed); - } - if(!BufferFmt) - { - Source->OffsetType = AL_NONE; - Source->Offset = 0.0; - return AL_FALSE; - } + DryGainHFAuto = AL_TRUE; + WetGainAuto = AL_TRUE; + WetGainHFAuto = AL_TRUE; + AirAbsorptionFactor = 0.0f; + RoomRolloffFactor = 0.0f; + DopplerFactor = 1.0f; + HeadRelative = AL_FALSE; + Looping = AL_FALSE; + mDistanceModel = DistanceModel::Default; + Resampler = ResamplerDefault; + DirectChannels = AL_FALSE; + Spatialize = SpatializeAuto; - switch(Source->OffsetType) - { - case AL_BYTE_OFFSET: - /* Determine the ByteOffset (and ensure it is block aligned) */ - *offset = (ALuint)Source->Offset; - if(BufferFmt->OriginalType == UserFmtIMA4) - { - ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4; - *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels); - *offset *= BufferFmt->OriginalAlign; - } - else if(BufferFmt->OriginalType == UserFmtMSADPCM) - { - ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7; - *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels); - *offset *= BufferFmt->OriginalAlign; - } - else - *offset /= FrameSizeFromFmt(BufferFmt->FmtChannels, BufferFmt->FmtType); - *frac = 0; - break; + StereoPan[0] = DEG2RAD( 30.0f); + StereoPan[1] = DEG2RAD(-30.0f); - case AL_SAMPLE_OFFSET: - dblfrac = modf(Source->Offset, &dbloff); - *offset = (ALuint)mind(dbloff, UINT_MAX); - *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0); - break; + Radius = 0.0f; - case AL_SEC_OFFSET: - dblfrac = modf(Source->Offset*BufferFmt->Frequency, &dbloff); - *offset = (ALuint)mind(dbloff, UINT_MAX); - *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0); - break; + Direct.Gain = 1.0f; + Direct.GainHF = 1.0f; + Direct.HFReference = LOWPASSFREQREF; + Direct.GainLF = 1.0f; + Direct.LFReference = HIGHPASSFREQREF; + Send.resize(num_sends); + for(auto &send : Send) + { + send.Slot = nullptr; + send.Gain = 1.0f; + send.GainHF = 1.0f; + send.HFReference = LOWPASSFREQREF; + send.GainLF = 1.0f; + send.LFReference = HIGHPASSFREQREF; } - Source->OffsetType = AL_NONE; - Source->Offset = 0.0; - return AL_TRUE; -} + Offset = 0.0; + OffsetType = AL_NONE; + SourceType = AL_UNDETERMINED; + state = AL_INITIAL; + + queue = nullptr; + VoiceIdx = -1; +} -static ALsource *AllocSource(ALCcontext *context) +ALsource::~ALsource() { - ALCdevice *device{context->Device}; - almtx_lock(&context->SourceLock); - if(context->NumSources >= device->SourcesMax) - { - almtx_unlock(&context->SourceLock); - alSetError(context, AL_OUT_OF_MEMORY, "Exceeding %u source limit", device->SourcesMax); - return nullptr; - } - auto sublist = std::find_if(context->SourceList.begin(), context->SourceList.end(), - [](const SourceSubList &entry) -> bool - { return entry.FreeMask != 0; } - ); - ALsizei lidx = std::distance(context->SourceList.begin(), sublist); - ALsource *source; - ALsizei slidx; - if(LIKELY(sublist != context->SourceList.end())) - { - slidx = CTZ64(sublist->FreeMask); - source = sublist->Sources + slidx; - } - else + ALbufferlistitem *BufferList{queue}; + while(BufferList != nullptr) { - /* Don't allocate so many list entries that the 32-bit ID could - * overflow... - */ - if(UNLIKELY(context->SourceList.size() >= 1<<25)) + ALbufferlistitem *next{BufferList->next.load(std::memory_order_relaxed)}; + for(ALsizei i{0};i < BufferList->num_buffers;i++) { - almtx_unlock(&device->BufferLock); - alSetError(context, AL_OUT_OF_MEMORY, "Too many sources allocated"); - return nullptr; + if(BufferList->buffers[i]) + DecrementRef(&BufferList->buffers[i]->ref); } - context->SourceList.emplace_back(); - sublist = context->SourceList.end() - 1; + al_free(BufferList); + BufferList = next; + } + queue = nullptr; - sublist->FreeMask = ~U64(0); - sublist->Sources = static_cast(al_calloc(16, sizeof(ALsource)*64)); - if(UNLIKELY(!sublist->Sources)) + std::for_each(Send.begin(), Send.end(), + [](ALsource::SendData &send) -> void { - context->SourceList.pop_back(); - almtx_unlock(&context->SourceLock); - alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate source batch"); - return nullptr; + if(send.Slot) + DecrementRef(&send.Slot->ref); + send.Slot = nullptr; } - - slidx = 0; - source = sublist->Sources + slidx; - } - - source = new (source) ALsource{device->NumAuxSends}; - - /* Add 1 to avoid source ID 0. */ - source->id = ((lidx<<6) | slidx) + 1; - - context->NumSources++; - sublist->FreeMask &= ~(U64(1)<SourceLock); - - return source; + ); } -static void FreeSource(ALCcontext *context, ALsource *source) +void UpdateAllSourceProps(ALCcontext *context) { - ALCdevice *device = context->Device; - ALuint id = source->id - 1; - ALsizei lidx = id >> 6; - ALsizei slidx = id & 0x3f; - ALvoice *voice; - - ALCdevice_Lock(device); - if((voice=GetSourceVoice(source, context)) != NULL) + for(ALsizei i{0};i < context->VoiceCount;++i) { - ATOMIC_STORE(&voice->Source, static_cast(nullptr), almemory_order_relaxed); - ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + ALvoice *voice{context->Voices[i]}; + ALsource *source{voice->Source.load(std::memory_order_acquire)}; + if(source && !source->PropsClean.test_and_set(std::memory_order_acq_rel)) + UpdateSourceProps(source, voice, context); } - ALCdevice_Unlock(device); - - source->~ALsource(); - - context->SourceList[lidx].FreeMask |= U64(1) << slidx; - context->NumSources--; } /* ReleaseALSources @@ -3460,8 +3420,8 @@ ALvoid ReleaseALSources(ALCcontext *context) ALuint64 usemask = ~sublist.FreeMask; while(usemask) { - ALsizei idx = CTZ64(usemask); - ALsource *source = sublist.Sources + idx; + ALsizei idx{CTZ64(usemask)}; + ALsource *source{sublist.Sources + idx}; source->~ALsource(); ++leftover; -- cgit v1.2.3