2007-11-13 18:02:18 -08:00
|
|
|
/**
|
|
|
|
* OpenAL cross platform audio library
|
|
|
|
* Copyright (C) 1999-2007 by authors.
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the
|
2014-08-18 14:11:03 +02:00
|
|
|
* Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2007-11-13 18:02:18 -08:00
|
|
|
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
|
|
|
*/
|
|
|
|
|
2008-01-16 14:09:04 -08:00
|
|
|
#include "config.h"
|
|
|
|
|
2019-07-29 17:54:07 -07:00
|
|
|
#include "source.h"
|
2012-08-20 12:22:00 -07:00
|
|
|
|
2019-07-28 11:28:36 -07:00
|
|
|
#include <algorithm>
|
|
|
|
#include <array>
|
|
|
|
#include <atomic>
|
|
|
|
#include <cassert>
|
|
|
|
#include <chrono>
|
|
|
|
#include <climits>
|
2018-11-18 03:25:32 -08:00
|
|
|
#include <cmath>
|
2019-07-28 11:28:36 -07:00
|
|
|
#include <cstdint>
|
|
|
|
#include <functional>
|
|
|
|
#include <iterator>
|
2018-11-22 12:02:02 -08:00
|
|
|
#include <limits>
|
2019-07-28 11:28:36 -07:00
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
|
|
|
#include <new>
|
2019-03-09 15:04:51 -08:00
|
|
|
#include <numeric>
|
2019-07-28 11:28:36 -07:00
|
|
|
#include <thread>
|
|
|
|
#include <utility>
|
2018-11-18 02:15:31 -08:00
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
#include "AL/al.h"
|
|
|
|
#include "AL/alc.h"
|
2019-07-28 11:28:36 -07:00
|
|
|
#include "AL/alext.h"
|
|
|
|
#include "AL/efx.h"
|
2018-11-17 23:02:27 -08:00
|
|
|
|
2019-07-28 18:33:29 -07:00
|
|
|
#include "alcmain.h"
|
2019-07-28 11:28:36 -07:00
|
|
|
#include "alcontext.h"
|
2019-04-10 17:47:13 -07:00
|
|
|
#include "alexcpt.h"
|
2016-03-29 00:44:58 -07:00
|
|
|
#include "almalloc.h"
|
2019-07-28 11:28:36 -07:00
|
|
|
#include "alnumeric.h"
|
2019-09-05 04:00:49 -07:00
|
|
|
#include "aloptional.h"
|
2019-08-05 22:40:19 -07:00
|
|
|
#include "alspan.h"
|
2019-07-28 11:28:36 -07:00
|
|
|
#include "alu.h"
|
|
|
|
#include "atomic.h"
|
2019-07-29 17:54:07 -07:00
|
|
|
#include "auxeffectslot.h"
|
2019-07-28 11:28:36 -07:00
|
|
|
#include "backends/base.h"
|
|
|
|
#include "bformatdec.h"
|
2019-07-29 17:54:07 -07:00
|
|
|
#include "buffer.h"
|
2020-12-12 10:38:24 -08:00
|
|
|
#include "core/ambidefs.h"
|
2020-12-04 09:42:13 -08:00
|
|
|
#include "core/filters/nfc.h"
|
|
|
|
#include "core/filters/splitter.h"
|
2019-07-29 15:57:48 -07:00
|
|
|
#include "event.h"
|
2019-07-29 17:54:07 -07:00
|
|
|
#include "filter.h"
|
2019-07-28 11:28:36 -07:00
|
|
|
#include "inprogext.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "math_defs.h"
|
|
|
|
#include "opthelpers.h"
|
|
|
|
#include "ringbuffer.h"
|
|
|
|
#include "threads.h"
|
2014-05-11 03:52:22 -07:00
|
|
|
|
2010-11-28 17:37:14 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
namespace {
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-12-26 21:55:39 -08:00
|
|
|
using namespace std::placeholders;
|
2019-09-12 11:33:04 -07:00
|
|
|
using std::chrono::nanoseconds;
|
2018-12-26 21:55:39 -08:00
|
|
|
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *GetSourceVoice(ALsource *source, ALCcontext *context)
|
2018-01-27 13:02:17 -08:00
|
|
|
{
|
2020-02-21 20:14:28 -08:00
|
|
|
auto voicelist = context->getVoicesSpan();
|
2019-08-02 12:38:20 -07:00
|
|
|
ALuint idx{source->VoiceIdx};
|
2020-02-21 20:14:28 -08:00
|
|
|
if(idx < voicelist.size())
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2018-11-30 10:08:18 -08:00
|
|
|
ALuint sid{source->id};
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice = voicelist[idx];
|
2020-02-21 20:14:28 -08:00
|
|
|
if(voice->mSourceID.load(std::memory_order_acquire) == sid)
|
|
|
|
return voice;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-08-02 12:38:20 -07:00
|
|
|
source->VoiceIdx = INVALID_VOICE_IDX;
|
2018-11-22 12:02:02 -08:00
|
|
|
return nullptr;
|
2018-01-27 13:02:17 -08:00
|
|
|
}
|
2018-01-27 11:56:31 -08:00
|
|
|
|
2020-03-04 21:15:32 -08:00
|
|
|
|
2020-03-28 17:20:38 -07:00
|
|
|
void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context)
|
2018-01-27 01:51:01 -08:00
|
|
|
{
|
2018-11-22 12:02:02 -08:00
|
|
|
/* Get an unused property container, or allocate a new one as needed. */
|
2020-03-28 17:20:38 -07:00
|
|
|
VoicePropsItem *props{context->mFreeVoiceProps.load(std::memory_order_acquire)};
|
2018-11-22 12:02:02 -08:00
|
|
|
if(!props)
|
2020-03-28 17:20:38 -07:00
|
|
|
props = new VoicePropsItem{};
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
VoicePropsItem *next;
|
2018-11-22 12:02:02 -08:00
|
|
|
do {
|
|
|
|
next = props->next.load(std::memory_order_relaxed);
|
2019-07-30 09:05:54 -07:00
|
|
|
} while(context->mFreeVoiceProps.compare_exchange_weak(props, next,
|
2018-11-22 12:02:02 -08:00
|
|
|
std::memory_order_acq_rel, std::memory_order_acquire) == 0);
|
|
|
|
}
|
2018-01-27 01:51:01 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
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;
|
2018-12-26 22:27:34 -08:00
|
|
|
props->Position = source->Position;
|
|
|
|
props->Velocity = source->Velocity;
|
|
|
|
props->Direction = source->Direction;
|
|
|
|
props->OrientAt = source->OrientAt;
|
|
|
|
props->OrientUp = source->OrientUp;
|
2018-11-22 12:02:02 -08:00
|
|
|
props->HeadRelative = source->HeadRelative;
|
|
|
|
props->mDistanceModel = source->mDistanceModel;
|
2018-12-24 19:29:01 -08:00
|
|
|
props->mResampler = source->mResampler;
|
2018-11-22 12:02:02 -08:00
|
|
|
props->DirectChannels = source->DirectChannels;
|
2018-12-24 19:29:01 -08:00
|
|
|
props->mSpatializeMode = source->mSpatialize;
|
2018-01-27 19:40:47 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
props->DryGainHFAuto = source->DryGainHFAuto;
|
|
|
|
props->WetGainAuto = source->WetGainAuto;
|
|
|
|
props->WetGainHFAuto = source->WetGainHFAuto;
|
|
|
|
props->OuterGainHF = source->OuterGainHF;
|
2018-01-27 19:40:47 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
props->AirAbsorptionFactor = source->AirAbsorptionFactor;
|
|
|
|
props->RoomRolloffFactor = source->RoomRolloffFactor;
|
|
|
|
props->DopplerFactor = source->DopplerFactor;
|
2018-01-27 17:24:18 -08:00
|
|
|
|
2018-12-26 22:27:34 -08:00
|
|
|
props->StereoPan = source->StereoPan;
|
2018-01-27 01:51:01 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
props->Radius = source->Radius;
|
2015-09-22 08:48:26 -07:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
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;
|
2015-09-22 08:48:26 -07:00
|
|
|
|
2020-03-28 17:20:38 -07:00
|
|
|
auto copy_send = [](const ALsource::SendData &srcsend) noexcept -> VoiceProps::SendData
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-11-07 08:36:49 -08:00
|
|
|
VoiceProps::SendData ret{};
|
|
|
|
ret.Slot = srcsend.Slot ? &srcsend.Slot->mSlot : nullptr;
|
2018-12-26 22:27:34 -08:00
|
|
|
ret.Gain = srcsend.Gain;
|
|
|
|
ret.GainHF = srcsend.GainHF;
|
|
|
|
ret.HFReference = srcsend.HFReference;
|
|
|
|
ret.GainLF = srcsend.GainLF;
|
|
|
|
ret.LFReference = srcsend.LFReference;
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send);
|
2020-11-07 08:36:49 -08:00
|
|
|
if(!props->Send[0].Slot && context->mDefaultSlot)
|
|
|
|
props->Send[0].Slot = &context->mDefaultSlot->mSlot;
|
2012-12-05 09:22:38 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
/* Set the new container for updating internal parameters. */
|
2019-03-10 16:29:06 -07:00
|
|
|
props = voice->mUpdate.exchange(props, std::memory_order_acq_rel);
|
2018-11-22 12:02:02 -08:00
|
|
|
if(props)
|
|
|
|
{
|
|
|
|
/* If there was an unused update container, put it back in the
|
|
|
|
* freelist.
|
|
|
|
*/
|
2019-07-30 09:05:54 -07:00
|
|
|
AtomicReplaceHead(context->mFreeVoiceProps, props);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
}
|
2012-12-05 09:22:38 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
/* 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).
|
|
|
|
*/
|
2019-09-12 11:33:04 -07:00
|
|
|
int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2019-08-01 19:44:09 -07:00
|
|
|
ALCdevice *device{context->mDevice.get()};
|
2020-11-19 04:18:25 -08:00
|
|
|
const BufferlistItem *Current{};
|
2020-03-03 20:44:19 -08:00
|
|
|
uint64_t readPos{};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALuint refcount;
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice;
|
2012-12-05 13:48:33 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
do {
|
2020-03-03 20:32:44 -08:00
|
|
|
refcount = device->waitForMix();
|
|
|
|
*clocktime = GetDeviceClockTime(device);
|
2018-11-22 12:02:02 -08:00
|
|
|
voice = GetSourceVoice(Source, context);
|
|
|
|
if(voice)
|
|
|
|
{
|
2019-03-10 16:29:06 -07:00
|
|
|
Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
|
2016-03-25 14:40:44 -07:00
|
|
|
|
2019-03-10 16:29:06 -07:00
|
|
|
readPos = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << 32;
|
2019-08-31 15:49:34 -07:00
|
|
|
readPos |= uint64_t{voice->mPositionFrac.load(std::memory_order_relaxed)} <<
|
2020-10-21 17:16:27 -07:00
|
|
|
(32-MixerFracBits);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
std::atomic_thread_fence(std::memory_order_acquire);
|
|
|
|
} while(refcount != device->MixCount.load(std::memory_order_relaxed));
|
2016-04-25 00:30:47 -07:00
|
|
|
|
2020-03-03 20:44:19 -08:00
|
|
|
if(!voice)
|
|
|
|
return 0;
|
|
|
|
|
2020-11-19 04:18:25 -08:00
|
|
|
const BufferlistItem *BufferList{Source->queue};
|
2020-03-03 20:44:19 -08:00
|
|
|
while(BufferList && BufferList != Current)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-03-03 20:44:19 -08:00
|
|
|
readPos += uint64_t{BufferList->mSampleLen} << 32;
|
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2020-03-03 20:44:19 -08:00
|
|
|
return static_cast<int64_t>(minu64(readPos, 0x7fffffffffffffff_u64));
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2017-05-05 02:41:34 -07:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
/* 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).
|
|
|
|
*/
|
2020-03-03 20:44:19 -08:00
|
|
|
double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2019-08-01 19:44:09 -07:00
|
|
|
ALCdevice *device{context->mDevice.get()};
|
2020-11-19 04:18:25 -08:00
|
|
|
const BufferlistItem *Current{};
|
2020-03-03 20:44:19 -08:00
|
|
|
uint64_t readPos{};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALuint refcount;
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice;
|
2017-12-03 14:45:19 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
do {
|
2020-03-03 20:32:44 -08:00
|
|
|
refcount = device->waitForMix();
|
|
|
|
*clocktime = GetDeviceClockTime(device);
|
2018-11-22 12:02:02 -08:00
|
|
|
voice = GetSourceVoice(Source, context);
|
|
|
|
if(voice)
|
|
|
|
{
|
2019-03-10 16:29:06 -07:00
|
|
|
Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2020-10-21 17:16:27 -07:00
|
|
|
readPos = uint64_t{voice->mPosition.load(std::memory_order_relaxed)} << MixerFracBits;
|
2019-03-10 16:29:06 -07:00
|
|
|
readPos |= voice->mPositionFrac.load(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
std::atomic_thread_fence(std::memory_order_acquire);
|
|
|
|
} while(refcount != device->MixCount.load(std::memory_order_relaxed));
|
2012-08-20 14:16:58 -07:00
|
|
|
|
2020-03-03 20:44:19 -08:00
|
|
|
if(!voice)
|
|
|
|
return 0.0f;
|
2017-02-24 01:47:34 -08:00
|
|
|
|
2020-11-19 04:18:25 -08:00
|
|
|
const BufferlistItem *BufferList{Source->queue};
|
|
|
|
const BufferStorage *BufferFmt{nullptr};
|
2020-03-03 20:44:19 -08:00
|
|
|
while(BufferList && BufferList != Current)
|
|
|
|
{
|
|
|
|
if(!BufferFmt) BufferFmt = BufferList->mBuffer;
|
2020-10-21 17:16:27 -07:00
|
|
|
readPos += uint64_t{BufferList->mSampleLen} << MixerFracBits;
|
2020-03-03 20:44:19 -08:00
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
|
|
|
}
|
|
|
|
while(BufferList && !BufferFmt)
|
|
|
|
{
|
|
|
|
BufferFmt = BufferList->mBuffer;
|
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2020-03-03 20:44:19 -08:00
|
|
|
assert(BufferFmt != nullptr);
|
2017-03-06 13:16:14 -08:00
|
|
|
|
2020-11-19 04:18:25 -08:00
|
|
|
return static_cast<double>(readPos) / double{MixerFracOne} / BufferFmt->mSampleRate;
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
}
|
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
/* 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).
|
|
|
|
*/
|
2020-03-03 20:44:19 -08:00
|
|
|
double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2019-08-01 19:44:09 -07:00
|
|
|
ALCdevice *device{context->mDevice.get()};
|
2020-11-19 04:18:25 -08:00
|
|
|
const BufferlistItem *Current{};
|
2020-03-03 20:44:19 -08:00
|
|
|
ALuint readPos{};
|
|
|
|
ALuint readPosFrac{};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALuint refcount;
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
do {
|
2020-03-03 20:32:44 -08:00
|
|
|
refcount = device->waitForMix();
|
2018-11-22 12:02:02 -08:00
|
|
|
voice = GetSourceVoice(Source, context);
|
|
|
|
if(voice)
|
|
|
|
{
|
2019-03-10 16:29:06 -07:00
|
|
|
Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-03-10 16:29:06 -07:00
|
|
|
readPos = voice->mPosition.load(std::memory_order_relaxed);
|
|
|
|
readPosFrac = voice->mPositionFrac.load(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
std::atomic_thread_fence(std::memory_order_acquire);
|
|
|
|
} while(refcount != device->MixCount.load(std::memory_order_relaxed));
|
|
|
|
|
2020-03-03 20:44:19 -08:00
|
|
|
if(!voice)
|
|
|
|
return 0.0;
|
2019-09-11 14:33:26 -07:00
|
|
|
|
2020-11-19 04:18:25 -08:00
|
|
|
const BufferlistItem *BufferList{Source->queue};
|
2020-11-21 00:54:25 -08:00
|
|
|
const ALbuffer *BufferFmt{nullptr};
|
2019-09-11 14:33:26 -07:00
|
|
|
while(BufferList)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
if(!BufferFmt)
|
|
|
|
BufferFmt = static_cast<const ALbuffer*>(BufferList->mBuffer);
|
2020-02-17 17:30:47 -08:00
|
|
|
if(BufferList == Current) break;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2020-02-17 17:30:47 -08:00
|
|
|
readPos += BufferList->mSampleLen;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-09-11 14:33:26 -07:00
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
|
|
|
}
|
2020-02-17 17:30:47 -08:00
|
|
|
while(BufferList && !BufferFmt)
|
2019-09-11 14:33:26 -07:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
BufferFmt = static_cast<const ALbuffer*>(BufferList->mBuffer);
|
2020-02-17 17:30:47 -08:00
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
2019-09-11 14:33:26 -07:00
|
|
|
}
|
2020-02-17 17:30:47 -08:00
|
|
|
assert(BufferFmt != nullptr);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2020-03-03 20:44:19 -08:00
|
|
|
double offset{};
|
2019-09-11 14:33:26 -07:00
|
|
|
switch(name)
|
|
|
|
{
|
|
|
|
case AL_SEC_OFFSET:
|
2020-11-19 04:18:25 -08:00
|
|
|
offset = (readPos + readPosFrac/double{MixerFracOne}) / BufferFmt->mSampleRate;
|
2019-09-11 14:33:26 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AL_SAMPLE_OFFSET:
|
2020-10-21 17:16:27 -07:00
|
|
|
offset = readPos + readPosFrac/double{MixerFracOne};
|
2019-09-11 14:33:26 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AL_BYTE_OFFSET:
|
2020-11-21 00:54:25 -08:00
|
|
|
if(BufferFmt->OriginalType == UserFmtIMA4)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
ALuint FrameBlockSize{BufferFmt->OriginalAlign};
|
|
|
|
ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
|
|
|
|
ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
|
2019-09-11 14:33:26 -07:00
|
|
|
|
|
|
|
/* Round down to nearest ADPCM block */
|
2020-03-25 22:39:45 -07:00
|
|
|
offset = static_cast<double>(readPos / FrameBlockSize * BlockSize);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2020-11-21 00:54:25 -08:00
|
|
|
else if(BufferFmt->OriginalType == UserFmtMSADPCM)
|
2019-09-11 14:33:26 -07:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
ALuint FrameBlockSize{BufferFmt->OriginalAlign};
|
2019-09-11 14:33:26 -07:00
|
|
|
ALuint align{(FrameBlockSize-2)/2 + 7};
|
2020-11-21 00:54:25 -08:00
|
|
|
ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-09-11 14:33:26 -07:00
|
|
|
/* Round down to nearest ADPCM block */
|
2020-03-25 22:39:45 -07:00
|
|
|
offset = static_cast<double>(readPos / FrameBlockSize * BlockSize);
|
2019-09-11 14:33:26 -07:00
|
|
|
}
|
|
|
|
else
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
const ALuint FrameSize{BufferFmt->frameSizeFromFmt()};
|
2020-03-25 22:39:45 -07:00
|
|
|
offset = static_cast<double>(readPos * FrameSize);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-09-11 14:33:26 -07:00
|
|
|
break;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-05 15:08:22 -07:00
|
|
|
struct VoicePos {
|
|
|
|
ALuint pos, frac;
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *bufferitem;
|
2019-09-05 15:08:22 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GetSampleOffset
|
2018-11-22 12:02:02 -08:00
|
|
|
*
|
2019-09-05 15:08:22 -07:00
|
|
|
* Retrieves the voice position, fixed-point fraction, and bufferlist item
|
2020-02-26 01:03:23 -08:00
|
|
|
* using the givem offset type and offset. If the offset is out of range,
|
|
|
|
* returns an empty optional.
|
2018-11-22 12:02:02 -08:00
|
|
|
*/
|
2020-11-19 04:18:25 -08:00
|
|
|
al::optional<VoicePos> GetSampleOffset(BufferlistItem *BufferList, ALenum OffsetType,
|
2020-02-26 01:03:23 -08:00
|
|
|
double Offset)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
/* Find the first valid Buffer in the Queue */
|
2020-11-21 00:54:25 -08:00
|
|
|
const ALbuffer *BufferFmt{nullptr};
|
2019-09-05 15:08:22 -07:00
|
|
|
while(BufferList)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
BufferFmt = static_cast<const ALbuffer*>(BufferList->mBuffer);
|
|
|
|
if(BufferFmt != nullptr) break;
|
2019-07-31 09:20:53 -07:00
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-09-05 15:08:22 -07:00
|
|
|
if(!BufferList)
|
2020-11-21 00:54:25 -08:00
|
|
|
return al::nullopt;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-09-05 15:08:22 -07:00
|
|
|
/* Get sample frame offset */
|
|
|
|
ALuint offset{0u}, frac{0u};
|
2020-03-25 22:39:45 -07:00
|
|
|
double dbloff, dblfrac;
|
2020-02-26 01:03:23 -08:00
|
|
|
switch(OffsetType)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-11-19 04:18:25 -08:00
|
|
|
case AL_SEC_OFFSET:
|
|
|
|
dblfrac = std::modf(Offset*BufferFmt->mSampleRate, &dbloff);
|
|
|
|
offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
|
|
|
|
frac = static_cast<ALuint>(mind(dblfrac*MixerFracOne, MixerFracOne-1.0));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AL_SAMPLE_OFFSET:
|
|
|
|
dblfrac = std::modf(Offset, &dbloff);
|
|
|
|
offset = static_cast<ALuint>(mind(dbloff, std::numeric_limits<ALuint>::max()));
|
|
|
|
frac = static_cast<ALuint>(mind(dblfrac*MixerFracOne, MixerFracOne-1.0));
|
|
|
|
break;
|
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
case AL_BYTE_OFFSET:
|
|
|
|
/* Determine the ByteOffset (and ensure it is block aligned) */
|
2020-02-26 01:03:23 -08:00
|
|
|
offset = static_cast<ALuint>(Offset);
|
2020-11-21 00:54:25 -08:00
|
|
|
if(BufferFmt->OriginalType == UserFmtIMA4)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
const ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
|
|
|
|
offset /= align * BufferFmt->channelsFromFmt();
|
|
|
|
offset *= BufferFmt->OriginalAlign;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2020-11-21 00:54:25 -08:00
|
|
|
else if(BufferFmt->OriginalType == UserFmtMSADPCM)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
const ALuint align{(BufferFmt->OriginalAlign-2)/2 + 7};
|
|
|
|
offset /= align * BufferFmt->channelsFromFmt();
|
|
|
|
offset *= BufferFmt->OriginalAlign;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
else
|
2020-01-18 13:23:59 -08:00
|
|
|
offset /= BufferFmt->frameSizeFromFmt();
|
2019-09-05 15:08:22 -07:00
|
|
|
frac = 0;
|
2018-11-22 12:02:02 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-09-05 15:08:22 -07:00
|
|
|
/* Find the bufferlist item this offset belongs to. */
|
2018-11-22 12:02:02 -08:00
|
|
|
ALuint totalBufferLen{0u};
|
|
|
|
while(BufferList && totalBufferLen <= offset)
|
|
|
|
{
|
2019-08-11 03:34:35 -07:00
|
|
|
if(BufferList->mSampleLen > offset-totalBufferLen)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
/* Offset is in this buffer */
|
2020-11-21 00:54:25 -08:00
|
|
|
return al::make_optional(VoicePos{offset-totalBufferLen, frac, BufferList});
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-08-11 03:34:35 -07:00
|
|
|
totalBufferLen += BufferList->mSampleLen;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-31 09:20:53 -07:00
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Offset is out of range of the queue */
|
2020-11-21 00:54:25 -08:00
|
|
|
return al::nullopt;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-19 04:18:25 -08:00
|
|
|
void InitVoice(Voice *voice, ALsource *source, BufferlistItem *BufferList, ALCcontext *context,
|
2020-02-26 01:39:30 -08:00
|
|
|
ALCdevice *device)
|
|
|
|
{
|
2020-03-04 13:40:21 -08:00
|
|
|
voice->mLoopBuffer.store(source->Looping ? source->queue : nullptr, std::memory_order_relaxed);
|
2020-02-26 01:39:30 -08:00
|
|
|
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferStorage *buffer{BufferList->mBuffer};
|
2020-03-25 21:06:24 -07:00
|
|
|
ALuint num_channels{buffer->channelsFromFmt()};
|
2020-11-19 04:18:25 -08:00
|
|
|
voice->mFrequency = buffer->mSampleRate;
|
|
|
|
voice->mFmtChannels = buffer->mChannels;
|
2020-02-26 01:39:30 -08:00
|
|
|
voice->mSampleSize = buffer->bytesFromFmt();
|
2020-11-19 04:18:25 -08:00
|
|
|
voice->mAmbiLayout = buffer->mAmbiLayout;
|
|
|
|
voice->mAmbiScaling = buffer->mAmbiScaling;
|
|
|
|
voice->mAmbiOrder = buffer->mAmbiOrder;
|
2020-02-26 01:39:30 -08:00
|
|
|
|
2020-11-19 04:18:25 -08:00
|
|
|
if(buffer->mCallback) voice->mFlags |= VoiceIsCallback;
|
2020-10-21 10:39:00 -07:00
|
|
|
else if(source->SourceType == AL_STATIC) voice->mFlags |= VoiceIsStatic;
|
2020-02-26 01:39:30 -08:00
|
|
|
voice->mNumCallbackSamples = 0;
|
|
|
|
|
2020-03-04 11:01:35 -08:00
|
|
|
/* Clear the stepping value explicitly so the mixer knows not to mix this
|
|
|
|
* until the update gets applied.
|
|
|
|
*/
|
|
|
|
voice->mStep = 0;
|
|
|
|
|
2020-03-25 21:06:24 -07:00
|
|
|
if(voice->mChans.capacity() > 2 && num_channels < voice->mChans.capacity())
|
2020-03-28 17:20:38 -07:00
|
|
|
al::vector<Voice::ChannelData>{}.swap(voice->mChans);
|
2020-03-25 21:06:24 -07:00
|
|
|
voice->mChans.reserve(maxu(2, num_channels));
|
|
|
|
voice->mChans.resize(num_channels);
|
|
|
|
|
2020-02-26 01:39:30 -08:00
|
|
|
/* Don't need to set the VOICE_IS_AMBISONIC flag if the device is not
|
|
|
|
* higher order than the voice. No HF scaling is necessary to mix it.
|
|
|
|
*/
|
2020-04-04 01:52:29 -07:00
|
|
|
if(voice->mAmbiOrder && device->mAmbiOrder > voice->mAmbiOrder)
|
2020-02-26 01:39:30 -08:00
|
|
|
{
|
|
|
|
const uint8_t *OrderFromChan{(voice->mFmtChannels == FmtBFormat2D) ?
|
|
|
|
AmbiIndex::OrderFrom2DChannel.data() :
|
|
|
|
AmbiIndex::OrderFromChannel.data()};
|
2020-03-25 21:06:24 -07:00
|
|
|
const auto scales = BFormatDec::GetHFOrderScales(voice->mAmbiOrder, device->mAmbiOrder);
|
2020-02-26 01:39:30 -08:00
|
|
|
|
|
|
|
const BandSplitter splitter{400.0f / static_cast<float>(device->Frequency)};
|
|
|
|
|
2020-03-25 21:06:24 -07:00
|
|
|
for(auto &chandata : voice->mChans)
|
2020-02-26 01:39:30 -08:00
|
|
|
{
|
|
|
|
chandata.mPrevSamples.fill(0.0f);
|
|
|
|
chandata.mAmbiScale = scales[*(OrderFromChan++)];
|
|
|
|
chandata.mAmbiSplitter = splitter;
|
|
|
|
chandata.mDryParams = DirectParams{};
|
|
|
|
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
|
2020-03-25 21:06:24 -07:00
|
|
|
}
|
2020-02-26 01:39:30 -08:00
|
|
|
|
2020-10-21 10:39:00 -07:00
|
|
|
voice->mFlags |= VoiceIsAmbisonic;
|
2020-02-26 01:39:30 -08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Clear previous samples. */
|
2020-03-25 21:06:24 -07:00
|
|
|
for(auto &chandata : voice->mChans)
|
2020-02-26 01:39:30 -08:00
|
|
|
{
|
|
|
|
chandata.mPrevSamples.fill(0.0f);
|
|
|
|
chandata.mDryParams = DirectParams{};
|
|
|
|
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
|
2020-03-25 21:06:24 -07:00
|
|
|
}
|
2020-02-26 01:39:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if(device->AvgSpeakerDist > 0.0f)
|
|
|
|
{
|
2020-10-21 16:39:21 -07:00
|
|
|
const float w1{SpeedOfSoundMetersPerSec /
|
2020-02-26 01:39:30 -08:00
|
|
|
(device->AvgSpeakerDist * static_cast<float>(device->Frequency))};
|
2020-03-25 21:06:24 -07:00
|
|
|
for(auto &chandata : voice->mChans)
|
|
|
|
chandata.mDryParams.NFCtrlFilter.init(w1);
|
2020-02-26 01:39:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
source->PropsClean.test_and_set(std::memory_order_acq_rel);
|
|
|
|
UpdateSourceProps(source, voice, context);
|
2020-03-04 21:15:32 -08:00
|
|
|
|
|
|
|
voice->mSourceID.store(source->id, std::memory_order_release);
|
2020-02-26 01:39:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-21 03:23:22 -08:00
|
|
|
VoiceChange *GetVoiceChanger(ALCcontext *ctx)
|
2020-02-20 22:50:37 -08:00
|
|
|
{
|
2020-02-21 03:23:22 -08:00
|
|
|
VoiceChange *vchg{ctx->mVoiceChangeTail};
|
|
|
|
if UNLIKELY(vchg == ctx->mCurrentVoiceChange.load(std::memory_order_acquire))
|
2020-02-20 22:50:37 -08:00
|
|
|
{
|
2020-02-21 03:23:22 -08:00
|
|
|
ctx->allocVoiceChanges(1);
|
|
|
|
vchg = ctx->mVoiceChangeTail;
|
2020-02-20 22:50:37 -08:00
|
|
|
}
|
|
|
|
|
2020-02-21 03:23:22 -08:00
|
|
|
ctx->mVoiceChangeTail = vchg->mNext.exchange(nullptr, std::memory_order_relaxed);
|
2020-02-20 22:50:37 -08:00
|
|
|
|
2020-02-21 03:23:22 -08:00
|
|
|
return vchg;
|
2020-02-20 22:50:37 -08:00
|
|
|
}
|
|
|
|
|
2020-02-21 03:23:22 -08:00
|
|
|
void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
|
2020-02-20 22:50:37 -08:00
|
|
|
{
|
2020-02-20 23:53:56 -08:00
|
|
|
ALCdevice *device{ctx->mDevice.get()};
|
|
|
|
|
2020-02-20 22:50:37 -08:00
|
|
|
VoiceChange *oldhead{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
|
|
|
|
while(VoiceChange *next{oldhead->mNext.load(std::memory_order_relaxed)})
|
|
|
|
oldhead = next;
|
|
|
|
oldhead->mNext.store(tail, std::memory_order_release);
|
2020-02-20 23:53:56 -08:00
|
|
|
|
2020-02-24 11:01:45 -08:00
|
|
|
const bool connected{device->Connected.load(std::memory_order_acquire)};
|
2020-03-03 20:32:44 -08:00
|
|
|
device->waitForMix();
|
2020-02-24 11:01:45 -08:00
|
|
|
if UNLIKELY(!connected)
|
|
|
|
{
|
|
|
|
/* If the device is disconnected, just ignore all pending changes. */
|
|
|
|
VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
|
|
|
|
while(VoiceChange *next{cur->mNext.load(std::memory_order_acquire)})
|
2020-02-24 22:21:44 -08:00
|
|
|
{
|
2020-02-24 11:01:45 -08:00
|
|
|
cur = next;
|
2020-03-28 17:20:38 -07:00
|
|
|
if(Voice *voice{cur->mVoice})
|
2020-02-24 22:21:44 -08:00
|
|
|
voice->mSourceID.store(0, std::memory_order_relaxed);
|
|
|
|
}
|
2020-02-24 11:01:45 -08:00
|
|
|
ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
|
|
|
|
}
|
2020-02-20 22:50:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-28 17:20:38 -07:00
|
|
|
bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALCcontext *context,
|
2020-02-26 03:46:15 -08:00
|
|
|
ALCdevice *device)
|
|
|
|
{
|
|
|
|
/* First, get a free voice to start at the new offset. */
|
|
|
|
auto voicelist = context->getVoicesSpan();
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *newvoice{};
|
2020-02-26 03:46:15 -08:00
|
|
|
ALuint vidx{0};
|
2020-03-28 17:20:38 -07:00
|
|
|
for(Voice *voice : voicelist)
|
2020-02-26 03:46:15 -08:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
if(voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
|
2020-02-26 03:46:15 -08:00
|
|
|
&& voice->mSourceID.load(std::memory_order_relaxed) == 0u
|
|
|
|
&& voice->mPendingChange.load(std::memory_order_relaxed) == false)
|
|
|
|
{
|
|
|
|
newvoice = voice;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++vidx;
|
|
|
|
}
|
2020-02-28 20:07:43 -08:00
|
|
|
if UNLIKELY(!newvoice)
|
|
|
|
{
|
|
|
|
auto &allvoices = *context->mVoices.load(std::memory_order_relaxed);
|
|
|
|
if(allvoices.size() == voicelist.size())
|
|
|
|
context->allocVoices(1);
|
|
|
|
context->mActiveVoiceCount.fetch_add(1, std::memory_order_release);
|
|
|
|
voicelist = context->getVoicesSpan();
|
|
|
|
|
|
|
|
vidx = 0;
|
2020-03-28 17:20:38 -07:00
|
|
|
for(Voice *voice : voicelist)
|
2020-02-28 20:07:43 -08:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
if(voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
|
2020-02-28 20:07:43 -08:00
|
|
|
&& voice->mSourceID.load(std::memory_order_relaxed) == 0u
|
|
|
|
&& voice->mPendingChange.load(std::memory_order_relaxed) == false)
|
|
|
|
{
|
|
|
|
newvoice = voice;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++vidx;
|
|
|
|
}
|
|
|
|
}
|
2020-02-26 03:46:15 -08:00
|
|
|
|
|
|
|
/* Initialize the new voice and set its starting offset.
|
|
|
|
* TODO: It might be better to have the VoiceChange processing copy the old
|
|
|
|
* voice's mixing parameters (and pending update) insead of initializing it
|
|
|
|
* all here. This would just need to set the minimum properties to link the
|
|
|
|
* voice to the source and its position-dependent properties (including the
|
|
|
|
* fading flag).
|
|
|
|
*/
|
2020-03-28 17:20:38 -07:00
|
|
|
newvoice->mPlayState.store(Voice::Pending, std::memory_order_relaxed);
|
2020-02-26 03:46:15 -08:00
|
|
|
newvoice->mPosition.store(vpos.pos, std::memory_order_relaxed);
|
|
|
|
newvoice->mPositionFrac.store(vpos.frac, std::memory_order_relaxed);
|
|
|
|
newvoice->mCurrentBuffer.store(vpos.bufferitem, std::memory_order_relaxed);
|
2020-03-04 13:40:21 -08:00
|
|
|
newvoice->mFlags = (vpos.pos > 0 || vpos.frac > 0 || vpos.bufferitem != source->queue) ?
|
2020-10-21 10:39:00 -07:00
|
|
|
VoiceIsFading : 0u;
|
2020-03-04 13:40:21 -08:00
|
|
|
InitVoice(newvoice, source, source->queue, context, device);
|
2020-02-26 03:46:15 -08:00
|
|
|
source->VoiceIdx = vidx;
|
|
|
|
|
2020-03-04 10:09:44 -08:00
|
|
|
/* Set the old voice as having a pending change, and send it off with the
|
|
|
|
* new one with a new offset voice change.
|
2020-02-26 03:46:15 -08:00
|
|
|
*/
|
|
|
|
oldvoice->mPendingChange.store(true, std::memory_order_relaxed);
|
|
|
|
|
|
|
|
VoiceChange *vchg{GetVoiceChanger(context)};
|
|
|
|
vchg->mOldVoice = oldvoice;
|
|
|
|
vchg->mVoice = newvoice;
|
|
|
|
vchg->mSourceID = source->id;
|
|
|
|
vchg->mState = AL_SAMPLE_OFFSET;
|
|
|
|
SendVoiceChanges(context, vchg);
|
|
|
|
|
|
|
|
/* If the old voice still has a sourceID, it's still active and the change-
|
|
|
|
* over will work on the next update.
|
|
|
|
*/
|
|
|
|
if LIKELY(oldvoice->mSourceID.load(std::memory_order_acquire) != 0u)
|
|
|
|
return true;
|
|
|
|
|
2020-03-04 10:09:44 -08:00
|
|
|
/* Otherwise, if the new voice's state is not pending, the change-over
|
2020-02-26 03:46:15 -08:00
|
|
|
* already happened.
|
|
|
|
*/
|
2020-03-28 17:20:38 -07:00
|
|
|
if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending)
|
2020-02-26 03:46:15 -08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Otherwise, wait for any current mix to finish and check one last time. */
|
2020-03-03 20:32:44 -08:00
|
|
|
device->waitForMix();
|
2020-03-28 17:20:38 -07:00
|
|
|
if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending)
|
2020-02-26 03:46:15 -08:00
|
|
|
return true;
|
|
|
|
/* The change-over failed because the old voice stopped before the new
|
|
|
|
* voice could start at the new offset. Let go of the new voice and have
|
|
|
|
* the caller store the source offset since it's stopped.
|
|
|
|
*/
|
|
|
|
newvoice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
|
|
|
|
newvoice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
|
|
|
|
newvoice->mSourceID.store(0u, std::memory_order_relaxed);
|
2020-03-28 17:20:38 -07:00
|
|
|
newvoice->mPlayState.store(Voice::Stopped, std::memory_order_relaxed);
|
2020-02-26 03:46:15 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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).
|
|
|
|
*/
|
2020-03-28 17:20:38 -07:00
|
|
|
inline ALenum GetSourceState(ALsource *source, Voice *voice)
|
2020-02-26 03:46:15 -08:00
|
|
|
{
|
|
|
|
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->mDeferUpdates.load(std::memory_order_acquire) &&
|
|
|
|
IsPlayingOrPaused(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
bool EnsureSources(ALCcontext *context, size_t needed)
|
|
|
|
{
|
|
|
|
size_t count{std::accumulate(context->mSourceList.cbegin(), context->mSourceList.cend(),
|
|
|
|
size_t{0},
|
|
|
|
[](size_t cur, const SourceSubList &sublist) noexcept -> size_t
|
2020-10-13 01:21:44 -07:00
|
|
|
{ return cur + static_cast<ALuint>(PopCount(sublist.FreeMask)); })};
|
2019-09-12 11:33:04 -07:00
|
|
|
|
|
|
|
while(needed > count)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(context->mSourceList.size() >= 1<<25)
|
2019-09-12 11:33:04 -07:00
|
|
|
return false;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
context->mSourceList.emplace_back();
|
|
|
|
auto sublist = context->mSourceList.end() - 1;
|
2019-01-07 04:06:40 -08:00
|
|
|
sublist->FreeMask = ~0_u64;
|
2019-09-12 11:33:04 -07:00
|
|
|
sublist->Sources = static_cast<ALsource*>(al_calloc(alignof(ALsource), sizeof(ALsource)*64));
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!sublist->Sources)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2019-07-30 09:05:54 -07:00
|
|
|
context->mSourceList.pop_back();
|
2019-09-12 11:33:04 -07:00
|
|
|
return false;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-09-12 11:33:04 -07:00
|
|
|
count += 64;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-09-12 11:33:04 -07:00
|
|
|
return true;
|
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2020-02-25 06:39:03 -08:00
|
|
|
ALsource *AllocSource(ALCcontext *context)
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
|
|
|
auto sublist = std::find_if(context->mSourceList.begin(), context->mSourceList.end(),
|
|
|
|
[](const SourceSubList &entry) noexcept -> bool
|
|
|
|
{ return entry.FreeMask != 0; }
|
|
|
|
);
|
|
|
|
auto lidx = static_cast<ALuint>(std::distance(context->mSourceList.begin(), sublist));
|
2020-10-13 01:21:44 -07:00
|
|
|
auto slidx = static_cast<ALuint>(CountTrailingZeros(sublist->FreeMask));
|
2019-09-12 11:33:04 -07:00
|
|
|
|
2020-02-25 06:39:03 -08:00
|
|
|
ALsource *source{::new(sublist->Sources + slidx) ALsource{}};
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
/* Add 1 to avoid source ID 0. */
|
|
|
|
source->id = ((lidx<<6) | slidx) + 1;
|
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
context->mNumSources += 1;
|
2019-01-07 04:06:40 -08:00
|
|
|
sublist->FreeMask &= ~(1_u64 << slidx);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
return source;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FreeSource(ALCcontext *context, ALsource *source)
|
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
const ALuint id{source->id - 1};
|
|
|
|
const size_t lidx{id >> 6};
|
|
|
|
const ALuint slidx{id & 0x3f};
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
if(IsPlayingOrPaused(source))
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
if(Voice *voice{GetSourceVoice(source, context)})
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
2020-02-21 03:23:22 -08:00
|
|
|
VoiceChange *vchg{GetVoiceChanger(context)};
|
2020-02-20 22:50:37 -08:00
|
|
|
|
2020-02-26 01:48:59 -08:00
|
|
|
voice->mPendingChange.store(true, std::memory_order_relaxed);
|
2020-02-20 22:50:37 -08:00
|
|
|
vchg->mVoice = voice;
|
2020-04-21 01:01:18 -07:00
|
|
|
vchg->mSourceID = source->id;
|
2020-02-20 22:50:37 -08:00
|
|
|
vchg->mState = AL_STOPPED;
|
|
|
|
|
2020-02-21 03:23:22 -08:00
|
|
|
SendVoiceChanges(context, vchg);
|
2019-09-12 11:33:04 -07:00
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
|
2019-06-05 17:25:08 -07:00
|
|
|
al::destroy_at(source);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
context->mSourceList[lidx].FreeMask |= 1_u64 << slidx;
|
|
|
|
context->mNumSources--;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept
|
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
const size_t lidx{(id-1) >> 6};
|
|
|
|
const ALuint slidx{(id-1) & 0x3f};
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(lidx >= context->mSourceList.size())
|
2018-11-22 12:02:02 -08:00
|
|
|
return nullptr;
|
2019-07-30 09:05:54 -07:00
|
|
|
SourceSubList &sublist{context->mSourceList[lidx]};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
|
2018-11-22 12:02:02 -08:00
|
|
|
return nullptr;
|
|
|
|
return sublist.Sources + slidx;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
|
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
const size_t lidx{(id-1) >> 6};
|
|
|
|
const ALuint slidx{(id-1) & 0x3f};
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(lidx >= device->BufferList.size())
|
2018-11-22 12:02:02 -08:00
|
|
|
return nullptr;
|
|
|
|
BufferSubList &sublist = device->BufferList[lidx];
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
|
2018-11-22 12:02:02 -08:00
|
|
|
return nullptr;
|
|
|
|
return sublist.Buffers + slidx;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept
|
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
const size_t lidx{(id-1) >> 6};
|
|
|
|
const ALuint slidx{(id-1) & 0x3f};
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(lidx >= device->FilterList.size())
|
2018-11-22 12:02:02 -08:00
|
|
|
return nullptr;
|
|
|
|
FilterSubList &sublist = device->FilterList[lidx];
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
|
2018-11-22 12:02:02 -08:00
|
|
|
return nullptr;
|
|
|
|
return sublist.Filters + slidx;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
|
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
const size_t lidx{(id-1) >> 6};
|
|
|
|
const ALuint slidx{(id-1) & 0x3f};
|
2019-02-20 22:00:26 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(lidx >= context->mEffectSlotList.size())
|
2019-02-20 22:00:26 -08:00
|
|
|
return nullptr;
|
2019-07-30 09:05:54 -07:00
|
|
|
EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
|
2018-11-22 12:02:02 -08:00
|
|
|
return nullptr;
|
2019-02-20 22:00:26 -08:00
|
|
|
return sublist.EffectSlots + slidx;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-20 01:37:19 -08:00
|
|
|
al::optional<SpatializeMode> SpatializeModeFromEnum(ALenum mode)
|
|
|
|
{
|
|
|
|
switch(mode)
|
|
|
|
{
|
|
|
|
case AL_FALSE: return al::make_optional(SpatializeMode::Off);
|
|
|
|
case AL_TRUE: return al::make_optional(SpatializeMode::On);
|
|
|
|
case AL_AUTO_SOFT: return al::make_optional(SpatializeMode::Auto);
|
|
|
|
}
|
|
|
|
WARN("Unsupported spatialize mode: 0x%04x\n", mode);
|
|
|
|
return al::nullopt;
|
|
|
|
}
|
|
|
|
ALenum EnumFromSpatializeMode(SpatializeMode mode)
|
|
|
|
{
|
|
|
|
switch(mode)
|
|
|
|
{
|
|
|
|
case SpatializeMode::Off: return AL_FALSE;
|
|
|
|
case SpatializeMode::On: return AL_TRUE;
|
|
|
|
case SpatializeMode::Auto: return AL_AUTO_SOFT;
|
|
|
|
}
|
|
|
|
throw std::runtime_error{"Invalid SpatializeMode: "+std::to_string(int(mode))};
|
|
|
|
}
|
|
|
|
|
|
|
|
al::optional<DirectMode> DirectModeFromEnum(ALenum mode)
|
|
|
|
{
|
|
|
|
switch(mode)
|
|
|
|
{
|
|
|
|
case AL_FALSE: return al::make_optional(DirectMode::Off);
|
|
|
|
case AL_DROP_UNMATCHED_SOFT: return al::make_optional(DirectMode::DropMismatch);
|
|
|
|
case AL_REMIX_UNMATCHED_SOFT: return al::make_optional(DirectMode::RemixMismatch);
|
|
|
|
}
|
|
|
|
WARN("Unsupported direct mode: 0x%04x\n", mode);
|
|
|
|
return al::nullopt;
|
|
|
|
}
|
|
|
|
ALenum EnumFromDirectMode(DirectMode mode)
|
|
|
|
{
|
|
|
|
switch(mode)
|
|
|
|
{
|
|
|
|
case DirectMode::Off: return AL_FALSE;
|
|
|
|
case DirectMode::DropMismatch: return AL_DROP_UNMATCHED_SOFT;
|
|
|
|
case DirectMode::RemixMismatch: return AL_REMIX_UNMATCHED_SOFT;
|
|
|
|
}
|
|
|
|
throw std::runtime_error{"Invalid DirectMode: "+std::to_string(int(mode))};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-24 11:41:50 -08:00
|
|
|
enum SourceProp : ALenum {
|
2018-11-22 12:02:02 -08:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-08-11 22:48:18 -07:00
|
|
|
constexpr size_t MaxValues{6u};
|
|
|
|
|
2019-08-03 19:36:19 -07:00
|
|
|
ALuint FloatValsByProp(ALenum prop)
|
2012-12-05 20:51:25 -08:00
|
|
|
{
|
2018-11-24 11:41:50 -08:00
|
|
|
switch(static_cast<SourceProp>(prop))
|
2012-12-05 20:51:25 -08:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_PITCH:
|
|
|
|
case AL_GAIN:
|
|
|
|
case AL_MIN_GAIN:
|
|
|
|
case AL_MAX_GAIN:
|
|
|
|
case AL_MAX_DISTANCE:
|
|
|
|
case AL_ROLLOFF_FACTOR:
|
|
|
|
case AL_DOPPLER_FACTOR:
|
|
|
|
case AL_CONE_OUTER_GAIN:
|
|
|
|
case AL_SEC_OFFSET:
|
|
|
|
case AL_SAMPLE_OFFSET:
|
|
|
|
case AL_BYTE_OFFSET:
|
|
|
|
case AL_CONE_INNER_ANGLE:
|
|
|
|
case AL_CONE_OUTER_ANGLE:
|
|
|
|
case AL_REFERENCE_DISTANCE:
|
|
|
|
case AL_CONE_OUTER_GAINHF:
|
|
|
|
case AL_AIR_ABSORPTION_FACTOR:
|
|
|
|
case AL_ROOM_ROLLOFF_FACTOR:
|
|
|
|
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_RELATIVE:
|
|
|
|
case AL_LOOPING:
|
|
|
|
case AL_SOURCE_STATE:
|
|
|
|
case AL_BUFFERS_QUEUED:
|
|
|
|
case AL_BUFFERS_PROCESSED:
|
|
|
|
case AL_SOURCE_TYPE:
|
|
|
|
case AL_SOURCE_RADIUS:
|
|
|
|
case AL_SOURCE_RESAMPLER_SOFT:
|
|
|
|
case AL_SOURCE_SPATIALIZE_SOFT:
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case AL_STEREO_ANGLES:
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
case AL_POSITION:
|
|
|
|
case AL_VELOCITY:
|
|
|
|
case AL_DIRECTION:
|
|
|
|
return 3;
|
|
|
|
|
|
|
|
case AL_ORIENTATION:
|
|
|
|
return 6;
|
|
|
|
|
|
|
|
case AL_SEC_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SEC_OFFSET_CLOCK_SOFT:
|
|
|
|
break; /* Double only */
|
|
|
|
|
|
|
|
case AL_BUFFER:
|
|
|
|
case AL_DIRECT_FILTER:
|
|
|
|
case AL_AUXILIARY_SEND_FILTER:
|
|
|
|
break; /* i/i64 only */
|
|
|
|
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SAMPLE_OFFSET_CLOCK_SOFT:
|
|
|
|
break; /* i64 only */
|
2012-12-05 20:51:25 -08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2019-08-03 19:36:19 -07:00
|
|
|
ALuint DoubleValsByProp(ALenum prop)
|
2012-12-05 20:51:25 -08:00
|
|
|
{
|
2018-11-24 11:41:50 -08:00
|
|
|
switch(static_cast<SourceProp>(prop))
|
2012-12-05 20:51:25 -08:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_PITCH:
|
|
|
|
case AL_GAIN:
|
|
|
|
case AL_MIN_GAIN:
|
|
|
|
case AL_MAX_GAIN:
|
|
|
|
case AL_MAX_DISTANCE:
|
|
|
|
case AL_ROLLOFF_FACTOR:
|
|
|
|
case AL_DOPPLER_FACTOR:
|
|
|
|
case AL_CONE_OUTER_GAIN:
|
|
|
|
case AL_SEC_OFFSET:
|
|
|
|
case AL_SAMPLE_OFFSET:
|
|
|
|
case AL_BYTE_OFFSET:
|
|
|
|
case AL_CONE_INNER_ANGLE:
|
|
|
|
case AL_CONE_OUTER_ANGLE:
|
|
|
|
case AL_REFERENCE_DISTANCE:
|
|
|
|
case AL_CONE_OUTER_GAINHF:
|
|
|
|
case AL_AIR_ABSORPTION_FACTOR:
|
|
|
|
case AL_ROOM_ROLLOFF_FACTOR:
|
|
|
|
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_RELATIVE:
|
|
|
|
case AL_LOOPING:
|
|
|
|
case AL_SOURCE_STATE:
|
|
|
|
case AL_BUFFERS_QUEUED:
|
|
|
|
case AL_BUFFERS_PROCESSED:
|
|
|
|
case AL_SOURCE_TYPE:
|
|
|
|
case AL_SOURCE_RADIUS:
|
|
|
|
case AL_SOURCE_RESAMPLER_SOFT:
|
|
|
|
case AL_SOURCE_SPATIALIZE_SOFT:
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case AL_SEC_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SEC_OFFSET_CLOCK_SOFT:
|
|
|
|
case AL_STEREO_ANGLES:
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
case AL_POSITION:
|
|
|
|
case AL_VELOCITY:
|
|
|
|
case AL_DIRECTION:
|
|
|
|
return 3;
|
|
|
|
|
|
|
|
case AL_ORIENTATION:
|
|
|
|
return 6;
|
|
|
|
|
|
|
|
case AL_BUFFER:
|
|
|
|
case AL_DIRECT_FILTER:
|
|
|
|
case AL_AUXILIARY_SEND_FILTER:
|
|
|
|
break; /* i/i64 only */
|
|
|
|
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SAMPLE_OFFSET_CLOCK_SOFT:
|
|
|
|
break; /* i64 only */
|
2012-12-05 20:51:25 -08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2012-12-05 19:58:01 -08:00
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const float> values);
|
|
|
|
bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int> values);
|
|
|
|
bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int64_t> values);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-08-03 18:57:38 -07:00
|
|
|
#define CHECKSIZE(v, s) do { \
|
2019-08-11 22:48:18 -07:00
|
|
|
if LIKELY((v).size() == (s) || (v).size() == MaxValues) break; \
|
|
|
|
Context->setError(AL_INVALID_ENUM, \
|
|
|
|
"Property 0x%04x expects %d value(s), got %zu", prop, (s), \
|
|
|
|
(v).size()); \
|
|
|
|
return false; \
|
2019-08-03 18:57:38 -07:00
|
|
|
} while(0)
|
2012-08-28 22:16:55 -07:00
|
|
|
#define CHECKVAL(x) do { \
|
2019-08-11 22:48:18 -07:00
|
|
|
if LIKELY(x) break; \
|
|
|
|
Context->setError(AL_INVALID_VALUE, "Value out of range"); \
|
|
|
|
return false; \
|
2012-08-28 22:16:55 -07:00
|
|
|
} while(0)
|
|
|
|
|
2019-08-03 18:57:38 -07:00
|
|
|
bool UpdateSourceProps(ALsource *source, ALCcontext *context)
|
2019-05-23 05:06:26 -07:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice;
|
2019-05-23 05:06:26 -07:00
|
|
|
if(SourceShouldUpdate(source, context) && (voice=GetSourceVoice(source, context)) != nullptr)
|
|
|
|
UpdateSourceProps(source, voice, context);
|
|
|
|
else
|
|
|
|
source->PropsClean.clear(std::memory_order_release);
|
2019-08-03 18:57:38 -07:00
|
|
|
return true;
|
2019-05-23 05:06:26 -07:00
|
|
|
}
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const float> values)
|
2012-08-28 22:16:55 -07:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
int ival;
|
2012-12-05 09:22:38 -08:00
|
|
|
|
|
|
|
switch(prop)
|
2012-08-28 22:16:55 -07:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SEC_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SEC_OFFSET_CLOCK_SOFT:
|
|
|
|
/* Query only */
|
|
|
|
SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
|
|
|
|
"Setting read-only source property 0x%04x", prop);
|
2015-09-22 08:48:26 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_PITCH:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->Pitch = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_CONE_INNER_ANGLE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->InnerAngle = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_CONE_OUTER_ANGLE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f && values[0] <= 360.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->OuterAngle = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_GAIN:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->Gain = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_MAX_DISTANCE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->MaxDistance = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_ROLLOFF_FACTOR:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->RolloffFactor = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_REFERENCE_DISTANCE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->RefDistance = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_MIN_GAIN:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->MinGain = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_MAX_GAIN:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->MaxGain = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_CONE_OUTER_GAIN:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->OuterGain = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_CONE_OUTER_GAINHF:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->OuterGainHF = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_AIR_ABSORPTION_FACTOR:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->AirAbsorptionFactor = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_ROOM_ROLLOFF_FACTOR:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f && values[0] <= 10.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->RoomRolloffFactor = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_DOPPLER_FACTOR:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->DopplerFactor = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SEC_OFFSET:
|
|
|
|
case AL_SAMPLE_OFFSET:
|
|
|
|
case AL_BYTE_OFFSET:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2020-03-28 17:20:38 -07:00
|
|
|
if(Voice *voice{GetSourceVoice(Source, Context)})
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
2020-10-21 10:39:00 -07:00
|
|
|
if((voice->mFlags&VoiceIsCallback))
|
2020-02-26 03:46:15 -08:00
|
|
|
SETERR_RETURN(Context, AL_INVALID_VALUE, false,
|
|
|
|
"Source offset for callback is invalid");
|
|
|
|
auto vpos = GetSampleOffset(Source->queue, prop, values[0]);
|
|
|
|
if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid offset");
|
|
|
|
|
|
|
|
if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mDevice.get()))
|
2020-02-26 01:03:23 -08:00
|
|
|
return true;
|
2019-09-12 11:33:04 -07:00
|
|
|
}
|
2020-02-26 01:03:23 -08:00
|
|
|
Source->OffsetType = prop;
|
|
|
|
Source->Offset = values[0];
|
2019-09-12 11:33:04 -07:00
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SOURCE_RADIUS:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0.0f && std::isfinite(values[0]));
|
|
|
|
|
|
|
|
Source->Radius = values[0];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
|
|
|
|
|
|
|
case AL_STEREO_ANGLES:
|
|
|
|
CHECKSIZE(values, 2);
|
|
|
|
CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]));
|
|
|
|
|
|
|
|
Source->StereoPan[0] = values[0];
|
|
|
|
Source->StereoPan[1] = values[1];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
|
|
|
|
|
|
|
|
|
|
|
case AL_POSITION:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
|
|
|
|
|
|
|
|
Source->Position[0] = values[0];
|
|
|
|
Source->Position[1] = values[1];
|
|
|
|
Source->Position[2] = values[2];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
|
|
|
|
|
|
|
case AL_VELOCITY:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
|
|
|
|
|
|
|
|
Source->Velocity[0] = values[0];
|
|
|
|
Source->Velocity[1] = values[1];
|
|
|
|
Source->Velocity[2] = values[2];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
|
|
|
|
|
|
|
case AL_DIRECTION:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
|
|
|
|
|
|
|
|
Source->Direction[0] = values[0];
|
|
|
|
Source->Direction[1] = values[1];
|
|
|
|
Source->Direction[2] = values[2];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
|
|
|
|
|
|
|
case AL_ORIENTATION:
|
|
|
|
CHECKSIZE(values, 6);
|
|
|
|
CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2])
|
|
|
|
&& std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]));
|
|
|
|
|
|
|
|
Source->OrientAt[0] = values[0];
|
|
|
|
Source->OrientAt[1] = values[1];
|
|
|
|
Source->OrientAt[2] = values[2];
|
|
|
|
Source->OrientUp[0] = values[3];
|
|
|
|
Source->OrientUp[1] = values[4];
|
|
|
|
Source->OrientUp[2] = values[5];
|
|
|
|
return UpdateSourceProps(Source, Context);
|
|
|
|
|
|
|
|
|
|
|
|
case AL_SOURCE_RELATIVE:
|
|
|
|
case AL_LOOPING:
|
|
|
|
case AL_SOURCE_STATE:
|
|
|
|
case AL_SOURCE_TYPE:
|
|
|
|
case AL_DISTANCE_MODEL:
|
|
|
|
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_SOURCE_RESAMPLER_SOFT:
|
|
|
|
case AL_SOURCE_SPATIALIZE_SOFT:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-03-25 22:39:45 -07:00
|
|
|
ival = static_cast<int>(values[0]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourceiv(Source, Context, prop, {&ival, 1u});
|
|
|
|
|
|
|
|
case AL_BUFFERS_QUEUED:
|
|
|
|
case AL_BUFFERS_PROCESSED:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-03-25 22:39:45 -07:00
|
|
|
ival = static_cast<int>(static_cast<ALuint>(values[0]));
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourceiv(Source, Context, prop, {&ival, 1u});
|
|
|
|
|
|
|
|
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;
|
2012-08-28 22:16:55 -07:00
|
|
|
}
|
|
|
|
|
2012-12-05 09:22:38 -08:00
|
|
|
ERR("Unexpected property: 0x%04x\n", prop);
|
2019-07-30 21:32:05 -07:00
|
|
|
Context->setError(AL_INVALID_ENUM, "Invalid source float property 0x%04x", prop);
|
2019-08-03 18:57:38 -07:00
|
|
|
return false;
|
2012-08-28 22:16:55 -07:00
|
|
|
}
|
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int> values)
|
2012-08-28 22:16:55 -07:00
|
|
|
{
|
2019-08-01 19:44:09 -07:00
|
|
|
ALCdevice *device{Context->mDevice.get()};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALeffectslot *slot{nullptr};
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *oldlist{nullptr};
|
2018-11-26 21:50:48 -08:00
|
|
|
std::unique_lock<std::mutex> slotlock;
|
2020-03-25 22:39:45 -07:00
|
|
|
float fvals[6];
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2012-12-05 09:55:05 -08:00
|
|
|
switch(prop)
|
2012-08-28 22:16:55 -07:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SOURCE_STATE:
|
|
|
|
case AL_SOURCE_TYPE:
|
|
|
|
case AL_BUFFERS_QUEUED:
|
|
|
|
case AL_BUFFERS_PROCESSED:
|
|
|
|
/* Query only */
|
|
|
|
SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
|
|
|
|
"Setting read-only source property 0x%04x", prop);
|
|
|
|
|
|
|
|
case AL_SOURCE_RELATIVE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
|
|
|
|
|
2019-09-14 20:19:59 -07:00
|
|
|
Source->HeadRelative = values[0] != AL_FALSE;
|
2019-09-12 11:33:04 -07:00
|
|
|
return UpdateSourceProps(Source, Context);
|
|
|
|
|
|
|
|
case AL_LOOPING:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
|
|
|
|
|
2019-09-14 20:19:59 -07:00
|
|
|
Source->Looping = values[0] != AL_FALSE;
|
2019-09-12 11:33:04 -07:00
|
|
|
if(IsPlayingOrPaused(Source))
|
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
if(Voice *voice{GetSourceVoice(Source, Context)})
|
2017-04-02 06:35:44 -07:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
if(Source->Looping)
|
|
|
|
voice->mLoopBuffer.store(Source->queue, std::memory_order_release);
|
|
|
|
else
|
|
|
|
voice->mLoopBuffer.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.
|
|
|
|
*/
|
2020-03-03 20:32:44 -08:00
|
|
|
device->waitForMix();
|
2017-04-02 06:35:44 -07:00
|
|
|
}
|
2019-09-12 11:33:04 -07:00
|
|
|
}
|
|
|
|
return true;
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_BUFFER:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
{
|
2020-02-17 00:32:22 -08:00
|
|
|
const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
|
2019-09-12 11:33:04 -07:00
|
|
|
if(state == AL_PLAYING || state == AL_PAUSED)
|
2019-08-03 18:57:38 -07:00
|
|
|
SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
|
2019-09-12 11:33:04 -07:00
|
|
|
"Setting buffer on playing or paused source %u", Source->id);
|
|
|
|
}
|
|
|
|
oldlist = Source->queue;
|
2020-10-13 05:54:05 -07:00
|
|
|
if(values[0])
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
2020-10-13 05:54:05 -07:00
|
|
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
|
|
|
ALbuffer *buffer{LookupBuffer(device, static_cast<ALuint>(values[0]))};
|
|
|
|
if(!buffer)
|
|
|
|
SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid buffer ID %u",
|
|
|
|
static_cast<ALuint>(values[0]));
|
|
|
|
if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
|
|
|
|
SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
|
|
|
|
"Setting non-persistently mapped buffer %u", buffer->id);
|
2020-11-21 00:54:25 -08:00
|
|
|
if(buffer->mCallback && ReadRef(buffer->ref) != 0)
|
2020-10-13 05:54:05 -07:00
|
|
|
SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
|
|
|
|
"Setting already-set callback buffer %u", buffer->id);
|
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
/* Add the selected buffer to a one-item queue */
|
2020-11-19 04:18:25 -08:00
|
|
|
auto newlist = new BufferlistItem{};
|
2020-11-21 00:54:25 -08:00
|
|
|
newlist->mSampleLen = buffer->mSampleLen;
|
|
|
|
newlist->mBuffer = buffer;
|
2019-09-12 11:33:04 -07:00
|
|
|
IncrementRef(buffer->ref);
|
|
|
|
|
|
|
|
/* Source is now Static */
|
|
|
|
Source->SourceType = AL_STATIC;
|
|
|
|
Source->queue = newlist;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Source is now Undetermined */
|
|
|
|
Source->SourceType = AL_UNDETERMINED;
|
|
|
|
Source->queue = nullptr;
|
|
|
|
}
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
/* Delete all elements in the previous queue */
|
|
|
|
while(oldlist != nullptr)
|
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
std::unique_ptr<BufferlistItem> head{oldlist};
|
|
|
|
oldlist = head->mNext.load(std::memory_order_relaxed);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2020-11-21 00:54:25 -08:00
|
|
|
if(auto *buffer{static_cast<ALbuffer*>(head->mBuffer)})
|
|
|
|
DecrementRef(buffer->ref);
|
2019-09-12 11:33:04 -07:00
|
|
|
}
|
|
|
|
return true;
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SEC_OFFSET:
|
|
|
|
case AL_SAMPLE_OFFSET:
|
|
|
|
case AL_BYTE_OFFSET:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] >= 0);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2020-03-28 17:20:38 -07:00
|
|
|
if(Voice *voice{GetSourceVoice(Source, Context)})
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
2020-10-21 10:39:00 -07:00
|
|
|
if((voice->mFlags&VoiceIsCallback))
|
2020-02-26 03:46:15 -08:00
|
|
|
SETERR_RETURN(Context, AL_INVALID_VALUE, false,
|
|
|
|
"Source offset for callback is invalid");
|
|
|
|
auto vpos = GetSampleOffset(Source->queue, prop, values[0]);
|
|
|
|
if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid source offset");
|
|
|
|
|
|
|
|
if(SetVoiceOffset(voice, *vpos, Source, Context, device))
|
2020-02-26 01:03:23 -08:00
|
|
|
return true;
|
2019-09-12 11:33:04 -07:00
|
|
|
}
|
2020-02-26 01:03:23 -08:00
|
|
|
Source->OffsetType = prop;
|
|
|
|
Source->Offset = values[0];
|
2019-09-12 11:33:04 -07:00
|
|
|
return true;
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_DIRECT_FILTER:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-10-13 05:54:05 -07:00
|
|
|
if(values[0])
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
2020-10-13 05:54:05 -07:00
|
|
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
|
|
|
ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[0]))};
|
|
|
|
if(!filter)
|
|
|
|
SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid filter ID %u",
|
|
|
|
static_cast<ALuint>(values[0]));
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->Direct.Gain = filter->Gain;
|
|
|
|
Source->Direct.GainHF = filter->GainHF;
|
|
|
|
Source->Direct.HFReference = filter->HFReference;
|
|
|
|
Source->Direct.GainLF = filter->GainLF;
|
|
|
|
Source->Direct.LFReference = filter->LFReference;
|
|
|
|
}
|
2020-10-13 05:54:05 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Source->Direct.Gain = 1.0f;
|
|
|
|
Source->Direct.GainHF = 1.0f;
|
|
|
|
Source->Direct.HFReference = LOWPASSFREQREF;
|
|
|
|
Source->Direct.GainLF = 1.0f;
|
|
|
|
Source->Direct.LFReference = HIGHPASSFREQREF;
|
|
|
|
}
|
2019-09-12 11:33:04 -07:00
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_DIRECT_FILTER_GAINHF_AUTO:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-14 20:19:59 -07:00
|
|
|
Source->DryGainHFAuto = values[0] != AL_FALSE;
|
2019-09-12 11:33:04 -07:00
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-14 20:19:59 -07:00
|
|
|
Source->WetGainAuto = values[0] != AL_FALSE;
|
2019-09-12 11:33:04 -07:00
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] == AL_FALSE || values[0] == AL_TRUE);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-14 20:19:59 -07:00
|
|
|
Source->WetGainHFAuto = values[0] != AL_FALSE;
|
2019-09-12 11:33:04 -07:00
|
|
|
return UpdateSourceProps(Source, Context);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_DIRECT_CHANNELS_SOFT:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-11-20 01:37:19 -08:00
|
|
|
if(auto mode = DirectModeFromEnum(values[0]))
|
|
|
|
{
|
|
|
|
Source->DirectChannels = *mode;
|
|
|
|
return UpdateSourceProps(Source, Context);
|
|
|
|
}
|
|
|
|
Context->setError(AL_INVALID_VALUE, "Unsupported AL_DIRECT_CHANNELS_SOFT: 0x%04x\n",
|
|
|
|
values[0]);
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_DISTANCE_MODEL:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] == AL_NONE ||
|
|
|
|
values[0] == AL_INVERSE_DISTANCE || values[0] == AL_INVERSE_DISTANCE_CLAMPED ||
|
|
|
|
values[0] == AL_LINEAR_DISTANCE || values[0] == AL_LINEAR_DISTANCE_CLAMPED ||
|
|
|
|
values[0] == AL_EXPONENT_DISTANCE || values[0] == AL_EXPONENT_DISTANCE_CLAMPED);
|
2017-04-21 15:48:39 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->mDistanceModel = static_cast<DistanceModel>(values[0]);
|
|
|
|
if(Context->mSourceDistanceModel)
|
2019-08-03 18:57:38 -07:00
|
|
|
return UpdateSourceProps(Source, Context);
|
2019-09-12 11:33:04 -07:00
|
|
|
return true;
|
2017-04-21 15:48:39 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SOURCE_RESAMPLER_SOFT:
|
|
|
|
CHECKSIZE(values, 1);
|
2019-09-22 21:19:19 -07:00
|
|
|
CHECKVAL(values[0] >= 0 && values[0] <= static_cast<int>(Resampler::Max));
|
2017-05-05 02:41:34 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->mResampler = static_cast<Resampler>(values[0]);
|
|
|
|
return UpdateSourceProps(Source, Context);
|
2017-05-05 02:41:34 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SOURCE_SPATIALIZE_SOFT:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-11-20 01:37:19 -08:00
|
|
|
if(auto mode = SpatializeModeFromEnum(values[0]))
|
|
|
|
{
|
|
|
|
Source->mSpatialize = *mode;
|
|
|
|
return UpdateSourceProps(Source, Context);
|
|
|
|
}
|
|
|
|
Context->setError(AL_INVALID_VALUE, "Unsupported AL_SOURCE_SPATIALIZE_SOFT: 0x%04x\n",
|
|
|
|
values[0]);
|
|
|
|
return false;
|
2012-08-28 22:16:55 -07:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_AUXILIARY_SEND_FILTER:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
slotlock = std::unique_lock<std::mutex>{Context->mEffectSlotLock};
|
|
|
|
if(values[0] && (slot=LookupEffectSlot(Context, static_cast<ALuint>(values[0]))) == nullptr)
|
|
|
|
SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid effect ID %u", values[0]);
|
2019-09-13 04:15:05 -07:00
|
|
|
if(static_cast<ALuint>(values[1]) >= device->NumAuxSends)
|
2019-09-12 11:33:04 -07:00
|
|
|
SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid send %u", values[1]);
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
|
2020-10-13 05:54:05 -07:00
|
|
|
if(values[2])
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
|
|
|
ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[2]))};
|
|
|
|
if(!filter)
|
|
|
|
SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid filter ID %u", values[2]);
|
2019-09-12 11:33:04 -07:00
|
|
|
|
2020-10-13 05:54:05 -07:00
|
|
|
auto &send = Source->Send[static_cast<ALuint>(values[1])];
|
|
|
|
send.Gain = filter->Gain;
|
|
|
|
send.GainHF = filter->GainHF;
|
|
|
|
send.HFReference = filter->HFReference;
|
|
|
|
send.GainLF = filter->GainLF;
|
|
|
|
send.LFReference = filter->LFReference;
|
|
|
|
}
|
|
|
|
else
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
|
|
|
/* Disable filter */
|
|
|
|
auto &send = Source->Send[static_cast<ALuint>(values[1])];
|
|
|
|
send.Gain = 1.0f;
|
|
|
|
send.GainHF = 1.0f;
|
|
|
|
send.HFReference = LOWPASSFREQREF;
|
|
|
|
send.GainLF = 1.0f;
|
|
|
|
send.LFReference = HIGHPASSFREQREF;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(slot != Source->Send[static_cast<ALuint>(values[1])].Slot && IsPlayingOrPaused(Source))
|
|
|
|
{
|
|
|
|
/* Add refcount on the new slot, and release the previous slot */
|
|
|
|
if(slot) IncrementRef(slot->ref);
|
2019-09-14 18:27:57 -07:00
|
|
|
if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
|
|
|
|
DecrementRef(oldslot->ref);
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
|
|
|
|
|
|
|
|
/* We must force an update if the auxiliary slot changed on an
|
|
|
|
* active source, in case the slot is about to be deleted.
|
|
|
|
*/
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice{GetSourceVoice(Source, Context)};
|
2019-09-12 11:33:04 -07:00
|
|
|
if(voice) UpdateSourceProps(Source, voice, Context);
|
|
|
|
else Source->PropsClean.clear(std::memory_order_release);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(slot) IncrementRef(slot->ref);
|
2019-09-14 18:27:57 -07:00
|
|
|
if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
|
|
|
|
DecrementRef(oldslot->ref);
|
2019-09-12 11:33:04 -07:00
|
|
|
Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
|
|
|
|
UpdateSourceProps(Source, Context);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
/* 1x float */
|
|
|
|
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_DOPPLER_FACTOR:
|
|
|
|
case AL_CONE_OUTER_GAINHF:
|
|
|
|
case AL_AIR_ABSORPTION_FACTOR:
|
|
|
|
case AL_ROOM_ROLLOFF_FACTOR:
|
|
|
|
case AL_SOURCE_RADIUS:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-03-25 22:39:45 -07:00
|
|
|
fvals[0] = static_cast<float>(values[0]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourcefv(Source, Context, prop, {fvals, 1u});
|
|
|
|
|
|
|
|
/* 3x float */
|
|
|
|
case AL_POSITION:
|
|
|
|
case AL_VELOCITY:
|
|
|
|
case AL_DIRECTION:
|
|
|
|
CHECKSIZE(values, 3);
|
2020-03-25 22:39:45 -07:00
|
|
|
fvals[0] = static_cast<float>(values[0]);
|
|
|
|
fvals[1] = static_cast<float>(values[1]);
|
|
|
|
fvals[2] = static_cast<float>(values[2]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourcefv(Source, Context, prop, {fvals, 3u});
|
|
|
|
|
|
|
|
/* 6x float */
|
|
|
|
case AL_ORIENTATION:
|
|
|
|
CHECKSIZE(values, 6);
|
2020-03-25 22:39:45 -07:00
|
|
|
fvals[0] = static_cast<float>(values[0]);
|
|
|
|
fvals[1] = static_cast<float>(values[1]);
|
|
|
|
fvals[2] = static_cast<float>(values[2]);
|
|
|
|
fvals[3] = static_cast<float>(values[3]);
|
|
|
|
fvals[4] = static_cast<float>(values[4]);
|
|
|
|
fvals[5] = static_cast<float>(values[5]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourcefv(Source, Context, prop, {fvals, 6u});
|
|
|
|
|
|
|
|
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SEC_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SEC_OFFSET_CLOCK_SOFT:
|
|
|
|
case AL_SAMPLE_OFFSET_CLOCK_SOFT:
|
|
|
|
case AL_STEREO_ANGLES:
|
|
|
|
break;
|
2012-10-14 01:36:46 -07:00
|
|
|
}
|
|
|
|
|
2012-12-05 09:55:05 -08:00
|
|
|
ERR("Unexpected property: 0x%04x\n", prop);
|
2019-07-30 21:32:05 -07:00
|
|
|
Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
|
2019-08-03 18:57:38 -07:00
|
|
|
return false;
|
2012-10-14 01:36:46 -07:00
|
|
|
}
|
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int64_t> values)
|
2012-10-14 01:36:46 -07:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
float fvals[MaxValues];
|
|
|
|
int ivals[MaxValues];
|
2012-10-14 01:36:46 -07:00
|
|
|
|
2012-12-05 09:55:05 -08:00
|
|
|
switch(prop)
|
2012-10-14 01:36:46 -07:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SOURCE_TYPE:
|
|
|
|
case AL_BUFFERS_QUEUED:
|
|
|
|
case AL_BUFFERS_PROCESSED:
|
|
|
|
case AL_SOURCE_STATE:
|
|
|
|
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SAMPLE_OFFSET_CLOCK_SOFT:
|
|
|
|
/* Query only */
|
|
|
|
SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
|
|
|
|
"Setting read-only source property 0x%04x", prop);
|
|
|
|
|
|
|
|
/* 1x int */
|
|
|
|
case AL_SOURCE_RELATIVE:
|
|
|
|
case AL_LOOPING:
|
|
|
|
case AL_SEC_OFFSET:
|
|
|
|
case AL_SAMPLE_OFFSET:
|
|
|
|
case AL_BYTE_OFFSET:
|
|
|
|
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:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] <= INT_MAX && values[0] >= INT_MIN);
|
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
ivals[0] = static_cast<int>(values[0]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourceiv(Source, Context, prop, {ivals, 1u});
|
|
|
|
|
|
|
|
/* 1x uint */
|
|
|
|
case AL_BUFFER:
|
|
|
|
case AL_DIRECT_FILTER:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0);
|
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
ivals[0] = static_cast<int>(values[0]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourceiv(Source, Context, prop, {ivals, 1u});
|
|
|
|
|
|
|
|
/* 3x uint */
|
|
|
|
case AL_AUXILIARY_SEND_FILTER:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 && values[1] <= UINT_MAX && values[1] >= 0
|
|
|
|
&& values[2] <= UINT_MAX && values[2] >= 0);
|
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
ivals[0] = static_cast<int>(values[0]);
|
|
|
|
ivals[1] = static_cast<int>(values[1]);
|
|
|
|
ivals[2] = static_cast<int>(values[2]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourceiv(Source, Context, prop, {ivals, 3u});
|
|
|
|
|
|
|
|
/* 1x float */
|
|
|
|
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_DOPPLER_FACTOR:
|
|
|
|
case AL_CONE_OUTER_GAINHF:
|
|
|
|
case AL_AIR_ABSORPTION_FACTOR:
|
|
|
|
case AL_ROOM_ROLLOFF_FACTOR:
|
|
|
|
case AL_SOURCE_RADIUS:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-03-25 22:39:45 -07:00
|
|
|
fvals[0] = static_cast<float>(values[0]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourcefv(Source, Context, prop, {fvals, 1u});
|
|
|
|
|
|
|
|
/* 3x float */
|
|
|
|
case AL_POSITION:
|
|
|
|
case AL_VELOCITY:
|
|
|
|
case AL_DIRECTION:
|
|
|
|
CHECKSIZE(values, 3);
|
2020-03-25 22:39:45 -07:00
|
|
|
fvals[0] = static_cast<float>(values[0]);
|
|
|
|
fvals[1] = static_cast<float>(values[1]);
|
|
|
|
fvals[2] = static_cast<float>(values[2]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourcefv(Source, Context, prop, {fvals, 3u});
|
|
|
|
|
|
|
|
/* 6x float */
|
|
|
|
case AL_ORIENTATION:
|
|
|
|
CHECKSIZE(values, 6);
|
2020-03-25 22:39:45 -07:00
|
|
|
fvals[0] = static_cast<float>(values[0]);
|
|
|
|
fvals[1] = static_cast<float>(values[1]);
|
|
|
|
fvals[2] = static_cast<float>(values[2]);
|
|
|
|
fvals[3] = static_cast<float>(values[3]);
|
|
|
|
fvals[4] = static_cast<float>(values[4]);
|
|
|
|
fvals[5] = static_cast<float>(values[5]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return SetSourcefv(Source, Context, prop, {fvals, 6u});
|
|
|
|
|
|
|
|
case AL_SEC_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SEC_OFFSET_CLOCK_SOFT:
|
|
|
|
case AL_STEREO_ANGLES:
|
|
|
|
break;
|
2012-08-28 22:16:55 -07:00
|
|
|
}
|
|
|
|
|
2012-12-05 09:55:05 -08:00
|
|
|
ERR("Unexpected property: 0x%04x\n", prop);
|
2019-07-30 21:32:05 -07:00
|
|
|
Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
|
2019-08-03 19:36:19 -07:00
|
|
|
return false;
|
2012-08-28 22:16:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#undef CHECKVAL
|
|
|
|
|
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values);
|
|
|
|
bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int> values);
|
|
|
|
bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values)
|
2012-08-20 14:16:58 -07:00
|
|
|
{
|
2019-08-01 19:44:09 -07:00
|
|
|
ALCdevice *device{Context->mDevice.get()};
|
2016-05-28 00:43:14 -07:00
|
|
|
ClockLatency clocktime;
|
2019-09-12 11:33:04 -07:00
|
|
|
nanoseconds srcclock;
|
2020-03-25 22:39:45 -07:00
|
|
|
int ivals[MaxValues];
|
2019-08-12 14:30:47 -07:00
|
|
|
bool err;
|
2012-08-20 14:16:58 -07:00
|
|
|
|
2012-12-05 09:22:38 -08:00
|
|
|
switch(prop)
|
2012-08-20 14:16:58 -07:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_GAIN:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->Gain;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_PITCH:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->Pitch;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_MAX_DISTANCE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->MaxDistance;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_ROLLOFF_FACTOR:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->RolloffFactor;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_REFERENCE_DISTANCE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->RefDistance;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_CONE_INNER_ANGLE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->InnerAngle;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_CONE_OUTER_ANGLE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->OuterAngle;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_MIN_GAIN:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->MinGain;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_MAX_GAIN:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->MaxGain;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_CONE_OUTER_GAIN:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->OuterGain;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SEC_OFFSET:
|
|
|
|
case AL_SAMPLE_OFFSET:
|
|
|
|
case AL_BYTE_OFFSET:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = GetSourceOffset(Source, prop, Context);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_CONE_OUTER_GAINHF:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->OuterGainHF;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_AIR_ABSORPTION_FACTOR:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->AirAbsorptionFactor;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_ROOM_ROLLOFF_FACTOR:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->RoomRolloffFactor;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_DOPPLER_FACTOR:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->DopplerFactor;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SOURCE_RADIUS:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->Radius;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_STEREO_ANGLES:
|
|
|
|
CHECKSIZE(values, 2);
|
|
|
|
values[0] = Source->StereoPan[0];
|
|
|
|
values[1] = Source->StereoPan[1];
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SEC_OFFSET_LATENCY_SOFT:
|
|
|
|
CHECKSIZE(values, 2);
|
|
|
|
/* 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<std::mutex> _{device->StateLock};
|
|
|
|
clocktime = GetClockLatency(device);
|
|
|
|
}
|
|
|
|
if(srcclock == clocktime.ClockTime)
|
2020-03-25 22:39:45 -07:00
|
|
|
values[1] = static_cast<double>(clocktime.Latency.count()) / 1000000000.0;
|
2019-09-12 11:33:04 -07:00
|
|
|
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.
|
2016-05-28 04:11:57 -07:00
|
|
|
*/
|
2019-09-12 11:33:04 -07:00
|
|
|
const nanoseconds diff{clocktime.ClockTime - srcclock};
|
|
|
|
const nanoseconds latency{clocktime.Latency - std::min(clocktime.Latency, diff)};
|
2020-03-25 22:39:45 -07:00
|
|
|
values[1] = static_cast<double>(latency.count()) / 1000000000.0;
|
2019-09-12 11:33:04 -07:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SEC_OFFSET_CLOCK_SOFT:
|
|
|
|
CHECKSIZE(values, 2);
|
|
|
|
values[0] = GetSourceSecOffset(Source, Context, &srcclock);
|
2020-03-25 22:39:45 -07:00
|
|
|
values[1] = static_cast<double>(srcclock.count()) / 1000000000.0;
|
2019-09-12 11:33:04 -07:00
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_POSITION:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
values[0] = Source->Position[0];
|
|
|
|
values[1] = Source->Position[1];
|
|
|
|
values[2] = Source->Position[2];
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_VELOCITY:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
values[0] = Source->Velocity[0];
|
|
|
|
values[1] = Source->Velocity[1];
|
|
|
|
values[2] = Source->Velocity[2];
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_DIRECTION:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
values[0] = Source->Direction[0];
|
|
|
|
values[1] = Source->Direction[1];
|
|
|
|
values[2] = Source->Direction[2];
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_ORIENTATION:
|
|
|
|
CHECKSIZE(values, 6);
|
|
|
|
values[0] = Source->OrientAt[0];
|
|
|
|
values[1] = Source->OrientAt[1];
|
|
|
|
values[2] = Source->OrientAt[2];
|
|
|
|
values[3] = Source->OrientUp[0];
|
|
|
|
values[4] = Source->OrientUp[1];
|
|
|
|
values[5] = Source->OrientUp[2];
|
|
|
|
return 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:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
|
2020-03-25 22:39:45 -07:00
|
|
|
values[0] = static_cast<double>(ivals[0]);
|
2019-09-12 11:33:04 -07:00
|
|
|
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;
|
2012-08-20 14:16:58 -07:00
|
|
|
}
|
|
|
|
|
2012-12-05 09:55:05 -08:00
|
|
|
ERR("Unexpected property: 0x%04x\n", prop);
|
2019-07-30 21:32:05 -07:00
|
|
|
Context->setError(AL_INVALID_ENUM, "Invalid source double property 0x%04x", prop);
|
2019-08-03 19:36:19 -07:00
|
|
|
return false;
|
2012-08-20 14:16:58 -07:00
|
|
|
}
|
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int> values)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
double dvals[MaxValues];
|
2019-08-12 14:30:47 -07:00
|
|
|
bool err;
|
2009-08-16 15:09:36 -07:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
switch(prop)
|
2018-11-21 11:11:25 -08:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SOURCE_RELATIVE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->HeadRelative;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_LOOPING:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->Looping;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_BUFFER:
|
|
|
|
CHECKSIZE(values, 1);
|
2019-09-14 18:27:57 -07:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
BufferlistItem *BufferList{(Source->SourceType==AL_STATIC) ? Source->queue : nullptr};
|
|
|
|
ALbuffer *buffer{BufferList ? static_cast<ALbuffer*>(BufferList->mBuffer) : nullptr};
|
|
|
|
values[0] = buffer ? static_cast<int>(buffer->id) : 0;
|
2019-09-14 18:27:57 -07:00
|
|
|
}
|
2019-09-12 11:33:04 -07:00
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SOURCE_STATE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = GetSourceState(Source, GetSourceVoice(Source, Context));
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_BUFFERS_QUEUED:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-11-19 04:18:25 -08:00
|
|
|
if(BufferlistItem *BufferList{Source->queue})
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
|
|
|
ALsizei count{0};
|
|
|
|
do {
|
|
|
|
++count;
|
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
|
|
|
} while(BufferList != nullptr);
|
|
|
|
values[0] = count;
|
|
|
|
}
|
2019-09-14 18:27:57 -07:00
|
|
|
else
|
|
|
|
values[0] = 0;
|
2019-09-12 11:33:04 -07:00
|
|
|
return true;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_BUFFERS_PROCESSED:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
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] = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-11-19 04:18:25 -08:00
|
|
|
const BufferlistItem *BufferList{Source->queue};
|
|
|
|
const BufferlistItem *Current{nullptr};
|
2019-09-12 11:33:04 -07:00
|
|
|
ALsizei played{0};
|
|
|
|
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice{GetSourceVoice(Source, Context)};
|
2019-09-12 11:33:04 -07:00
|
|
|
if(voice != nullptr)
|
|
|
|
Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
|
|
|
|
else if(Source->state == AL_INITIAL)
|
|
|
|
Current = BufferList;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
while(BufferList && BufferList != Current)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
++played;
|
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-09-12 11:33:04 -07:00
|
|
|
values[0] = played;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SOURCE_TYPE:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->SourceType;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_DIRECT_FILTER_GAINHF_AUTO:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->DryGainHFAuto;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->WetGainAuto;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = Source->WetGainHFAuto;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_DIRECT_CHANNELS_SOFT:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-11-20 01:37:19 -08:00
|
|
|
values[0] = EnumFromDirectMode(Source->DirectChannels);
|
2019-09-12 11:33:04 -07:00
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_DISTANCE_MODEL:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
values[0] = static_cast<int>(Source->mDistanceModel);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SOURCE_RESAMPLER_SOFT:
|
|
|
|
CHECKSIZE(values, 1);
|
2019-09-22 21:19:19 -07:00
|
|
|
values[0] = static_cast<int>(Source->mResampler);
|
2019-09-12 11:33:04 -07:00
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SOURCE_SPATIALIZE_SOFT:
|
|
|
|
CHECKSIZE(values, 1);
|
2020-11-20 01:37:19 -08:00
|
|
|
values[0] = EnumFromSpatializeMode(Source->mSpatialize);
|
2019-09-12 11:33:04 -07:00
|
|
|
return 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:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
|
2020-03-25 22:39:45 -07:00
|
|
|
values[0] = static_cast<int>(dvals[0]);
|
2019-09-12 11:33:04 -07:00
|
|
|
return err;
|
|
|
|
|
|
|
|
/* 3x float/double */
|
|
|
|
case AL_POSITION:
|
|
|
|
case AL_VELOCITY:
|
|
|
|
case AL_DIRECTION:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
|
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
values[0] = static_cast<int>(dvals[0]);
|
|
|
|
values[1] = static_cast<int>(dvals[1]);
|
|
|
|
values[2] = static_cast<int>(dvals[2]);
|
2019-09-12 11:33:04 -07:00
|
|
|
}
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* 6x float/double */
|
|
|
|
case AL_ORIENTATION:
|
|
|
|
CHECKSIZE(values, 6);
|
|
|
|
if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
|
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
values[0] = static_cast<int>(dvals[0]);
|
|
|
|
values[1] = static_cast<int>(dvals[1]);
|
|
|
|
values[2] = static_cast<int>(dvals[2]);
|
|
|
|
values[3] = static_cast<int>(dvals[3]);
|
|
|
|
values[4] = static_cast<int>(dvals[4]);
|
|
|
|
values[5] = static_cast<int>(dvals[5]);
|
2019-09-12 11:33:04 -07:00
|
|
|
}
|
|
|
|
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; /* ??? */
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2018-11-21 11:11:25 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
ERR("Unexpected property: 0x%04x\n", prop);
|
2019-07-30 21:32:05 -07:00
|
|
|
Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
|
2019-08-03 19:36:19 -07:00
|
|
|
return false;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2020-03-25 22:39:45 -07:00
|
|
|
bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2019-08-01 19:44:09 -07:00
|
|
|
ALCdevice *device = Context->mDevice.get();
|
2018-11-22 12:02:02 -08:00
|
|
|
ClockLatency clocktime;
|
2019-09-12 11:33:04 -07:00
|
|
|
nanoseconds srcclock;
|
2020-03-25 22:39:45 -07:00
|
|
|
double dvals[MaxValues];
|
|
|
|
int ivals[MaxValues];
|
2019-08-12 14:30:47 -07:00
|
|
|
bool err;
|
2018-11-21 11:11:25 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
switch(prop)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
|
|
|
CHECKSIZE(values, 2);
|
|
|
|
/* 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<std::mutex> _{device->StateLock};
|
|
|
|
clocktime = GetClockLatency(device);
|
|
|
|
}
|
|
|
|
if(srcclock == clocktime.ClockTime)
|
|
|
|
values[1] = clocktime.Latency.count();
|
|
|
|
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.
|
2018-11-22 12:02:02 -08:00
|
|
|
*/
|
2019-09-12 11:33:04 -07:00
|
|
|
const nanoseconds diff{clocktime.ClockTime - srcclock};
|
|
|
|
values[1] = nanoseconds{clocktime.Latency - std::min(clocktime.Latency, diff)}.count();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case AL_SAMPLE_OFFSET_CLOCK_SOFT:
|
|
|
|
CHECKSIZE(values, 2);
|
|
|
|
values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
|
|
|
|
values[1] = srcclock.count();
|
|
|
|
return 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:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
|
|
|
|
values[0] = static_cast<int64_t>(dvals[0]);
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* 3x float/double */
|
|
|
|
case AL_POSITION:
|
|
|
|
case AL_VELOCITY:
|
|
|
|
case AL_DIRECTION:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
|
|
|
|
{
|
|
|
|
values[0] = static_cast<int64_t>(dvals[0]);
|
|
|
|
values[1] = static_cast<int64_t>(dvals[1]);
|
|
|
|
values[2] = static_cast<int64_t>(dvals[2]);
|
|
|
|
}
|
|
|
|
return err;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
/* 6x float/double */
|
|
|
|
case AL_ORIENTATION:
|
|
|
|
CHECKSIZE(values, 6);
|
|
|
|
if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
|
|
|
|
{
|
|
|
|
values[0] = static_cast<int64_t>(dvals[0]);
|
|
|
|
values[1] = static_cast<int64_t>(dvals[1]);
|
|
|
|
values[2] = static_cast<int64_t>(dvals[2]);
|
|
|
|
values[3] = static_cast<int64_t>(dvals[3]);
|
|
|
|
values[4] = static_cast<int64_t>(dvals[4]);
|
|
|
|
values[5] = static_cast<int64_t>(dvals[5]);
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* 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:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
|
|
|
|
values[0] = ivals[0];
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* 1x uint */
|
|
|
|
case AL_BUFFER:
|
|
|
|
case AL_DIRECT_FILTER:
|
|
|
|
CHECKSIZE(values, 1);
|
|
|
|
if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
|
|
|
|
values[0] = static_cast<ALuint>(ivals[0]);
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* 3x uint */
|
|
|
|
case AL_AUXILIARY_SEND_FILTER:
|
|
|
|
CHECKSIZE(values, 3);
|
|
|
|
if((err=GetSourceiv(Source, Context, prop, {ivals, 3u})) != false)
|
|
|
|
{
|
|
|
|
values[0] = static_cast<ALuint>(ivals[0]);
|
|
|
|
values[1] = static_cast<ALuint>(ivals[1]);
|
|
|
|
values[2] = static_cast<ALuint>(ivals[2]);
|
|
|
|
}
|
|
|
|
return err;
|
2018-11-21 11:11:25 -08:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
case AL_SEC_OFFSET_LATENCY_SOFT:
|
|
|
|
case AL_SEC_OFFSET_CLOCK_SOFT:
|
|
|
|
break; /* Double only */
|
|
|
|
case AL_STEREO_ANGLES:
|
|
|
|
break; /* Float/double only */
|
2012-10-13 00:56:39 -07:00
|
|
|
}
|
2018-11-21 11:11:25 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
ERR("Unexpected property: 0x%04x\n", prop);
|
2019-07-30 21:32:05 -07:00
|
|
|
Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
|
2019-08-03 19:36:19 -07:00
|
|
|
return false;
|
2012-10-13 00:56:39 -07:00
|
|
|
}
|
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
} // namespace
|
2012-10-13 00:56:39 -07:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(n < 0)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "Generating %d sources", n);
|
2019-09-12 11:33:04 -07:00
|
|
|
if UNLIKELY(n <= 0) return;
|
|
|
|
|
|
|
|
std::unique_lock<std::mutex> srclock{context->mSourceLock};
|
|
|
|
ALCdevice *device{context->mDevice.get()};
|
2019-09-12 12:19:07 -07:00
|
|
|
if(static_cast<ALuint>(n) > device->SourcesMax-context->mNumSources)
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
2019-09-12 12:19:07 -07:00
|
|
|
context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)",
|
|
|
|
device->SourcesMax, context->mNumSources, n);
|
2019-09-12 11:33:04 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(!EnsureSources(context.get(), static_cast<ALuint>(n)))
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, (n==1)?"":"s");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(n == 1)
|
|
|
|
{
|
2020-02-25 06:39:03 -08:00
|
|
|
ALsource *source{AllocSource(context.get())};
|
2019-09-12 11:33:04 -07:00
|
|
|
sources[0] = source->id;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2012-12-05 20:51:25 -08:00
|
|
|
else
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
al::vector<ALuint> ids;
|
|
|
|
ids.reserve(static_cast<ALuint>(n));
|
|
|
|
do {
|
2020-02-25 06:39:03 -08:00
|
|
|
ALsource *source{AllocSource(context.get())};
|
2019-09-12 11:33:04 -07:00
|
|
|
ids.emplace_back(source->id);
|
|
|
|
} while(--n);
|
|
|
|
std::copy(ids.cbegin(), ids.cend(), sources);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2011-06-16 09:14:41 -07:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(n < 0)
|
2019-07-30 21:32:05 -07:00
|
|
|
SETERR_RETURN(context, AL_INVALID_VALUE,, "Deleting %d sources", n);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
/* Check that all Sources are valid */
|
2019-09-12 17:10:33 -07:00
|
|
|
auto validate_source = [&context](const ALuint sid) -> bool
|
2019-09-12 11:33:04 -07:00
|
|
|
{ return LookupSource(context.get(), sid) != nullptr; };
|
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
const ALuint *sources_end = sources + n;
|
2019-09-12 11:33:04 -07:00
|
|
|
auto invsrc = std::find_if_not(sources, sources_end, validate_source);
|
|
|
|
if UNLIKELY(invsrc != sources_end)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", *invsrc);
|
|
|
|
return;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-09-12 11:33:04 -07:00
|
|
|
|
|
|
|
/* All good. Delete source IDs. */
|
2019-09-12 17:10:33 -07:00
|
|
|
auto delete_source = [&context](const ALuint sid) -> void
|
2019-09-12 11:33:04 -07:00
|
|
|
{
|
|
|
|
ALsource *src{LookupSource(context.get(), sid)};
|
|
|
|
if(src) FreeSource(context.get(), src);
|
|
|
|
};
|
|
|
|
std::for_each(sources, sources_end, delete_source);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2012-08-20 15:26:35 -07:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if LIKELY(context)
|
2012-08-20 15:26:35 -07:00
|
|
|
{
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
if(LookupSource(context.get(), source) != nullptr)
|
|
|
|
return AL_TRUE;
|
2012-08-20 15:26:35 -07:00
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
return AL_FALSE;
|
2012-08-20 15:26:35 -07:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2012-08-20 15:26:35 -07:00
|
|
|
|
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source = LookupSource(context.get(), source);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2012-12-05 19:58:01 -08:00
|
|
|
else
|
2019-08-03 18:57:38 -07:00
|
|
|
SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source = LookupSource(context.get(), source);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2012-12-05 19:58:01 -08:00
|
|
|
else
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
const float fvals[3]{ value1, value2, value3 };
|
2018-11-22 12:02:02 -08:00
|
|
|
SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2011-06-16 09:14:41 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source = LookupSource(context.get(), source);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!values)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2012-12-05 19:58:01 -08:00
|
|
|
else
|
2019-08-11 22:48:18 -07:00
|
|
|
SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2012-08-18 11:02:54 -07:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2012-08-18 11:02:54 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source = LookupSource(context.get(), source);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2012-12-05 19:58:01 -08:00
|
|
|
else
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
const float fval[1]{static_cast<float>(value)};
|
2019-08-04 11:59:14 -07:00
|
|
|
SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fval);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2012-08-20 14:16:58 -07:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2012-08-20 14:16:58 -07:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2012-08-20 14:16:58 -07:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2012-08-20 14:16:58 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source = LookupSource(context.get(), source);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-03 18:57:38 -07:00
|
|
|
else
|
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
const float fvals[3]{static_cast<float>(value1), static_cast<float>(value2),
|
|
|
|
static_cast<float>(value3)};
|
2018-11-22 12:02:02 -08:00
|
|
|
SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
|
2012-08-18 11:02:54 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2012-08-18 11:02:54 -07:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2012-08-18 11:02:54 -07:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2012-08-18 11:02:54 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source = LookupSource(context.get(), source);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!values)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2012-12-05 19:58:01 -08:00
|
|
|
else
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2019-08-03 19:36:19 -07:00
|
|
|
const ALuint count{DoubleValsByProp(param)};
|
2020-03-25 22:39:45 -07:00
|
|
|
float fvals[MaxValues];
|
2019-08-03 19:36:19 -07:00
|
|
|
for(ALuint i{0};i < count;i++)
|
2020-03-25 22:39:45 -07:00
|
|
|
fvals[i] = static_cast<float>(values[i]);
|
2019-08-03 19:36:19 -07:00
|
|
|
SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {fvals, count});
|
2013-10-07 09:24:50 -07:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2009-08-16 15:09:36 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source = LookupSource(context.get(), source);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-03 18:57:38 -07:00
|
|
|
SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-11-21 11:11:25 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2010-03-24 02:23:00 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source = LookupSource(context.get(), source);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2016-08-08 22:31:08 -07:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
const int ivals[3]{ value1, value2, value3 };
|
2018-11-22 12:02:02 -08:00
|
|
|
SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-22 12:02:02 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source = LookupSource(context.get(), source);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!values)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-11 22:48:18 -07:00
|
|
|
SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-03 18:57:38 -07:00
|
|
|
SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-11-21 11:11:25 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2010-03-24 02:23:00 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2013-10-07 09:24:50 -07:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
const int64_t i64vals[3]{ value1, value2, value3 };
|
2018-11-22 12:02:02 -08:00
|
|
|
SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2009-08-16 15:09:36 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
|
|
std::lock_guard<std::mutex> __{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!values)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-11 22:48:18 -07:00
|
|
|
SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2009-08-16 15:09:36 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!value)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2013-10-07 09:24:50 -07:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
double dval[1];
|
2019-08-04 11:59:14 -07:00
|
|
|
if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dval))
|
2020-03-25 22:39:45 -07:00
|
|
|
*value = static_cast<float>(dval[0]);
|
2013-10-07 09:24:50 -07:00
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2012-04-23 19:46:05 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!(value1 && value2 && value3))
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
double dvals[3];
|
2018-11-22 12:02:02 -08:00
|
|
|
if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
|
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
*value1 = static_cast<float>(dvals[0]);
|
|
|
|
*value2 = static_cast<float>(dvals[1]);
|
|
|
|
*value3 = static_cast<float>(dvals[2]);
|
2011-09-11 03:57:40 -07:00
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2014-05-10 08:55:28 -07:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-01-20 11:49:01 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!values)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
|
|
|
{
|
2019-08-03 19:36:19 -07:00
|
|
|
const ALuint count{FloatValsByProp(param)};
|
2020-03-25 22:39:45 -07:00
|
|
|
double dvals[MaxValues];
|
2019-08-03 19:36:19 -07:00
|
|
|
if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {dvals, count}))
|
2018-05-21 23:25:56 -07:00
|
|
|
{
|
2019-08-03 19:36:19 -07:00
|
|
|
for(ALuint i{0};i < count;i++)
|
2020-03-25 22:39:45 -07:00
|
|
|
values[i] = static_cast<float>(dvals[i]);
|
2018-05-21 23:25:56 -07:00
|
|
|
}
|
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-05-21 23:25:56 -07:00
|
|
|
|
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!value)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-03 19:36:19 -07:00
|
|
|
GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!(value1 && value2 && value3))
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-05-21 23:25:56 -07:00
|
|
|
else
|
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
double dvals[3];
|
2018-11-22 12:02:02 -08:00
|
|
|
if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
|
|
|
|
{
|
|
|
|
*value1 = dvals[0];
|
|
|
|
*value2 = dvals[1];
|
|
|
|
*value3 = dvals[2];
|
|
|
|
}
|
2018-05-21 23:25:56 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-05-21 23:25:56 -07:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-05-21 23:25:56 -07:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-05-21 23:25:56 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!values)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-11 22:48:18 -07:00
|
|
|
GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-05-21 23:25:56 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!value)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-03 19:36:19 -07:00
|
|
|
GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-05-21 23:25:56 -07:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!(value1 && value2 && value3))
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2018-05-21 23:25:56 -07:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
int ivals[3];
|
2018-11-22 12:02:02 -08:00
|
|
|
if(GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals))
|
2018-05-21 23:25:56 -07:00
|
|
|
{
|
2018-11-22 12:02:02 -08:00
|
|
|
*value1 = ivals[0];
|
|
|
|
*value2 = ivals[1];
|
|
|
|
*value3 = ivals[2];
|
2018-05-21 23:25:56 -07:00
|
|
|
}
|
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-05-21 23:25:56 -07:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-05-21 23:25:56 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!values)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-11 22:48:18 -07:00
|
|
|
GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2018-05-21 23:25:56 -07:00
|
|
|
|
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2014-05-10 08:55:28 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!value)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-03 19:36:19 -07:00
|
|
|
GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2010-03-24 02:23:00 -07:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!(value1 && value2 && value3))
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2017-04-18 00:58:33 -07:00
|
|
|
else
|
2013-10-07 09:24:50 -07:00
|
|
|
{
|
2020-03-25 22:39:45 -07:00
|
|
|
int64_t i64vals[3];
|
2018-11-22 12:02:02 -08:00
|
|
|
if(GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals))
|
|
|
|
{
|
|
|
|
*value1 = i64vals[0];
|
|
|
|
*value2 = i64vals[1];
|
|
|
|
*value3 = i64vals[2];
|
|
|
|
}
|
2013-10-07 09:24:50 -07:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 11:11:25 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *Source{LookupSource(context.get(), source)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!Source)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
|
2019-08-04 11:59:14 -07:00
|
|
|
else if UNLIKELY(!values)
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
2018-11-22 12:02:02 -08:00
|
|
|
else
|
2019-08-11 22:48:18 -07:00
|
|
|
GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, MaxValues});
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2017-02-27 15:35:15 -08:00
|
|
|
|
2016-07-31 23:42:30 -07:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcePlay(ALuint source)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
|
|
|
{ alSourcePlayv(1, &source); }
|
|
|
|
END_API_FUNC
|
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(n < 0)
|
|
|
|
context->setError(AL_INVALID_VALUE, "Playing %d sources", n);
|
|
|
|
if UNLIKELY(n <= 0) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-06 14:59:26 -07:00
|
|
|
al::vector<ALsource*> extra_sources;
|
2019-09-25 03:01:58 -07:00
|
|
|
std::array<ALsource*,8> source_storage;
|
|
|
|
al::span<ALsource*> srchandles;
|
|
|
|
if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
|
|
|
|
srchandles = {source_storage.data(), static_cast<ALuint>(n)};
|
|
|
|
else
|
2019-07-06 14:59:26 -07:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
extra_sources.resize(static_cast<ALuint>(n));
|
2019-09-25 03:01:58 -07:00
|
|
|
srchandles = {extra_sources.data(), extra_sources.size()};
|
2019-07-06 14:59:26 -07:00
|
|
|
}
|
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2019-09-25 03:01:58 -07:00
|
|
|
for(auto &srchdl : srchandles)
|
2019-07-06 14:59:26 -07:00
|
|
|
{
|
2019-09-25 03:01:58 -07:00
|
|
|
srchdl = LookupSource(context.get(), *sources);
|
|
|
|
if(!srchdl)
|
|
|
|
SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
|
|
|
|
++sources;
|
2019-07-06 14:59:26 -07:00
|
|
|
}
|
2014-05-10 05:07:13 -07:00
|
|
|
|
2019-08-01 19:44:09 -07:00
|
|
|
ALCdevice *device{context->mDevice.get()};
|
2018-11-22 12:02:02 -08:00
|
|
|
/* If the device is disconnected, go right to stopped. */
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!device->Connected.load(std::memory_order_acquire))
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
/* TODO: Send state change event? */
|
2020-02-21 04:29:32 -08:00
|
|
|
for(ALsource *source : srchandles)
|
|
|
|
{
|
|
|
|
source->Offset = 0.0;
|
|
|
|
source->OffsetType = AL_NONE;
|
|
|
|
source->state = AL_STOPPED;
|
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
return;
|
2018-02-25 09:51:07 -08:00
|
|
|
}
|
|
|
|
|
2019-03-09 15:04:51 -08:00
|
|
|
/* Count the number of reusable voices. */
|
2020-02-21 20:14:28 -08:00
|
|
|
auto voicelist = context->getVoicesSpan();
|
2020-02-21 00:54:05 -08:00
|
|
|
size_t free_voices{0};
|
2020-03-28 17:20:38 -07:00
|
|
|
for(const Voice *voice : voicelist)
|
2019-09-04 23:04:55 -07:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
free_voices += (voice->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
|
2020-02-21 20:14:28 -08:00
|
|
|
&& voice->mSourceID.load(std::memory_order_relaxed) == 0u
|
2020-02-26 01:48:59 -08:00
|
|
|
&& voice->mPendingChange.load(std::memory_order_relaxed) == false);
|
2020-02-21 00:54:05 -08:00
|
|
|
if(free_voices == srchandles.size())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if UNLIKELY(srchandles.size() != free_voices)
|
2018-02-25 09:51:07 -08:00
|
|
|
{
|
2020-02-21 20:14:28 -08:00
|
|
|
const size_t inc_amount{srchandles.size() - free_voices};
|
|
|
|
auto &allvoices = *context->mVoices.load(std::memory_order_relaxed);
|
|
|
|
if(inc_amount > allvoices.size() - voicelist.size())
|
|
|
|
{
|
|
|
|
/* Increase the number of voices to handle the request. */
|
2020-02-22 18:01:34 -08:00
|
|
|
context->allocVoices(inc_amount - (allvoices.size() - voicelist.size()));
|
2020-02-21 20:14:28 -08:00
|
|
|
}
|
|
|
|
context->mActiveVoiceCount.fetch_add(inc_amount, std::memory_order_release);
|
|
|
|
voicelist = context->getVoicesSpan();
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
|
2020-02-24 21:40:54 -08:00
|
|
|
auto voiceiter = voicelist.begin();
|
|
|
|
ALuint vidx{0};
|
2020-02-21 04:29:32 -08:00
|
|
|
VoiceChange *tail{}, *cur{};
|
|
|
|
for(ALsource *source : srchandles)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
/* Check that there is a queue containing at least one valid, non zero
|
|
|
|
* length buffer.
|
|
|
|
*/
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *BufferList{source->queue};
|
2019-08-11 03:34:35 -07:00
|
|
|
while(BufferList && BufferList->mSampleLen == 0)
|
2020-02-17 17:34:17 -08:00
|
|
|
{
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferStorage *buffer{BufferList->mBuffer};
|
|
|
|
if(buffer && buffer->mCallback) break;
|
2019-07-31 09:20:53 -07:00
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
2020-02-17 17:34:17 -08:00
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
/* If there's nothing to play, go right to stopped. */
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!BufferList)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
/* NOTE: A source without any playable buffers should not have a
|
|
|
|
* Voice since it shouldn't be in a playing or paused state. So
|
2018-11-22 12:02:02 -08:00
|
|
|
* there's no need to look up its voice and clear the source.
|
2018-02-25 09:51:07 -08:00
|
|
|
*/
|
2018-11-22 12:02:02 -08:00
|
|
|
source->Offset = 0.0;
|
2020-02-21 04:29:32 -08:00
|
|
|
source->OffsetType = AL_NONE;
|
|
|
|
source->state = AL_STOPPED;
|
|
|
|
continue;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2010-03-24 02:23:00 -07:00
|
|
|
|
2020-02-21 04:29:32 -08:00
|
|
|
if(!cur)
|
|
|
|
cur = tail = GetVoiceChanger(context.get());
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
|
|
|
|
cur = cur->mNext.load(std::memory_order_relaxed);
|
|
|
|
}
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice{GetSourceVoice(source, context.get())};
|
2018-11-22 12:02:02 -08:00
|
|
|
switch(GetSourceState(source, voice))
|
|
|
|
{
|
2018-12-03 01:41:52 -08:00
|
|
|
case AL_PAUSED:
|
2020-02-24 22:21:44 -08:00
|
|
|
/* A source that's paused simply resumes. If there's no voice, it
|
|
|
|
* was lost from a disconnect, so just start over with a new one.
|
|
|
|
*/
|
2020-02-23 21:40:54 -08:00
|
|
|
cur->mOldVoice = nullptr;
|
2020-02-24 22:21:44 -08:00
|
|
|
if(!voice) break;
|
2020-02-21 04:29:32 -08:00
|
|
|
cur->mVoice = voice;
|
|
|
|
cur->mSourceID = source->id;
|
|
|
|
cur->mState = AL_PLAYING;
|
2018-12-03 01:41:52 -08:00
|
|
|
source->state = AL_PLAYING;
|
2020-02-21 04:29:32 -08:00
|
|
|
continue;
|
2018-12-03 01:41:52 -08:00
|
|
|
|
2019-09-06 21:20:20 -07:00
|
|
|
case AL_PLAYING:
|
|
|
|
/* A source that's already playing is restarted from the beginning.
|
|
|
|
* Stop the current voice and start a new one so it properly cross-
|
|
|
|
* fades back to the beginning.
|
|
|
|
*/
|
2020-02-24 22:21:44 -08:00
|
|
|
if(voice)
|
2020-02-26 01:48:59 -08:00
|
|
|
voice->mPendingChange.store(true, std::memory_order_relaxed);
|
2020-02-23 21:40:54 -08:00
|
|
|
cur->mOldVoice = voice;
|
2019-09-06 21:20:20 -07:00
|
|
|
voice = nullptr;
|
|
|
|
break;
|
|
|
|
|
2018-12-03 01:41:52 -08:00
|
|
|
default:
|
|
|
|
assert(voice == nullptr);
|
2020-02-23 21:40:54 -08:00
|
|
|
cur->mOldVoice = nullptr;
|
2018-12-03 01:41:52 -08:00
|
|
|
break;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2018-11-20 12:25:15 -08:00
|
|
|
|
2020-02-24 21:40:54 -08:00
|
|
|
/* Find the next unused voice to play this source with. */
|
|
|
|
for(;voiceiter != voicelist.end();++voiceiter,++vidx)
|
2019-09-16 07:16:31 -07:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *v{*voiceiter};
|
|
|
|
if(v->mPlayState.load(std::memory_order_acquire) == Voice::Stopped
|
2020-02-21 20:14:28 -08:00
|
|
|
&& v->mSourceID.load(std::memory_order_relaxed) == 0u
|
2020-02-26 01:48:59 -08:00
|
|
|
&& v->mPendingChange.load(std::memory_order_relaxed) == false)
|
2020-02-21 20:14:28 -08:00
|
|
|
{
|
|
|
|
voice = v;
|
|
|
|
break;
|
|
|
|
}
|
2020-02-24 22:21:44 -08:00
|
|
|
}
|
2018-11-20 12:25:15 -08:00
|
|
|
|
2020-03-04 13:40:21 -08:00
|
|
|
voice->mPosition.store(0u, std::memory_order_relaxed);
|
|
|
|
voice->mPositionFrac.store(0, std::memory_order_relaxed);
|
|
|
|
voice->mCurrentBuffer.store(source->queue, std::memory_order_relaxed);
|
2020-03-04 11:01:35 -08:00
|
|
|
voice->mFlags = 0;
|
2020-03-04 13:40:21 -08:00
|
|
|
/* A source that's not playing or paused has any offset applied when it
|
|
|
|
* starts playing.
|
|
|
|
*/
|
|
|
|
if(const ALenum offsettype{source->OffsetType})
|
|
|
|
{
|
|
|
|
const double offset{source->Offset};
|
|
|
|
source->OffsetType = AL_NONE;
|
|
|
|
source->Offset = 0.0;
|
|
|
|
if(auto vpos = GetSampleOffset(BufferList, offsettype, offset))
|
|
|
|
{
|
|
|
|
voice->mPosition.store(vpos->pos, std::memory_order_relaxed);
|
|
|
|
voice->mPositionFrac.store(vpos->frac, std::memory_order_relaxed);
|
|
|
|
voice->mCurrentBuffer.store(vpos->bufferitem, std::memory_order_relaxed);
|
|
|
|
if(vpos->pos != 0 || vpos->frac != 0 || vpos->bufferitem != source->queue)
|
2020-10-21 10:39:00 -07:00
|
|
|
voice->mFlags |= VoiceIsFading;
|
2020-03-04 13:40:21 -08:00
|
|
|
}
|
|
|
|
}
|
2020-02-26 01:39:30 -08:00
|
|
|
InitVoice(voice, source, BufferList, context.get(), device);
|
2020-02-21 04:55:04 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
source->VoiceIdx = vidx;
|
2020-02-21 04:29:32 -08:00
|
|
|
source->state = AL_PLAYING;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2020-02-21 04:29:32 -08:00
|
|
|
cur->mVoice = voice;
|
|
|
|
cur->mSourceID = source->id;
|
|
|
|
cur->mState = AL_PLAYING;
|
|
|
|
}
|
|
|
|
if LIKELY(tail)
|
|
|
|
SendVoiceChanges(context.get(), tail);
|
2018-11-20 12:25:15 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
|
|
|
|
2018-11-20 12:25:15 -08:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcePause(ALuint source)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
|
|
|
{ alSourcePausev(1, &source); }
|
|
|
|
END_API_FUNC
|
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(n < 0)
|
|
|
|
context->setError(AL_INVALID_VALUE, "Pausing %d sources", n);
|
|
|
|
if UNLIKELY(n <= 0) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-06 14:59:26 -07:00
|
|
|
al::vector<ALsource*> extra_sources;
|
2019-09-25 03:01:58 -07:00
|
|
|
std::array<ALsource*,8> source_storage;
|
|
|
|
al::span<ALsource*> srchandles;
|
|
|
|
if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
|
|
|
|
srchandles = {source_storage.data(), static_cast<ALuint>(n)};
|
|
|
|
else
|
2019-07-06 14:59:26 -07:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
extra_sources.resize(static_cast<ALuint>(n));
|
2019-09-25 03:01:58 -07:00
|
|
|
srchandles = {extra_sources.data(), extra_sources.size()};
|
2019-07-06 14:59:26 -07:00
|
|
|
}
|
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2019-09-25 03:01:58 -07:00
|
|
|
for(auto &srchdl : srchandles)
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
{
|
2019-09-25 03:01:58 -07:00
|
|
|
srchdl = LookupSource(context.get(), *sources);
|
|
|
|
if(!srchdl)
|
|
|
|
SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
|
|
|
|
++sources;
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
}
|
|
|
|
|
2020-02-21 04:29:32 -08:00
|
|
|
/* Pausing has to be done in two steps. First, for each source that's
|
|
|
|
* detected to be playing, chamge the voice (asynchronously) to
|
|
|
|
* stopping/paused.
|
|
|
|
*/
|
|
|
|
VoiceChange *tail{}, *cur{};
|
|
|
|
for(ALsource *source : srchandles)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice{GetSourceVoice(source, context.get())};
|
2020-02-21 04:29:32 -08:00
|
|
|
if(GetSourceState(source, voice) == AL_PLAYING)
|
2019-03-09 16:48:07 -08:00
|
|
|
{
|
2020-02-21 04:29:32 -08:00
|
|
|
if(!cur)
|
|
|
|
cur = tail = GetVoiceChanger(context.get());
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
|
|
|
|
cur = cur->mNext.load(std::memory_order_relaxed);
|
|
|
|
}
|
|
|
|
cur->mVoice = voice;
|
|
|
|
cur->mSourceID = source->id;
|
|
|
|
cur->mState = AL_PAUSED;
|
2019-03-09 16:48:07 -08:00
|
|
|
}
|
2020-02-21 04:29:32 -08:00
|
|
|
}
|
|
|
|
if LIKELY(tail)
|
|
|
|
{
|
|
|
|
SendVoiceChanges(context.get(), tail);
|
|
|
|
/* Second, now that the voice changes have been sent, because it's
|
|
|
|
* possible that the voice stopped after it was detected playing and
|
|
|
|
* before the voice got paused, recheck that the source is still
|
|
|
|
* considered playing and set it to paused if so.
|
|
|
|
*/
|
|
|
|
for(ALsource *source : srchandles)
|
2017-02-21 16:31:59 -08:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice{GetSourceVoice(source, context.get())};
|
2020-02-21 04:29:32 -08:00
|
|
|
if(GetSourceState(source, voice) == AL_PLAYING)
|
|
|
|
source->state = AL_PAUSED;
|
2017-02-21 16:31:59 -08:00
|
|
|
}
|
2020-02-21 04:29:32 -08:00
|
|
|
}
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
|
|
|
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourceStop(ALuint source)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
|
|
|
{ alSourceStopv(1, &source); }
|
|
|
|
END_API_FUNC
|
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(n < 0)
|
|
|
|
context->setError(AL_INVALID_VALUE, "Stopping %d sources", n);
|
|
|
|
if UNLIKELY(n <= 0) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-06 14:59:26 -07:00
|
|
|
al::vector<ALsource*> extra_sources;
|
2019-09-25 03:01:58 -07:00
|
|
|
std::array<ALsource*,8> source_storage;
|
|
|
|
al::span<ALsource*> srchandles;
|
|
|
|
if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
|
|
|
|
srchandles = {source_storage.data(), static_cast<ALuint>(n)};
|
|
|
|
else
|
2019-07-06 14:59:26 -07:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
extra_sources.resize(static_cast<ALuint>(n));
|
2019-09-25 03:01:58 -07:00
|
|
|
srchandles = {extra_sources.data(), extra_sources.size()};
|
2019-07-06 14:59:26 -07:00
|
|
|
}
|
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2019-09-25 03:01:58 -07:00
|
|
|
for(auto &srchdl : srchandles)
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
{
|
2019-09-25 03:01:58 -07:00
|
|
|
srchdl = LookupSource(context.get(), *sources);
|
|
|
|
if(!srchdl)
|
|
|
|
SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
|
|
|
|
++sources;
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
}
|
|
|
|
|
2020-02-21 03:23:22 -08:00
|
|
|
VoiceChange *tail{}, *cur{};
|
|
|
|
for(ALsource *source : srchandles)
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
if(Voice *voice{GetSourceVoice(source, context.get())})
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-02-21 03:23:22 -08:00
|
|
|
if(!cur)
|
|
|
|
cur = tail = GetVoiceChanger(context.get());
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
|
|
|
|
cur = cur->mNext.load(std::memory_order_relaxed);
|
|
|
|
}
|
2020-02-26 01:48:59 -08:00
|
|
|
voice->mPendingChange.store(true, std::memory_order_relaxed);
|
2020-02-20 23:53:56 -08:00
|
|
|
cur->mVoice = voice;
|
|
|
|
cur->mSourceID = source->id;
|
|
|
|
cur->mState = AL_STOPPED;
|
2018-11-22 12:02:02 -08:00
|
|
|
source->state = AL_STOPPED;
|
|
|
|
}
|
|
|
|
source->Offset = 0.0;
|
2020-02-21 03:23:22 -08:00
|
|
|
source->OffsetType = AL_NONE;
|
2020-02-20 23:53:56 -08:00
|
|
|
source->VoiceIdx = INVALID_VOICE_IDX;
|
|
|
|
}
|
2020-02-21 04:29:32 -08:00
|
|
|
if LIKELY(tail)
|
2020-02-21 03:23:22 -08:00
|
|
|
SendVoiceChanges(context.get(), tail);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
|
|
|
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourceRewind(ALuint source)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
|
|
|
{ alSourceRewindv(1, &source); }
|
|
|
|
END_API_FUNC
|
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(n < 0)
|
|
|
|
context->setError(AL_INVALID_VALUE, "Rewinding %d sources", n);
|
|
|
|
if UNLIKELY(n <= 0) return;
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
|
2019-07-06 14:59:26 -07:00
|
|
|
al::vector<ALsource*> extra_sources;
|
2019-09-25 03:01:58 -07:00
|
|
|
std::array<ALsource*,8> source_storage;
|
|
|
|
al::span<ALsource*> srchandles;
|
|
|
|
if LIKELY(static_cast<ALuint>(n) <= source_storage.size())
|
|
|
|
srchandles = {source_storage.data(), static_cast<ALuint>(n)};
|
|
|
|
else
|
2019-07-06 14:59:26 -07:00
|
|
|
{
|
2019-09-12 11:33:04 -07:00
|
|
|
extra_sources.resize(static_cast<ALuint>(n));
|
2019-09-25 03:01:58 -07:00
|
|
|
srchandles = {extra_sources.data(), extra_sources.size()};
|
2019-07-06 14:59:26 -07:00
|
|
|
}
|
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2019-09-25 03:01:58 -07:00
|
|
|
for(auto &srchdl : srchandles)
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
{
|
2019-09-25 03:01:58 -07:00
|
|
|
srchdl = LookupSource(context.get(), *sources);
|
|
|
|
if(!srchdl)
|
|
|
|
SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", *sources);
|
|
|
|
++sources;
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
}
|
|
|
|
|
2020-02-21 03:23:22 -08:00
|
|
|
VoiceChange *tail{}, *cur{};
|
|
|
|
for(ALsource *source : srchandles)
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
{
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice{GetSourceVoice(source, context.get())};
|
2019-09-01 15:28:33 -07:00
|
|
|
if(source->state != AL_INITIAL)
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-02-21 03:23:22 -08:00
|
|
|
if(!cur)
|
|
|
|
cur = tail = GetVoiceChanger(context.get());
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
|
|
|
|
cur = cur->mNext.load(std::memory_order_relaxed);
|
|
|
|
}
|
|
|
|
if(voice)
|
2020-02-26 01:48:59 -08:00
|
|
|
voice->mPendingChange.store(true, std::memory_order_relaxed);
|
2020-02-20 23:53:56 -08:00
|
|
|
cur->mVoice = voice;
|
|
|
|
cur->mSourceID = source->id;
|
|
|
|
cur->mState = AL_INITIAL;
|
2018-11-22 12:02:02 -08:00
|
|
|
source->state = AL_INITIAL;
|
|
|
|
}
|
|
|
|
source->Offset = 0.0;
|
2020-02-21 03:23:22 -08:00
|
|
|
source->OffsetType = AL_NONE;
|
2020-02-20 23:53:56 -08:00
|
|
|
source->VoiceIdx = INVALID_VOICE_IDX;
|
|
|
|
}
|
2020-02-21 04:29:32 -08:00
|
|
|
if LIKELY(tail)
|
2020-02-21 03:23:22 -08:00
|
|
|
SendVoiceChanges(context.get(), tail);
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
{
|
2018-11-22 12:02:02 -08:00
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(nb < 0)
|
|
|
|
context->setError(AL_INVALID_VALUE, "Queueing %d buffers", nb);
|
|
|
|
if UNLIKELY(nb <= 0) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *source{LookupSource(context.get(),src)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!source)
|
2019-07-30 21:32:05 -07:00
|
|
|
SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
/* Can't queue on a Static Source */
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(source->SourceType == AL_STATIC)
|
2019-07-30 21:32:05 -07:00
|
|
|
SETERR_RETURN(context, AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
/* Check for a valid Buffer, for its frequency and format */
|
2019-08-01 19:44:09 -07:00
|
|
|
ALCdevice *device{context->mDevice.get()};
|
2020-11-21 00:54:25 -08:00
|
|
|
ALbuffer *BufferFmt{nullptr};
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *BufferList{source->queue};
|
2019-08-11 03:34:35 -07:00
|
|
|
while(BufferList && !BufferFmt)
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
BufferFmt = static_cast<ALbuffer*>(BufferList->mBuffer);
|
2019-07-31 09:20:53 -07:00
|
|
|
BufferList = BufferList->mNext.load(std::memory_order_relaxed);
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-26 22:06:53 -08:00
|
|
|
std::unique_lock<std::mutex> buflock{device->BufferLock};
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *BufferListStart{nullptr};
|
2018-11-22 12:02:02 -08:00
|
|
|
BufferList = nullptr;
|
|
|
|
for(ALsizei i{0};i < nb;i++)
|
|
|
|
{
|
2020-04-04 01:52:29 -07:00
|
|
|
bool fmt_mismatch{false};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALbuffer *buffer{nullptr};
|
|
|
|
if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr)
|
|
|
|
{
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_NAME, "Queueing invalid buffer ID %u", buffers[i]);
|
2018-11-22 12:02:02 -08:00
|
|
|
goto buffer_error;
|
|
|
|
}
|
2020-11-21 00:54:25 -08:00
|
|
|
if(buffer && buffer->mCallback)
|
2020-02-17 00:32:22 -08:00
|
|
|
{
|
|
|
|
context->setError(AL_INVALID_OPERATION, "Queueing callback buffer %u", buffers[i]);
|
|
|
|
goto buffer_error;
|
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
if(!BufferListStart)
|
|
|
|
{
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferListStart = new BufferlistItem{};
|
2018-11-22 12:02:02 -08:00
|
|
|
BufferList = BufferListStart;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-11-19 04:18:25 -08:00
|
|
|
auto item = new BufferlistItem{};
|
2019-07-31 09:20:53 -07:00
|
|
|
BufferList->mNext.store(item, std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
BufferList = item;
|
|
|
|
}
|
2019-07-31 09:20:53 -07:00
|
|
|
BufferList->mNext.store(nullptr, std::memory_order_relaxed);
|
2020-11-21 00:54:25 -08:00
|
|
|
BufferList->mSampleLen = buffer ? buffer->mSampleLen : 0;
|
|
|
|
BufferList->mBuffer = buffer;
|
2018-11-22 12:02:02 -08:00
|
|
|
if(!buffer) continue;
|
2017-12-15 22:59:51 -08:00
|
|
|
|
2019-08-01 13:28:53 -07:00
|
|
|
IncrementRef(buffer->ref);
|
2017-12-15 22:59:51 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
|
2017-04-18 00:58:33 -07:00
|
|
|
{
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_OPERATION, "Queueing non-persistently mapped buffer %u",
|
|
|
|
buffer->id);
|
2018-11-22 12:02:02 -08:00
|
|
|
goto buffer_error;
|
2017-04-18 00:58:33 -07:00
|
|
|
}
|
2010-03-24 02:23:00 -07:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
if(BufferFmt == nullptr)
|
2020-11-21 00:54:25 -08:00
|
|
|
BufferFmt = buffer;
|
2020-04-04 01:52:29 -07:00
|
|
|
else
|
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate;
|
|
|
|
fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels;
|
2020-11-19 04:18:25 -08:00
|
|
|
if(BufferFmt->isBFormat())
|
2020-04-04 01:52:29 -07:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout;
|
|
|
|
fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling;
|
2020-04-04 01:52:29 -07:00
|
|
|
}
|
2020-11-21 00:54:25 -08:00
|
|
|
fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder;
|
|
|
|
fmt_mismatch |= BufferFmt->OriginalType != buffer->OriginalType;
|
2020-04-04 01:52:29 -07:00
|
|
|
}
|
|
|
|
if(fmt_mismatch)
|
2017-04-18 00:58:33 -07:00
|
|
|
{
|
2019-07-30 21:32:05 -07:00
|
|
|
context->setError(AL_INVALID_OPERATION, "Queueing buffer with mismatched format");
|
2014-03-04 22:44:30 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
buffer_error:
|
|
|
|
/* A buffer failed (invalid ID or format), so unlock and release
|
|
|
|
* each buffer we had. */
|
|
|
|
while(BufferListStart)
|
|
|
|
{
|
2020-11-19 04:18:25 -08:00
|
|
|
std::unique_ptr<BufferlistItem> head{BufferListStart};
|
2019-08-11 03:34:35 -07:00
|
|
|
BufferListStart = head->mNext.load(std::memory_order_relaxed);
|
2020-11-21 00:54:25 -08:00
|
|
|
if(auto *buf{static_cast<ALbuffer*>(head->mBuffer)})
|
|
|
|
DecrementRef(buf->ref);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
return;
|
2017-04-18 00:58:33 -07:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2018-11-22 12:02:02 -08:00
|
|
|
/* All buffers good. */
|
|
|
|
buflock.unlock();
|
2015-09-22 08:48:26 -07:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
/* Source is now streaming */
|
|
|
|
source->SourceType = AL_STREAMING;
|
|
|
|
|
2019-09-30 03:03:27 -07:00
|
|
|
BufferList = source->queue;
|
|
|
|
if(!BufferList)
|
2018-11-22 12:02:02 -08:00
|
|
|
source->queue = BufferListStart;
|
|
|
|
else
|
|
|
|
{
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *next;
|
2019-07-31 09:20:53 -07:00
|
|
|
while((next=BufferList->mNext.load(std::memory_order_relaxed)) != nullptr)
|
2018-11-22 12:02:02 -08:00
|
|
|
BufferList = next;
|
2019-07-31 09:20:53 -07:00
|
|
|
BufferList->mNext.store(BufferListStart, std::memory_order_release);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2020-04-28 14:48:12 -07:00
|
|
|
AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
|
2019-04-10 17:47:13 -07:00
|
|
|
START_API_FUNC
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
ContextRef context{GetContextRef()};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!context) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(nb < 0)
|
|
|
|
context->setError(AL_INVALID_VALUE, "Unqueueing %d buffers", nb);
|
|
|
|
if UNLIKELY(nb <= 0) return;
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-07-30 09:05:54 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource *source{LookupSource(context.get(),src)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!source)
|
2019-07-30 21:32:05 -07:00
|
|
|
SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid source ID %u", src);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(source->Looping)
|
2019-07-30 21:32:05 -07:00
|
|
|
SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from looping source %u", src);
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(source->SourceType != AL_STREAMING)
|
2019-07-30 21:32:05 -07:00
|
|
|
SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing from a non-streaming source %u",
|
|
|
|
src);
|
2018-11-22 12:02:02 -08:00
|
|
|
|
|
|
|
/* Make sure enough buffers have been processed to unqueue. */
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *BufferList{source->queue};
|
2020-03-28 17:20:38 -07:00
|
|
|
Voice *voice{GetSourceVoice(source, context.get())};
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *Current{nullptr};
|
2018-11-22 12:02:02 -08:00
|
|
|
if(voice)
|
2019-03-10 16:29:06 -07:00
|
|
|
Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
else if(source->state == AL_INITIAL)
|
|
|
|
Current = BufferList;
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(BufferList == Current)
|
2020-05-25 02:02:50 -07:00
|
|
|
SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing %d buffer%s (none processed)", nb,
|
|
|
|
(nb==1) ? "" : "s");
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-08-11 03:34:35 -07:00
|
|
|
ALuint i{1u};
|
2019-07-31 09:20:53 -07:00
|
|
|
while(i < static_cast<ALuint>(nb))
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
|
|
|
/* If the next bufferlist to check is NULL or is the current one, it's
|
2020-05-25 02:02:50 -07:00
|
|
|
* trying to unqueue more buffers than are processed.
|
2018-11-22 12:02:02 -08:00
|
|
|
*/
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *next{BufferList->mNext.load(std::memory_order_relaxed)};
|
2019-08-04 11:59:14 -07:00
|
|
|
if UNLIKELY(!next || next == Current)
|
2020-05-25 02:02:50 -07:00
|
|
|
SETERR_RETURN(context, AL_INVALID_VALUE,, "Unqueueing %d buffers (only %u processed)",
|
|
|
|
nb, i);
|
2018-11-22 12:02:02 -08:00
|
|
|
BufferList = next;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-08-11 03:34:35 -07:00
|
|
|
++i;
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2019-09-12 11:33:04 -07:00
|
|
|
do {
|
2020-11-19 04:18:25 -08:00
|
|
|
std::unique_ptr<BufferlistItem> head{source->queue};
|
2019-08-11 03:34:35 -07:00
|
|
|
source->queue = head->mNext.load(std::memory_order_relaxed);
|
|
|
|
|
2020-11-21 00:54:25 -08:00
|
|
|
if(auto *buffer{static_cast<ALbuffer*>(head->mBuffer)})
|
2018-11-22 12:02:02 -08:00
|
|
|
{
|
2020-11-21 00:54:25 -08:00
|
|
|
*(buffers++) = buffer->id;
|
|
|
|
DecrementRef(buffer->ref);
|
2010-01-12 02:22:38 -08:00
|
|
|
}
|
2019-08-11 03:34:35 -07:00
|
|
|
else
|
|
|
|
*(buffers++) = 0;
|
2019-09-12 11:33:04 -07:00
|
|
|
} while(--nb);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2019-04-10 17:47:13 -07:00
|
|
|
END_API_FUNC
|
2007-11-13 18:02:18 -08:00
|
|
|
|
|
|
|
|
2020-02-25 06:39:03 -08:00
|
|
|
ALsource::ALsource()
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-22 12:02:02 -08:00
|
|
|
Direct.Gain = 1.0f;
|
|
|
|
Direct.GainHF = 1.0f;
|
|
|
|
Direct.HFReference = LOWPASSFREQREF;
|
|
|
|
Direct.GainLF = 1.0f;
|
|
|
|
Direct.LFReference = HIGHPASSFREQREF;
|
|
|
|
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;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2010-03-24 02:23:00 -07:00
|
|
|
|
2019-02-04 21:28:37 -08:00
|
|
|
PropsClean.test_and_set(std::memory_order_relaxed);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
2008-01-16 13:27:15 -08:00
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
ALsource::~ALsource()
|
2018-01-27 13:02:17 -08:00
|
|
|
{
|
2020-11-19 04:18:25 -08:00
|
|
|
BufferlistItem *BufferList{queue};
|
2018-11-22 12:02:02 -08:00
|
|
|
while(BufferList != nullptr)
|
2018-01-27 13:02:17 -08:00
|
|
|
{
|
2020-11-19 04:18:25 -08:00
|
|
|
std::unique_ptr<BufferlistItem> head{BufferList};
|
2019-08-11 03:34:35 -07:00
|
|
|
BufferList = head->mNext.load(std::memory_order_relaxed);
|
2020-11-21 00:54:25 -08:00
|
|
|
if(auto *buffer{static_cast<ALbuffer*>(head->mBuffer)})
|
|
|
|
DecrementRef(buffer->ref);
|
2018-11-22 12:02:02 -08:00
|
|
|
}
|
|
|
|
queue = nullptr;
|
2018-11-18 02:15:31 -08:00
|
|
|
|
2020-02-25 06:39:03 -08:00
|
|
|
auto clear_send = [](ALsource::SendData &send) -> void
|
|
|
|
{ if(send.Slot) DecrementRef(send.Slot->ref); };
|
|
|
|
std::for_each(Send.begin(), Send.end(), clear_send);
|
2018-01-27 13:02:17 -08:00
|
|
|
}
|
|
|
|
|
2018-11-22 12:02:02 -08:00
|
|
|
void UpdateAllSourceProps(ALCcontext *context)
|
2018-01-27 13:02:17 -08:00
|
|
|
{
|
2019-09-01 00:24:49 -07:00
|
|
|
std::lock_guard<std::mutex> _{context->mSourceLock};
|
2020-02-21 20:14:28 -08:00
|
|
|
auto voicelist = context->getVoicesSpan();
|
2020-11-24 10:49:53 -08:00
|
|
|
ALuint vidx{0u};
|
|
|
|
for(Voice *voice : voicelist)
|
|
|
|
{
|
|
|
|
ALuint sid{voice->mSourceID.load(std::memory_order_acquire)};
|
|
|
|
ALsource *source = sid ? LookupSource(context, sid) : nullptr;
|
|
|
|
if(source && source->VoiceIdx == vidx)
|
2018-11-23 16:16:31 -08:00
|
|
|
{
|
2020-11-24 10:49:53 -08:00
|
|
|
if(!source->PropsClean.test_and_set(std::memory_order_acq_rel))
|
2020-02-21 20:14:28 -08:00
|
|
|
UpdateSourceProps(source, voice, context);
|
2018-11-23 16:16:31 -08:00
|
|
|
}
|
2020-11-24 10:49:53 -08:00
|
|
|
++vidx;
|
|
|
|
}
|
2018-01-27 13:02:17 -08:00
|
|
|
}
|
|
|
|
|
2018-11-25 08:42:43 -08:00
|
|
|
SourceSubList::~SourceSubList()
|
2008-01-16 13:27:15 -08:00
|
|
|
{
|
2019-02-11 12:16:58 -08:00
|
|
|
uint64_t usemask{~FreeMask};
|
2018-11-25 08:42:43 -08:00
|
|
|
while(usemask)
|
2008-01-16 13:27:15 -08:00
|
|
|
{
|
2020-10-13 01:21:44 -07:00
|
|
|
const ALsizei idx{CountTrailingZeros(usemask)};
|
2019-06-05 17:25:08 -07:00
|
|
|
al::destroy_at(Sources+idx);
|
2019-01-07 04:06:40 -08:00
|
|
|
usemask &= ~(1_u64 << idx);
|
2008-01-16 13:27:15 -08:00
|
|
|
}
|
2018-11-25 08:42:43 -08:00
|
|
|
FreeMask = ~usemask;
|
|
|
|
al_free(Sources);
|
|
|
|
Sources = nullptr;
|
2008-01-16 13:27:15 -08:00
|
|
|
}
|