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
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
2009-01-24 10:38:04 -08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2009-01-25 19:20:47 -08:00
|
|
|
#include <ctype.h>
|
2009-02-02 11:18:33 -08:00
|
|
|
#include <assert.h>
|
2009-01-25 19:20:47 -08:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
#include <cmath>
|
2018-12-22 16:01:14 -08:00
|
|
|
#include <limits>
|
2018-11-21 05:06:31 -08:00
|
|
|
#include <algorithm>
|
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
#include "alMain.h"
|
2018-11-17 23:02:27 -08:00
|
|
|
#include "alcontext.h"
|
2007-12-31 01:09:57 -08:00
|
|
|
#include "alSource.h"
|
|
|
|
#include "alBuffer.h"
|
|
|
|
#include "alListener.h"
|
2008-01-16 14:01:24 -08:00
|
|
|
#include "alAuxEffectSlot.h"
|
2008-08-14 05:43:52 -07:00
|
|
|
#include "alu.h"
|
2008-01-03 05:36:51 -08:00
|
|
|
#include "bs2b.h"
|
2014-02-23 21:11:01 -08:00
|
|
|
#include "hrtf.h"
|
2018-01-11 06:50:53 -08:00
|
|
|
#include "mastering.h"
|
2016-02-26 16:09:06 -08:00
|
|
|
#include "uhjfilter.h"
|
2016-03-15 05:08:05 -07:00
|
|
|
#include "bformatdec.h"
|
2018-02-03 01:07:06 -08:00
|
|
|
#include "ringbuffer.h"
|
2018-04-21 23:23:46 -07:00
|
|
|
#include "filters/splitter.h"
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-03-22 05:06:15 -07:00
|
|
|
#include "mixer/defs.h"
|
2018-01-11 08:44:52 -08:00
|
|
|
#include "fpu_modes.h"
|
2018-01-11 07:19:19 -08:00
|
|
|
#include "cpu_caps.h"
|
2017-08-28 10:31:23 -07:00
|
|
|
#include "bsinc_inc.h"
|
2014-11-22 04:20:17 -08:00
|
|
|
|
2018-11-11 14:56:25 -08:00
|
|
|
|
2018-12-08 21:58:44 -08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
ALfloat InitConeScale()
|
|
|
|
{
|
|
|
|
ALfloat ret{1.0f};
|
|
|
|
const char *str{getenv("__ALSOFT_HALF_ANGLE_CONES")};
|
|
|
|
if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
|
|
|
|
ret *= 0.5f;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ALfloat InitZScale()
|
|
|
|
{
|
|
|
|
ALfloat ret{1.0f};
|
|
|
|
const char *str{getenv("__ALSOFT_REVERSE_Z")};
|
|
|
|
if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
|
|
|
|
ret *= -1.0f;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ALboolean InitReverbSOS()
|
|
|
|
{
|
|
|
|
ALboolean ret{AL_FALSE};
|
|
|
|
const char *str{getenv("__ALSOFT_REVERB_IGNORES_SOUND_SPEED")};
|
|
|
|
if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
|
|
|
|
ret = AL_TRUE;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2017-08-07 01:38:26 -07:00
|
|
|
/* Cone scalar */
|
2018-12-08 21:58:44 -08:00
|
|
|
const ALfloat ConeScale{InitConeScale()};
|
2017-08-07 01:38:26 -07:00
|
|
|
|
|
|
|
/* Localized Z scalar for mono sources */
|
2018-12-08 21:58:44 -08:00
|
|
|
const ALfloat ZScale{InitZScale()};
|
2017-08-07 01:38:26 -07:00
|
|
|
|
2017-09-22 05:42:04 -07:00
|
|
|
/* Force default speed of sound for distance-related reverb decay. */
|
2018-12-08 21:58:44 -08:00
|
|
|
const ALboolean OverrideReverbSpeedOfSound{InitReverbSOS()};
|
2017-09-22 05:42:04 -07:00
|
|
|
|
2014-12-16 06:29:31 -08:00
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
namespace {
|
|
|
|
|
2018-12-04 15:30:39 -08:00
|
|
|
void ClearArray(ALfloat (&f)[MAX_OUTPUT_CHANNELS])
|
2018-03-25 18:02:07 -07:00
|
|
|
{
|
2018-12-04 15:30:39 -08:00
|
|
|
std::fill(std::begin(f), std::end(f), 0.0f);
|
2018-03-25 18:02:07 -07:00
|
|
|
}
|
|
|
|
|
2017-08-07 01:38:26 -07:00
|
|
|
struct ChanMap {
|
2018-12-24 19:29:01 -08:00
|
|
|
Channel channel;
|
2017-08-07 01:38:26 -07:00
|
|
|
ALfloat angle;
|
|
|
|
ALfloat elevation;
|
|
|
|
};
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
|
2017-04-17 21:16:01 -07:00
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
inline HrtfDirectMixerFunc SelectHrtfMixer(void)
|
2014-11-22 04:20:17 -08:00
|
|
|
{
|
|
|
|
#ifdef HAVE_NEON
|
|
|
|
if((CPUCapFlags&CPU_CAP_NEON))
|
2016-08-12 05:26:36 -07:00
|
|
|
return MixDirectHrtf_Neon;
|
2014-11-22 04:20:17 -08:00
|
|
|
#endif
|
2017-04-12 22:44:16 -07:00
|
|
|
#ifdef HAVE_SSE
|
|
|
|
if((CPUCapFlags&CPU_CAP_SSE))
|
|
|
|
return MixDirectHrtf_SSE;
|
|
|
|
#endif
|
2014-11-22 04:20:17 -08:00
|
|
|
|
2016-08-12 05:26:36 -07:00
|
|
|
return MixDirectHrtf_C;
|
2014-11-22 04:20:17 -08:00
|
|
|
}
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
|
|
|
|
void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
|
2018-02-10 15:50:05 -08:00
|
|
|
{
|
2018-12-26 12:46:01 -08:00
|
|
|
const ALsizei num_chans{device->Dry.NumChannels};
|
|
|
|
ASSUME(num_chans > 0);
|
2018-02-10 15:50:05 -08:00
|
|
|
|
2018-12-26 12:46:01 -08:00
|
|
|
if(AmbiUpsampler *ambiup{device->AmbiUp.get()})
|
|
|
|
ambiup->process(device->Dry.Buffer, num_chans, device->FOAOut.Buffer, SamplesToDo);
|
2018-02-10 15:50:05 -08:00
|
|
|
|
2018-12-26 12:46:01 -08:00
|
|
|
/* HRTF is stereo output only. */
|
|
|
|
const int lidx{(device->RealOut.ChannelName[0]==FrontLeft) ? 0 : 1};
|
|
|
|
const int ridx{(device->RealOut.ChannelName[1]==FrontRight) ? 1 : 0};
|
|
|
|
ALfloat *LeftOut{device->RealOut.Buffer[lidx]};
|
|
|
|
ALfloat *RightOut{device->RealOut.Buffer[ridx]};
|
|
|
|
|
|
|
|
const ALfloat (*Input)[BUFFERSIZE]{device->Dry.Buffer};
|
2018-11-22 07:54:29 -08:00
|
|
|
DirectHrtfState *state{device->mHrtfState.get()};
|
2018-12-26 12:46:01 -08:00
|
|
|
for(ALsizei c{0};c < num_chans;c++)
|
2018-02-10 15:50:05 -08:00
|
|
|
{
|
2018-12-26 12:46:01 -08:00
|
|
|
MixDirectHrtf(LeftOut, RightOut, Input[c], state->Offset, state->IrSize,
|
|
|
|
state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo);
|
2018-02-10 15:50:05 -08:00
|
|
|
}
|
|
|
|
state->Offset += SamplesToDo;
|
|
|
|
}
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
|
2018-02-10 15:50:05 -08:00
|
|
|
{
|
2018-12-26 12:46:01 -08:00
|
|
|
BFormatDec *ambidec{device->AmbiDecoder.get()};
|
2018-02-10 15:50:05 -08:00
|
|
|
if(device->Dry.Buffer != device->FOAOut.Buffer)
|
2018-12-26 12:46:01 -08:00
|
|
|
ambidec->upSample(device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
|
|
|
|
SamplesToDo);
|
|
|
|
ambidec->process(device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
|
|
|
|
SamplesToDo);
|
2018-02-10 15:50:05 -08:00
|
|
|
}
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
|
2018-02-10 15:50:05 -08:00
|
|
|
{
|
2018-12-08 02:50:34 -08:00
|
|
|
device->AmbiUp->process(device->RealOut.Buffer, device->RealOut.NumChannels,
|
2018-12-26 12:46:01 -08:00
|
|
|
device->FOAOut.Buffer, SamplesToDo);
|
2018-02-10 15:50:05 -08:00
|
|
|
}
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
|
2018-02-10 15:50:05 -08:00
|
|
|
{
|
2018-12-26 12:46:01 -08:00
|
|
|
/* UHJ is stereo output only. */
|
|
|
|
const int lidx{(device->RealOut.ChannelName[0]==FrontLeft) ? 0 : 1};
|
|
|
|
const int ridx{(device->RealOut.ChannelName[1]==FrontRight) ? 1 : 0};
|
2018-04-22 02:38:09 -07:00
|
|
|
|
|
|
|
/* Encode to stereo-compatible 2-channel UHJ output. */
|
2018-12-26 13:20:59 -08:00
|
|
|
Uhj2Encoder *uhj2enc{device->Uhj_Encoder.get()};
|
|
|
|
uhj2enc->encode(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
|
|
|
|
device->Dry.Buffer, SamplesToDo);
|
2018-02-10 15:50:05 -08:00
|
|
|
}
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
|
2018-02-10 15:50:05 -08:00
|
|
|
{
|
2018-12-26 12:46:01 -08:00
|
|
|
/* BS2B is stereo output only. */
|
|
|
|
const int lidx{(device->RealOut.ChannelName[0]==FrontLeft) ? 0 : 1};
|
|
|
|
const int ridx{(device->RealOut.ChannelName[1]==FrontRight) ? 1 : 0};
|
2018-04-22 02:38:09 -07:00
|
|
|
|
|
|
|
/* Apply binaural/crossfeed filter */
|
2018-11-22 06:49:37 -08:00
|
|
|
bs2b_cross_feed(device->Bs2b.get(), device->RealOut.Buffer[lidx],
|
2018-04-22 02:38:09 -07:00
|
|
|
device->RealOut.Buffer[ridx], SamplesToDo);
|
2018-02-10 15:50:05 -08:00
|
|
|
}
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
} // namespace
|
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
void aluInit(void)
|
|
|
|
{
|
|
|
|
MixDirectHrtf = SelectHrtfMixer();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DeinitVoice(ALvoice *voice) noexcept
|
|
|
|
{
|
|
|
|
delete voice->Update.exchange(nullptr, std::memory_order_acq_rel);
|
|
|
|
voice->~ALvoice();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-10 15:50:05 -08:00
|
|
|
void aluSelectPostProcess(ALCdevice *device)
|
|
|
|
{
|
2018-12-22 09:20:50 -08:00
|
|
|
if(device->mHrtf)
|
2018-02-10 15:50:05 -08:00
|
|
|
device->PostProcess = ProcessHrtf;
|
|
|
|
else if(device->AmbiDecoder)
|
|
|
|
device->PostProcess = ProcessAmbiDec;
|
|
|
|
else if(device->AmbiUp)
|
|
|
|
device->PostProcess = ProcessAmbiUp;
|
|
|
|
else if(device->Uhj_Encoder)
|
|
|
|
device->PostProcess = ProcessUhj;
|
|
|
|
else if(device->Bs2b)
|
|
|
|
device->PostProcess = ProcessBs2b;
|
|
|
|
else
|
2018-12-12 01:09:04 -08:00
|
|
|
device->PostProcess = nullptr;
|
2018-02-10 15:50:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-23 06:10:09 -07:00
|
|
|
/* Prepares the interpolator for a given rate (determined by increment).
|
2015-11-05 09:42:08 -08:00
|
|
|
*
|
|
|
|
* With a bit of work, and a trade of memory for CPU cost, this could be
|
|
|
|
* modified for use with an interpolated increment for buttery-smooth pitch
|
|
|
|
* changes.
|
|
|
|
*/
|
2018-01-10 19:20:58 -08:00
|
|
|
void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
|
2015-11-05 09:42:08 -08:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
ALsizei si{BSINC_SCALE_COUNT - 1};
|
|
|
|
ALfloat sf{0.0f};
|
2015-11-05 09:42:08 -08:00
|
|
|
|
|
|
|
if(increment > FRACTIONONE)
|
|
|
|
{
|
|
|
|
sf = (ALfloat)FRACTIONONE / increment;
|
2018-01-10 19:20:58 -08:00
|
|
|
sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
|
2018-05-03 21:43:53 -07:00
|
|
|
si = float2int(sf);
|
2018-01-10 19:20:58 -08:00
|
|
|
/* The interpolation factor is fit to this diagonally-symmetric curve
|
|
|
|
* to reduce the transition ripple caused by interpolating different
|
|
|
|
* scales of the sinc function.
|
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
sf = 1.0f - std::cos(std::asin(sf - si));
|
2015-11-05 09:42:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
state->sf = sf;
|
2017-08-27 10:16:36 -07:00
|
|
|
state->m = table->m[si];
|
2018-09-17 01:43:02 -07:00
|
|
|
state->l = (state->m/2) - 1;
|
2017-08-27 10:16:36 -07:00
|
|
|
state->filter = table->Tab + table->filterOffset[si];
|
2015-11-05 09:42:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
/* This RNG method was created based on the math found in opusdec. It's quick,
|
|
|
|
* and starting with a seed value of 22222, is suitable for generating
|
|
|
|
* whitenoise.
|
|
|
|
*/
|
2018-11-23 13:12:48 -08:00
|
|
|
inline ALuint dither_rng(ALuint *seed) noexcept
|
2018-11-20 02:12:04 -08:00
|
|
|
{
|
|
|
|
*seed = (*seed * 96314165) + 907633515;
|
|
|
|
return *seed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-12 04:22:11 -08:00
|
|
|
inline alu::Vector aluCrossproduct(const alu::Vector &in1, const alu::Vector &in2)
|
2018-11-20 02:12:04 -08:00
|
|
|
{
|
2018-12-12 04:22:11 -08:00
|
|
|
return alu::Vector{
|
|
|
|
in1[1]*in2[2] - in1[2]*in2[1],
|
|
|
|
in1[2]*in2[0] - in1[0]*in2[2],
|
|
|
|
in1[0]*in2[1] - in1[1]*in2[0],
|
|
|
|
0.0f
|
|
|
|
};
|
2018-11-20 02:12:04 -08:00
|
|
|
}
|
|
|
|
|
2018-12-12 04:22:11 -08:00
|
|
|
inline ALfloat aluDotproduct(const alu::Vector &vec1, const alu::Vector &vec2)
|
2018-11-20 02:12:04 -08:00
|
|
|
{
|
2018-12-12 04:22:11 -08:00
|
|
|
return vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2];
|
2018-11-20 02:12:04 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-12 04:22:11 -08:00
|
|
|
alu::Vector operator*(const alu::Matrix &mtx, const alu::Vector &vec) noexcept
|
2018-11-20 02:12:04 -08:00
|
|
|
{
|
2018-12-12 04:22:11 -08:00
|
|
|
return alu::Vector{
|
|
|
|
vec[0]*mtx[0][0] + vec[1]*mtx[1][0] + vec[2]*mtx[2][0] + vec[3]*mtx[3][0],
|
|
|
|
vec[0]*mtx[0][1] + vec[1]*mtx[1][1] + vec[2]*mtx[2][1] + vec[3]*mtx[3][1],
|
|
|
|
vec[0]*mtx[0][2] + vec[1]*mtx[1][2] + vec[2]*mtx[2][2] + vec[3]*mtx[3][2],
|
|
|
|
vec[0]*mtx[0][3] + vec[1]*mtx[1][3] + vec[2]*mtx[2][3] + vec[3]*mtx[3][3]
|
|
|
|
};
|
2018-11-20 02:12:04 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
|
|
|
|
{
|
2018-11-30 21:39:59 -08:00
|
|
|
ALbitfieldSOFT enabledevt{context->EnabledEvts.load(std::memory_order_acquire)};
|
2018-11-20 02:12:04 -08:00
|
|
|
if(!(enabledevt&EventType_SourceStateChange)) return;
|
|
|
|
|
2018-12-25 09:32:38 -08:00
|
|
|
auto evt_data = ll_ringbuffer_get_write_vector(context->AsyncEvents).first;
|
|
|
|
if(evt_data.len < 1) return;
|
|
|
|
|
|
|
|
AsyncEvent *evt{new (evt_data.buf) AsyncEvent{EventType_SourceStateChange}};
|
|
|
|
evt->u.srcstate.id = id;
|
|
|
|
evt->u.srcstate.state = AL_STOPPED;
|
|
|
|
ll_ringbuffer_write_advance(context->AsyncEvents, 1);
|
|
|
|
context->EventSem.post();
|
2018-11-20 02:12:04 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CalcContextParams(ALCcontext *Context)
|
2017-09-27 08:55:42 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
ALcontextProps *props{Context->Update.exchange(nullptr, std::memory_order_acq_rel)};
|
2017-09-27 08:55:42 -07:00
|
|
|
if(!props) return false;
|
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
ALlistener &Listener = Context->Listener;
|
2018-11-17 23:41:11 -08:00
|
|
|
Listener.Params.MetersPerUnit = props->MetersPerUnit;
|
2017-09-27 08:55:42 -07:00
|
|
|
|
2018-11-17 23:41:11 -08:00
|
|
|
Listener.Params.DopplerFactor = props->DopplerFactor;
|
|
|
|
Listener.Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
|
2017-09-27 08:55:42 -07:00
|
|
|
if(!OverrideReverbSpeedOfSound)
|
2018-11-17 23:41:11 -08:00
|
|
|
Listener.Params.ReverbSpeedOfSound = Listener.Params.SpeedOfSound *
|
|
|
|
Listener.Params.MetersPerUnit;
|
2017-09-27 08:55:42 -07:00
|
|
|
|
2018-11-17 23:41:11 -08:00
|
|
|
Listener.Params.SourceDistanceModel = props->SourceDistanceModel;
|
2018-11-18 03:39:32 -08:00
|
|
|
Listener.Params.mDistanceModel = props->mDistanceModel;
|
2017-09-27 08:55:42 -07:00
|
|
|
|
2018-11-19 01:20:03 -08:00
|
|
|
AtomicReplaceHead(Context->FreeContextProps, props);
|
2017-09-27 08:55:42 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
bool CalcListenerParams(ALCcontext *Context)
|
2012-10-12 07:38:29 -07:00
|
|
|
{
|
2018-11-17 23:41:11 -08:00
|
|
|
ALlistener &Listener = Context->Listener;
|
2016-05-11 18:40:17 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
ALlistenerProps *props{Listener.Update.exchange(nullptr, std::memory_order_acq_rel)};
|
2017-09-27 08:55:42 -07:00
|
|
|
if(!props) return false;
|
2012-10-12 07:38:29 -07:00
|
|
|
|
|
|
|
/* AT then UP */
|
2018-12-12 04:22:11 -08:00
|
|
|
alu::Vector N{props->Forward[0], props->Forward[1], props->Forward[2], 0.0f};
|
|
|
|
N.normalize();
|
|
|
|
alu::Vector V{props->Up[0], props->Up[1], props->Up[2], 0.0f};
|
|
|
|
V.normalize();
|
2012-10-12 07:38:29 -07:00
|
|
|
/* Build and normalize right-vector */
|
2018-12-12 04:22:11 -08:00
|
|
|
alu::Vector U{aluCrossproduct(N, V)};
|
|
|
|
U.normalize();
|
|
|
|
|
|
|
|
Listener.Params.Matrix = alu::Matrix{
|
|
|
|
U[0], V[0], -N[0], 0.0f,
|
|
|
|
U[1], V[1], -N[1], 0.0f,
|
|
|
|
U[2], V[2], -N[2], 0.0f,
|
|
|
|
0.0f, 0.0f, 0.0f, 1.0f
|
|
|
|
};
|
2015-08-24 03:02:58 -07:00
|
|
|
|
2018-12-25 22:32:30 -08:00
|
|
|
const alu::Vector P{Listener.Params.Matrix *
|
|
|
|
alu::Vector{props->Position[0], props->Position[1], props->Position[2], 1.0f}};
|
2018-12-12 04:22:11 -08:00
|
|
|
Listener.Params.Matrix.setRow(3, -P[0], -P[1], -P[2], 1.0f);
|
2014-12-16 06:29:31 -08:00
|
|
|
|
2018-12-25 22:32:30 -08:00
|
|
|
const alu::Vector vel{props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f};
|
2018-12-12 04:22:11 -08:00
|
|
|
Listener.Params.Velocity = Listener.Params.Matrix * vel;
|
2016-05-11 18:40:17 -07:00
|
|
|
|
2018-11-17 23:41:11 -08:00
|
|
|
Listener.Params.Gain = props->Gain * Context->GainBoost;
|
2016-08-23 19:17:17 -07:00
|
|
|
|
2018-11-19 01:20:03 -08:00
|
|
|
AtomicReplaceHead(Context->FreeListenerProps, props);
|
2017-09-27 08:55:42 -07:00
|
|
|
return true;
|
2012-10-12 07:38:29 -07:00
|
|
|
}
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
|
2016-05-12 18:26:33 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
ALeffectslotProps *props{slot->Update.exchange(nullptr, std::memory_order_acq_rel)};
|
2017-09-27 11:13:18 -07:00
|
|
|
if(!props && !force) return false;
|
2016-05-12 18:26:33 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
EffectState *state;
|
|
|
|
if(!props)
|
|
|
|
state = slot->Params.mEffectState;
|
|
|
|
else
|
2016-05-12 18:26:33 -07:00
|
|
|
{
|
2017-09-27 11:13:18 -07:00
|
|
|
slot->Params.Gain = props->Gain;
|
|
|
|
slot->Params.AuxSendAuto = props->AuxSendAuto;
|
2018-12-24 15:17:38 -08:00
|
|
|
slot->Params.Target = props->Target;
|
2017-09-27 11:13:18 -07:00
|
|
|
slot->Params.EffectType = props->Type;
|
|
|
|
slot->Params.EffectProps = props->Props;
|
|
|
|
if(IsReverbEffect(props->Type))
|
|
|
|
{
|
|
|
|
slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
|
|
|
|
slot->Params.DecayTime = props->Props.Reverb.DecayTime;
|
2018-03-11 22:40:08 -07:00
|
|
|
slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
|
2017-09-27 11:13:18 -07:00
|
|
|
slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
|
|
|
|
slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
|
|
|
|
slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slot->Params.RoomRolloff = 0.0f;
|
|
|
|
slot->Params.DecayTime = 0.0f;
|
2018-03-11 22:40:08 -07:00
|
|
|
slot->Params.DecayLFRatio = 0.0f;
|
2017-09-27 11:13:18 -07:00
|
|
|
slot->Params.DecayHFRatio = 0.0f;
|
|
|
|
slot->Params.DecayHFLimit = AL_FALSE;
|
|
|
|
slot->Params.AirAbsorptionGainHF = 1.0f;
|
|
|
|
}
|
2016-05-15 01:19:05 -07:00
|
|
|
|
2017-09-27 11:13:18 -07:00
|
|
|
state = props->State;
|
2018-12-25 09:32:38 -08:00
|
|
|
props->State = nullptr;
|
2018-12-26 12:25:34 -08:00
|
|
|
EffectState *oldstate{slot->Params.mEffectState};
|
|
|
|
slot->Params.mEffectState = state;
|
2018-09-21 02:37:37 -07:00
|
|
|
|
2018-12-26 12:25:34 -08:00
|
|
|
/* Manually decrement the old effect state's refcount if it's greater
|
|
|
|
* than 1. We need to be a bit clever here to avoid the refcount
|
|
|
|
* reaching 0 since it can't be deleted in the mixer.
|
|
|
|
*/
|
|
|
|
ALuint oldval{oldstate->mRef.load(std::memory_order_acquire)};
|
|
|
|
while(oldval > 1 && !oldstate->mRef.compare_exchange_weak(oldval, oldval-1,
|
|
|
|
std::memory_order_acq_rel, std::memory_order_acquire))
|
2018-09-21 02:37:37 -07:00
|
|
|
{
|
2018-12-26 12:25:34 -08:00
|
|
|
/* oldval was updated with the current value on failure, so just
|
|
|
|
* try again.
|
2018-09-21 02:37:37 -07:00
|
|
|
*/
|
|
|
|
}
|
2018-12-26 12:25:34 -08:00
|
|
|
|
|
|
|
if(oldval < 2)
|
2018-09-21 02:37:37 -07:00
|
|
|
{
|
2018-12-26 12:25:34 -08:00
|
|
|
/* Otherwise, if it would be deleted, send it off with a release
|
2018-09-21 02:37:37 -07:00
|
|
|
* event.
|
|
|
|
*/
|
2018-12-26 12:25:34 -08:00
|
|
|
auto evt_vec = ll_ringbuffer_get_write_vector(context->AsyncEvents);
|
|
|
|
if(LIKELY(evt_vec.first.len > 0))
|
2018-12-25 09:32:38 -08:00
|
|
|
{
|
2018-12-26 12:25:34 -08:00
|
|
|
AsyncEvent *evt{new (evt_vec.first.buf) AsyncEvent{EventType_ReleaseEffectState}};
|
2018-12-25 09:32:38 -08:00
|
|
|
evt->u.mEffectState = oldstate;
|
|
|
|
ll_ringbuffer_write_advance(context->AsyncEvents, 1);
|
2018-11-27 13:41:30 -08:00
|
|
|
context->EventSem.post();
|
2018-12-25 09:32:38 -08:00
|
|
|
}
|
2018-09-21 02:37:37 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* If writing the event failed, the queue was probably full.
|
|
|
|
* Store the old state in the property object where it can
|
|
|
|
* eventually be cleaned up sometime later (not ideal, but
|
|
|
|
* better than blocking or leaking).
|
|
|
|
*/
|
2018-12-25 09:32:38 -08:00
|
|
|
props->State = oldstate;
|
2018-09-21 02:37:37 -07:00
|
|
|
}
|
|
|
|
}
|
2016-05-12 18:26:33 -07:00
|
|
|
|
2018-11-19 01:20:03 -08:00
|
|
|
AtomicReplaceHead(context->FreeEffectslotProps, props);
|
2017-09-27 11:13:18 -07:00
|
|
|
}
|
2016-05-13 18:28:01 -07:00
|
|
|
|
2018-12-24 13:29:36 -08:00
|
|
|
MixParams params;
|
|
|
|
EffectTarget output;
|
|
|
|
if(ALeffectslot *target{slot->Params.Target})
|
|
|
|
{
|
|
|
|
auto iter = std::copy(std::begin(target->ChanMap), std::end(target->ChanMap),
|
|
|
|
std::begin(params.Ambi.Map));
|
|
|
|
std::fill(iter, std::end(params.Ambi.Map), BFChannelConfig{});
|
|
|
|
params.CoeffCount = 0;
|
|
|
|
params.Buffer = target->WetBuffer;
|
|
|
|
params.NumChannels = target->NumChannels;
|
|
|
|
|
|
|
|
output = EffectTarget{¶ms, ¶ms, nullptr};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ALCdevice *device{context->Device};
|
|
|
|
output = EffectTarget{&device->Dry, &device->FOAOut, &device->RealOut};
|
|
|
|
}
|
|
|
|
state->update(context, slot, &slot->Params.EffectProps, output);
|
2017-09-27 08:55:42 -07:00
|
|
|
return true;
|
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-12-24 19:29:01 -08:00
|
|
|
constexpr ChanMap MonoMap[1]{
|
2017-05-04 04:35:53 -07:00
|
|
|
{ FrontCenter, 0.0f, 0.0f }
|
2018-12-12 01:09:04 -08:00
|
|
|
}, RearMap[2]{
|
2018-12-22 16:01:14 -08:00
|
|
|
{ BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) },
|
|
|
|
{ BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) }
|
2018-12-12 01:09:04 -08:00
|
|
|
}, QuadMap[4]{
|
2018-12-22 16:01:14 -08:00
|
|
|
{ FrontLeft, Deg2Rad( -45.0f), Deg2Rad(0.0f) },
|
|
|
|
{ FrontRight, Deg2Rad( 45.0f), Deg2Rad(0.0f) },
|
|
|
|
{ BackLeft, Deg2Rad(-135.0f), Deg2Rad(0.0f) },
|
|
|
|
{ BackRight, Deg2Rad( 135.0f), Deg2Rad(0.0f) }
|
2018-12-12 01:09:04 -08:00
|
|
|
}, X51Map[6]{
|
2018-12-22 16:01:14 -08:00
|
|
|
{ FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) },
|
|
|
|
{ FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
|
|
|
|
{ FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
|
2017-05-04 04:35:53 -07:00
|
|
|
{ LFE, 0.0f, 0.0f },
|
2018-12-22 16:01:14 -08:00
|
|
|
{ SideLeft, Deg2Rad(-110.0f), Deg2Rad(0.0f) },
|
|
|
|
{ SideRight, Deg2Rad( 110.0f), Deg2Rad(0.0f) }
|
2018-12-12 01:09:04 -08:00
|
|
|
}, X61Map[7]{
|
2018-12-22 16:01:14 -08:00
|
|
|
{ FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) },
|
|
|
|
{ FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
|
|
|
|
{ FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
|
2017-05-04 04:35:53 -07:00
|
|
|
{ LFE, 0.0f, 0.0f },
|
2018-12-22 16:01:14 -08:00
|
|
|
{ BackCenter, Deg2Rad(180.0f), Deg2Rad(0.0f) },
|
|
|
|
{ SideLeft, Deg2Rad(-90.0f), Deg2Rad(0.0f) },
|
|
|
|
{ SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) }
|
2018-12-12 01:09:04 -08:00
|
|
|
}, X71Map[8]{
|
2018-12-22 16:01:14 -08:00
|
|
|
{ FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) },
|
|
|
|
{ FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
|
|
|
|
{ FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
|
2017-05-04 04:35:53 -07:00
|
|
|
{ LFE, 0.0f, 0.0f },
|
2018-12-22 16:01:14 -08:00
|
|
|
{ BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) },
|
|
|
|
{ BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) },
|
|
|
|
{ SideLeft, Deg2Rad( -90.0f), Deg2Rad(0.0f) },
|
|
|
|
{ SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) }
|
2017-05-04 04:35:53 -07:00
|
|
|
};
|
2011-06-29 23:18:49 -07:00
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev,
|
|
|
|
const ALfloat Distance, const ALfloat Spread,
|
|
|
|
const ALfloat DryGain, const ALfloat DryGainHF,
|
|
|
|
const ALfloat DryGainLF, const ALfloat *WetGain,
|
|
|
|
const ALfloat *WetGainLF, const ALfloat *WetGainHF,
|
|
|
|
ALeffectslot **SendSlots, const ALbuffer *Buffer,
|
2018-11-30 19:04:38 -08:00
|
|
|
const ALvoicePropsBase *props, const ALlistener &Listener,
|
2018-11-20 02:12:04 -08:00
|
|
|
const ALCdevice *Device)
|
2017-05-04 04:35:53 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
ChanMap StereoMap[2]{
|
2018-12-22 16:01:14 -08:00
|
|
|
{ FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) },
|
|
|
|
{ FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) }
|
2016-03-25 14:40:44 -07:00
|
|
|
};
|
2010-08-05 01:07:20 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
bool DirectChannels{props->DirectChannels != AL_FALSE};
|
|
|
|
const ChanMap *chans{nullptr};
|
|
|
|
ALsizei num_channels{0};
|
|
|
|
bool isbformat{false};
|
|
|
|
ALfloat downmix_gain{1.0f};
|
2018-12-24 19:29:01 -08:00
|
|
|
switch(Buffer->mFmtChannels)
|
2010-12-09 16:37:23 -08:00
|
|
|
{
|
|
|
|
case FmtMono:
|
2011-12-20 01:17:11 -08:00
|
|
|
chans = MonoMap;
|
2011-05-15 02:12:42 -07:00
|
|
|
num_channels = 1;
|
2017-05-04 04:35:53 -07:00
|
|
|
/* Mono buffers are never played direct. */
|
|
|
|
DirectChannels = false;
|
2010-12-09 16:37:23 -08:00
|
|
|
break;
|
2011-05-15 00:18:28 -07:00
|
|
|
|
2012-04-29 04:44:53 -07:00
|
|
|
case FmtStereo:
|
2017-05-04 04:35:53 -07:00
|
|
|
/* Convert counter-clockwise to clockwise. */
|
|
|
|
StereoMap[0].angle = -props->StereoPan[0];
|
|
|
|
StereoMap[1].angle = -props->StereoPan[1];
|
|
|
|
|
2016-02-26 16:09:06 -08:00
|
|
|
chans = StereoMap;
|
2011-06-17 16:20:18 -07:00
|
|
|
num_channels = 2;
|
2017-05-07 18:28:43 -07:00
|
|
|
downmix_gain = 1.0f / 2.0f;
|
2010-12-09 16:37:23 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FmtRear:
|
2011-12-20 01:17:11 -08:00
|
|
|
chans = RearMap;
|
2011-05-15 02:12:42 -07:00
|
|
|
num_channels = 2;
|
2017-05-07 18:28:43 -07:00
|
|
|
downmix_gain = 1.0f / 2.0f;
|
2010-12-09 16:37:23 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FmtQuad:
|
2011-12-20 01:17:11 -08:00
|
|
|
chans = QuadMap;
|
2011-05-15 02:12:42 -07:00
|
|
|
num_channels = 4;
|
2017-05-07 18:28:43 -07:00
|
|
|
downmix_gain = 1.0f / 4.0f;
|
2010-12-09 16:37:23 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FmtX51:
|
2011-12-20 01:17:11 -08:00
|
|
|
chans = X51Map;
|
2011-05-15 02:12:42 -07:00
|
|
|
num_channels = 6;
|
2017-05-07 18:28:43 -07:00
|
|
|
/* NOTE: Excludes LFE. */
|
|
|
|
downmix_gain = 1.0f / 5.0f;
|
2010-12-09 16:37:23 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FmtX61:
|
2011-12-20 01:17:11 -08:00
|
|
|
chans = X61Map;
|
2011-05-15 02:12:42 -07:00
|
|
|
num_channels = 7;
|
2017-05-07 18:28:43 -07:00
|
|
|
/* NOTE: Excludes LFE. */
|
|
|
|
downmix_gain = 1.0f / 6.0f;
|
2010-12-09 16:37:23 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FmtX71:
|
2011-12-20 01:17:11 -08:00
|
|
|
chans = X71Map;
|
2011-05-15 02:12:42 -07:00
|
|
|
num_channels = 8;
|
2017-05-07 18:28:43 -07:00
|
|
|
/* NOTE: Excludes LFE. */
|
|
|
|
downmix_gain = 1.0f / 7.0f;
|
2011-05-15 02:12:42 -07:00
|
|
|
break;
|
2014-10-31 17:18:45 -07:00
|
|
|
|
|
|
|
case FmtBFormat2D:
|
|
|
|
num_channels = 3;
|
2017-05-04 04:35:53 -07:00
|
|
|
isbformat = true;
|
|
|
|
DirectChannels = false;
|
2014-11-02 02:30:45 -08:00
|
|
|
break;
|
2014-10-31 17:18:45 -07:00
|
|
|
|
|
|
|
case FmtBFormat3D:
|
|
|
|
num_channels = 4;
|
2017-05-04 04:35:53 -07:00
|
|
|
isbformat = true;
|
|
|
|
DirectChannels = false;
|
2014-11-02 02:30:45 -08:00
|
|
|
break;
|
2011-05-15 02:12:42 -07:00
|
|
|
}
|
2018-12-25 18:49:12 -08:00
|
|
|
ASSUME(num_channels > 0);
|
2011-05-15 02:12:42 -07:00
|
|
|
|
2018-12-04 15:30:39 -08:00
|
|
|
std::for_each(std::begin(voice->Direct.Params), std::begin(voice->Direct.Params)+num_channels,
|
|
|
|
[](DirectParams ¶ms) -> void
|
|
|
|
{
|
|
|
|
params.Hrtf.Target = HrtfParams{};
|
|
|
|
ClearArray(params.Gains.Target);
|
|
|
|
}
|
|
|
|
);
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALsizei NumSends{Device->NumAuxSends};
|
2018-12-25 18:49:12 -08:00
|
|
|
ASSUME(NumSends >= 0);
|
2018-12-04 15:30:39 -08:00
|
|
|
std::for_each(voice->Send+0, voice->Send+NumSends,
|
|
|
|
[num_channels](ALvoice::SendData &send) -> void
|
|
|
|
{
|
|
|
|
std::for_each(std::begin(send.Params), std::begin(send.Params)+num_channels,
|
|
|
|
[](SendParams ¶ms) -> void { ClearArray(params.Gains.Target); }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
2018-03-25 18:02:07 -07:00
|
|
|
|
2017-05-02 04:25:08 -07:00
|
|
|
voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
|
2014-10-31 17:18:45 -07:00
|
|
|
if(isbformat)
|
|
|
|
{
|
2017-05-04 11:09:45 -07:00
|
|
|
/* Special handling for B-Format sources. */
|
2014-10-31 22:43:13 -07:00
|
|
|
|
2018-12-22 16:01:14 -08:00
|
|
|
if(Distance > std::numeric_limits<float>::epsilon())
|
2017-03-10 04:35:32 -08:00
|
|
|
{
|
2017-05-04 11:09:45 -07:00
|
|
|
/* Panning a B-Format sound toward some direction is easy. Just pan
|
|
|
|
* the first (W) channel as a normal mono sound and silence the
|
|
|
|
* others.
|
2017-03-10 04:35:32 -08:00
|
|
|
*/
|
2014-10-31 17:18:45 -07:00
|
|
|
|
2017-08-21 00:30:14 -07:00
|
|
|
if(Device->AvgSpeakerDist > 0.0f)
|
2016-01-28 00:02:46 -08:00
|
|
|
{
|
2018-12-22 10:00:06 -08:00
|
|
|
/* Clamp the distance for really close sources, to prevent
|
|
|
|
* excessive bass.
|
2017-05-04 11:09:45 -07:00
|
|
|
*/
|
2018-12-22 10:00:06 -08:00
|
|
|
const ALfloat mdist{maxf(Distance*Listener.Params.MetersPerUnit,
|
|
|
|
Device->AvgSpeakerDist/4.0f)};
|
|
|
|
const ALfloat w0{SPEEDOFSOUNDMETRESPERSEC /
|
|
|
|
(mdist * (ALfloat)Device->Frequency)};
|
2017-05-04 11:09:45 -07:00
|
|
|
|
|
|
|
/* Only need to adjust the first channel of a B-Format source. */
|
2018-12-05 15:20:52 -08:00
|
|
|
voice->Direct.Params[0].NFCtrlFilter.adjust(w0);
|
2017-05-04 11:09:45 -07:00
|
|
|
|
2018-12-04 15:30:39 -08:00
|
|
|
std::copy(std::begin(Device->NumChannelsPerOrder),
|
|
|
|
std::end(Device->NumChannelsPerOrder),
|
|
|
|
std::begin(voice->Direct.ChannelsPerOrder));
|
2017-05-04 11:09:45 -07:00
|
|
|
voice->Flags |= VOICE_HAS_NFC;
|
|
|
|
}
|
|
|
|
|
2018-12-22 10:00:06 -08:00
|
|
|
/* Always render B-Format sources to the FOA output, to ensure
|
|
|
|
* smooth changes if it switches between panned and unpanned.
|
|
|
|
*/
|
|
|
|
voice->Direct.Buffer = Device->FOAOut.Buffer;
|
|
|
|
voice->Direct.Channels = Device->FOAOut.NumChannels;
|
|
|
|
|
2018-08-31 17:35:57 -07:00
|
|
|
/* A scalar of 1.5 for plain stereo results in +/-60 degrees being
|
2018-08-29 01:45:27 -07:00
|
|
|
* moved to +/-90 degrees for direct right and left speaker
|
|
|
|
* responses.
|
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat coeffs[MAX_AMBI_COEFFS];
|
2018-12-24 19:29:01 -08:00
|
|
|
CalcAngleCoeffs((Device->mRenderMode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
|
2018-08-29 01:45:27 -07:00
|
|
|
Elev, Spread, coeffs);
|
2017-05-04 11:09:45 -07:00
|
|
|
|
2018-12-22 10:00:06 -08:00
|
|
|
/* NOTE: W needs to be scaled due to FuMa normalization. */
|
2018-12-25 10:06:17 -08:00
|
|
|
const ALfloat &scale0 = AmbiScale::FromFuMa[0];
|
|
|
|
ComputePanGains(&Device->FOAOut, coeffs, DryGain*scale0,
|
2018-12-12 01:09:04 -08:00
|
|
|
voice->Direct.Params[0].Gains.Target);
|
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
if(const ALeffectslot *Slot{SendSlots[i]})
|
2018-09-19 19:53:25 -07:00
|
|
|
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
|
2018-12-25 10:06:17 -08:00
|
|
|
WetGain[i]*scale0, voice->Send[i].Params[0].Gains.Target);
|
2017-05-04 11:09:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(Device->AvgSpeakerDist > 0.0f)
|
|
|
|
{
|
|
|
|
/* NOTE: The NFCtrlFilters were created with a w0 of 0, which
|
|
|
|
* is what we want for FOA input. The first channel may have
|
|
|
|
* been previously re-adjusted if panned, so reset it.
|
|
|
|
*/
|
2018-12-05 15:20:52 -08:00
|
|
|
voice->Direct.Params[0].NFCtrlFilter.adjust(0.0f);
|
2017-05-04 11:09:45 -07:00
|
|
|
|
|
|
|
voice->Direct.ChannelsPerOrder[0] = 1;
|
|
|
|
voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
|
2018-12-04 15:30:39 -08:00
|
|
|
std::fill(std::begin(voice->Direct.ChannelsPerOrder)+2,
|
|
|
|
std::end(voice->Direct.ChannelsPerOrder), 0);
|
2017-05-04 11:09:45 -07:00
|
|
|
voice->Flags |= VOICE_HAS_NFC;
|
|
|
|
}
|
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
/* Local B-Format sources have their XYZ channels rotated according
|
|
|
|
* to the orientation.
|
|
|
|
*/
|
2017-05-04 11:09:45 -07:00
|
|
|
/* AT then UP */
|
2018-12-12 04:22:11 -08:00
|
|
|
alu::Vector N{props->Orientation[0][0], props->Orientation[0][1],
|
|
|
|
props->Orientation[0][2], 0.0f};
|
|
|
|
N.normalize();
|
|
|
|
alu::Vector V{props->Orientation[1][0], props->Orientation[1][1],
|
|
|
|
props->Orientation[1][2], 0.0f};
|
|
|
|
V.normalize();
|
2017-05-04 11:09:45 -07:00
|
|
|
if(!props->HeadRelative)
|
|
|
|
{
|
2018-12-12 04:22:11 -08:00
|
|
|
N = Listener.Params.Matrix * N;
|
|
|
|
V = Listener.Params.Matrix * V;
|
2017-05-04 11:09:45 -07:00
|
|
|
}
|
|
|
|
/* Build and normalize right-vector */
|
2018-12-12 04:22:11 -08:00
|
|
|
alu::Vector U{aluCrossproduct(N, V)};
|
|
|
|
U.normalize();
|
2017-05-04 11:09:45 -07:00
|
|
|
|
2018-03-25 08:24:53 -07:00
|
|
|
/* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
|
|
|
|
* matrix is transposed, for the inputs to align on the rows and
|
|
|
|
* outputs on the columns.
|
|
|
|
*/
|
2018-12-25 10:06:17 -08:00
|
|
|
const ALfloat &scale0 = AmbiScale::FromFuMa[0];
|
|
|
|
const ALfloat &scale1 = AmbiScale::FromFuMa[1];
|
|
|
|
const ALfloat &scale2 = AmbiScale::FromFuMa[2];
|
|
|
|
const ALfloat &scale3 = AmbiScale::FromFuMa[3];
|
2018-12-12 04:22:11 -08:00
|
|
|
const alu::Matrix matrix{
|
2018-12-22 10:00:06 -08:00
|
|
|
// ACN0 ACN1 ACN2 ACN3
|
|
|
|
scale0, 0.0f, 0.0f, 0.0f, // Ambi W
|
|
|
|
0.0f, -N[0]*scale1, N[1]*scale2, -N[2]*scale3, // Ambi X
|
|
|
|
0.0f, U[0]*scale1, -U[1]*scale2, U[2]*scale3, // Ambi Y
|
|
|
|
0.0f, -V[0]*scale1, V[1]*scale2, -V[2]*scale3 // Ambi Z
|
2018-12-12 04:22:11 -08:00
|
|
|
};
|
2017-05-04 11:09:45 -07:00
|
|
|
|
|
|
|
voice->Direct.Buffer = Device->FOAOut.Buffer;
|
|
|
|
voice->Direct.Channels = Device->FOAOut.NumChannels;
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2018-12-12 04:22:11 -08:00
|
|
|
ComputePanGains(&Device->FOAOut, matrix[c].data(), DryGain,
|
2018-09-19 22:18:46 -07:00
|
|
|
voice->Direct.Params[c].Gains.Target);
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
if(const ALeffectslot *Slot{SendSlots[i]})
|
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2018-12-12 04:22:11 -08:00
|
|
|
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, matrix[c].data(),
|
2018-12-12 01:09:04 -08:00
|
|
|
WetGain[i], voice->Send[i].Params[c].Gains.Target
|
2017-05-04 11:09:45 -07:00
|
|
|
);
|
2016-01-28 00:02:46 -08:00
|
|
|
}
|
2015-10-23 20:16:11 -07:00
|
|
|
}
|
2014-10-31 17:18:45 -07:00
|
|
|
}
|
2017-05-04 04:35:53 -07:00
|
|
|
else if(DirectChannels)
|
2011-06-29 23:18:49 -07:00
|
|
|
{
|
2017-05-07 18:28:43 -07:00
|
|
|
/* Direct source channels always play local. Skip the virtual channels
|
|
|
|
* and write inputs to the matching real outputs.
|
2017-05-04 11:09:45 -07:00
|
|
|
*/
|
2017-05-04 04:35:53 -07:00
|
|
|
voice->Direct.Buffer = Device->RealOut.Buffer;
|
|
|
|
voice->Direct.Channels = Device->RealOut.NumChannels;
|
2017-05-04 11:09:45 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2017-05-04 04:35:53 -07:00
|
|
|
{
|
2018-12-20 13:26:39 -08:00
|
|
|
int idx{GetChannelIdxByName(Device->RealOut, chans[c].channel)};
|
2018-03-26 06:04:11 -07:00
|
|
|
if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
|
2017-05-04 04:35:53 -07:00
|
|
|
}
|
2016-02-14 01:22:01 -08:00
|
|
|
|
2017-05-04 11:09:45 -07:00
|
|
|
/* Auxiliary sends still use normal channel panning since they mix to
|
|
|
|
* B-Format, which can't channel-match.
|
2017-05-04 04:35:53 -07:00
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2014-11-22 13:10:32 -08:00
|
|
|
{
|
2017-05-04 04:35:53 -07:00
|
|
|
ALfloat coeffs[MAX_AMBI_COEFFS];
|
|
|
|
CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
|
2016-01-28 00:02:46 -08:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2016-01-28 00:02:46 -08:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
if(const ALeffectslot *Slot{SendSlots[i]})
|
|
|
|
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
|
|
|
|
WetGain[i], voice->Send[i].Params[c].Gains.Target
|
2017-05-04 04:35:53 -07:00
|
|
|
);
|
2016-01-28 00:02:46 -08:00
|
|
|
}
|
2012-03-12 18:27:25 -07:00
|
|
|
}
|
2017-05-04 04:35:53 -07:00
|
|
|
}
|
2018-12-24 19:29:01 -08:00
|
|
|
else if(Device->mRenderMode == HrtfRender)
|
2017-05-04 04:35:53 -07:00
|
|
|
{
|
2017-05-04 11:09:45 -07:00
|
|
|
/* Full HRTF rendering. Skip the virtual channels and render to the
|
|
|
|
* real outputs.
|
2017-05-04 04:35:53 -07:00
|
|
|
*/
|
|
|
|
voice->Direct.Buffer = Device->RealOut.Buffer;
|
|
|
|
voice->Direct.Channels = Device->RealOut.NumChannels;
|
2017-05-04 11:09:45 -07:00
|
|
|
|
2018-12-22 16:01:14 -08:00
|
|
|
if(Distance > std::numeric_limits<float>::epsilon())
|
2014-11-23 10:49:54 -08:00
|
|
|
{
|
2017-05-04 11:09:45 -07:00
|
|
|
/* Get the HRIR coefficients and delays just once, for the given
|
|
|
|
* source direction.
|
|
|
|
*/
|
2018-12-22 09:20:50 -08:00
|
|
|
GetHrtfCoeffs(Device->mHrtf, Elev, Azi, Spread,
|
2017-05-04 11:09:45 -07:00
|
|
|
voice->Direct.Params[0].Hrtf.Target.Coeffs,
|
|
|
|
voice->Direct.Params[0].Hrtf.Target.Delay);
|
2017-05-07 18:28:43 -07:00
|
|
|
voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
|
2017-05-04 04:35:53 -07:00
|
|
|
|
2017-05-04 11:09:45 -07:00
|
|
|
/* Remaining channels use the same results as the first. */
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{1};c < num_channels;c++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
|
|
|
/* Skip LFE */
|
2018-03-25 18:02:07 -07:00
|
|
|
if(chans[c].channel != LFE)
|
2017-05-04 11:09:45 -07:00
|
|
|
voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
|
2016-01-28 00:02:46 -08:00
|
|
|
}
|
|
|
|
|
2017-05-04 11:09:45 -07:00
|
|
|
/* Calculate the directional coefficients once, which apply to all
|
|
|
|
* input channels of the source sends.
|
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat coeffs[MAX_AMBI_COEFFS];
|
2018-03-29 12:11:37 -07:00
|
|
|
CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
|
2017-05-04 04:35:53 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2017-05-04 04:35:53 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
if(const ALeffectslot *Slot{SendSlots[i]})
|
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
|
|
|
/* Skip LFE */
|
2018-03-25 18:02:07 -07:00
|
|
|
if(chans[c].channel != LFE)
|
2018-12-12 01:09:04 -08:00
|
|
|
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
|
|
|
|
WetGain[i]*downmix_gain, voice->Send[i].Params[c].Gains.Target
|
2017-05-04 11:09:45 -07:00
|
|
|
);
|
|
|
|
}
|
2017-05-04 04:35:53 -07:00
|
|
|
}
|
2015-10-23 20:16:11 -07:00
|
|
|
}
|
2017-05-04 11:09:45 -07:00
|
|
|
else
|
|
|
|
{
|
2017-05-07 18:28:43 -07:00
|
|
|
/* Local sources on HRTF play with each channel panned to its
|
|
|
|
* relative location around the listener, providing "virtual
|
|
|
|
* speaker" responses.
|
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
/* Skip LFE */
|
2017-05-04 11:09:45 -07:00
|
|
|
if(chans[c].channel == LFE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Get the HRIR coefficients and delays for this channel
|
|
|
|
* position.
|
|
|
|
*/
|
2018-12-22 09:20:50 -08:00
|
|
|
GetHrtfCoeffs(Device->mHrtf, chans[c].elevation, chans[c].angle, Spread,
|
2017-05-04 11:09:45 -07:00
|
|
|
voice->Direct.Params[c].Hrtf.Target.Coeffs,
|
|
|
|
voice->Direct.Params[c].Hrtf.Target.Delay
|
|
|
|
);
|
|
|
|
voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
|
|
|
|
|
|
|
|
/* Normal panning for auxiliary sends. */
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat coeffs[MAX_AMBI_COEFFS];
|
2017-05-04 11:09:45 -07:00
|
|
|
CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
|
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
if(const ALeffectslot *Slot{SendSlots[i]})
|
|
|
|
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
|
|
|
|
WetGain[i], voice->Send[i].Params[c].Gains.Target
|
2017-05-04 11:09:45 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-05-04 04:35:53 -07:00
|
|
|
|
|
|
|
voice->Flags |= VOICE_HAS_HRTF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Non-HRTF rendering. Use normal panning to the output. */
|
2017-05-04 11:09:45 -07:00
|
|
|
|
2018-12-22 16:01:14 -08:00
|
|
|
if(Distance > std::numeric_limits<float>::epsilon())
|
2011-05-02 02:22:30 -07:00
|
|
|
{
|
2017-05-04 11:09:45 -07:00
|
|
|
/* Calculate NFC filter coefficient if needed. */
|
2017-08-21 00:30:14 -07:00
|
|
|
if(Device->AvgSpeakerDist > 0.0f)
|
2011-04-14 21:03:37 -07:00
|
|
|
{
|
2018-12-22 10:00:06 -08:00
|
|
|
/* Clamp the distance for really close sources, to prevent
|
|
|
|
* excessive bass.
|
2017-05-04 11:09:45 -07:00
|
|
|
*/
|
2018-12-22 10:00:06 -08:00
|
|
|
const ALfloat mdist{maxf(Distance*Listener.Params.MetersPerUnit,
|
|
|
|
Device->AvgSpeakerDist/4.0f)};
|
|
|
|
const ALfloat w0{SPEEDOFSOUNDMETRESPERSEC /
|
|
|
|
(mdist * (ALfloat)Device->Frequency)};
|
2017-05-04 11:09:45 -07:00
|
|
|
|
2018-02-11 21:59:59 -08:00
|
|
|
/* Adjust NFC filters. */
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2018-12-05 15:20:52 -08:00
|
|
|
voice->Direct.Params[c].NFCtrlFilter.adjust(w0);
|
2018-02-11 21:59:59 -08:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
std::copy(std::begin(Device->NumChannelsPerOrder),
|
|
|
|
std::end(Device->NumChannelsPerOrder),
|
|
|
|
std::begin(voice->Direct.ChannelsPerOrder));
|
2017-05-04 11:09:45 -07:00
|
|
|
voice->Flags |= VOICE_HAS_NFC;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate the directional coefficients once, which apply to all
|
|
|
|
* input channels.
|
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat coeffs[MAX_AMBI_COEFFS];
|
2018-12-24 19:29:01 -08:00
|
|
|
CalcAngleCoeffs((Device->mRenderMode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
|
2018-08-29 01:45:27 -07:00
|
|
|
Elev, Spread, coeffs);
|
2017-05-04 11:09:45 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
|
|
|
/* Special-case LFE */
|
|
|
|
if(chans[c].channel == LFE)
|
2016-01-28 00:02:46 -08:00
|
|
|
{
|
2017-05-04 11:09:45 -07:00
|
|
|
if(Device->Dry.Buffer == Device->RealOut.Buffer)
|
|
|
|
{
|
2018-12-20 13:26:39 -08:00
|
|
|
int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
|
2017-05-04 11:09:45 -07:00
|
|
|
if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
|
|
|
|
}
|
|
|
|
continue;
|
2016-01-28 00:02:46 -08:00
|
|
|
}
|
2017-05-04 04:35:53 -07:00
|
|
|
|
2018-09-19 22:18:46 -07:00
|
|
|
ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
|
|
|
|
voice->Direct.Params[c].Gains.Target);
|
2017-05-04 11:09:45 -07:00
|
|
|
}
|
2017-05-04 04:35:53 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2017-05-04 04:35:53 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
if(const ALeffectslot *Slot{SendSlots[i]})
|
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
|
|
|
/* Skip LFE */
|
2018-03-25 18:02:07 -07:00
|
|
|
if(chans[c].channel != LFE)
|
2018-12-12 01:09:04 -08:00
|
|
|
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
|
|
|
|
WetGain[i]*downmix_gain, voice->Send[i].Params[c].Gains.Target
|
2017-05-04 11:09:45 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(Device->AvgSpeakerDist > 0.0f)
|
|
|
|
{
|
|
|
|
/* If the source distance is 0, set w0 to w1 to act as a pass-
|
|
|
|
* through. We still want to pass the signal through the
|
|
|
|
* filters so they keep an appropriate history, in case the
|
|
|
|
* source moves away from the listener.
|
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALfloat w0{SPEEDOFSOUNDMETRESPERSEC /
|
|
|
|
(Device->AvgSpeakerDist * (ALfloat)Device->Frequency)};
|
2017-05-04 11:09:45 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2018-12-05 15:20:52 -08:00
|
|
|
voice->Direct.Params[c].NFCtrlFilter.adjust(w0);
|
2018-02-11 21:59:59 -08:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
std::copy(std::begin(Device->NumChannelsPerOrder),
|
|
|
|
std::end(Device->NumChannelsPerOrder),
|
|
|
|
std::begin(voice->Direct.ChannelsPerOrder));
|
2017-05-04 11:09:45 -07:00
|
|
|
voice->Flags |= VOICE_HAS_NFC;
|
|
|
|
}
|
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{0};c < num_channels;c++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
|
|
|
/* Special-case LFE */
|
|
|
|
if(chans[c].channel == LFE)
|
|
|
|
{
|
|
|
|
if(Device->Dry.Buffer == Device->RealOut.Buffer)
|
|
|
|
{
|
2018-12-20 13:26:39 -08:00
|
|
|
int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
|
2017-05-04 11:09:45 -07:00
|
|
|
if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat coeffs[MAX_AMBI_COEFFS];
|
2018-08-29 01:45:27 -07:00
|
|
|
CalcAngleCoeffs(
|
2018-12-24 19:29:01 -08:00
|
|
|
(Device->mRenderMode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
|
2018-08-29 01:45:27 -07:00
|
|
|
: chans[c].angle,
|
|
|
|
chans[c].elevation, Spread, coeffs
|
|
|
|
);
|
|
|
|
|
2018-09-19 22:18:46 -07:00
|
|
|
ComputePanGains(&Device->Dry, coeffs, DryGain,
|
2018-12-12 01:09:04 -08:00
|
|
|
voice->Direct.Params[c].Gains.Target);
|
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2017-05-04 11:09:45 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
if(const ALeffectslot *Slot{SendSlots[i]})
|
2017-05-04 11:09:45 -07:00
|
|
|
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
|
|
|
|
coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
|
|
|
|
);
|
|
|
|
}
|
2014-10-02 18:05:42 -07:00
|
|
|
}
|
2015-10-23 20:16:11 -07:00
|
|
|
}
|
2014-03-23 16:11:21 -07:00
|
|
|
}
|
2009-12-09 07:02:26 -08:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
const auto Frequency = static_cast<ALfloat>(Device->Frequency);
|
2013-05-27 19:14:02 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALfloat hfScale{props->Direct.HFReference / Frequency};
|
|
|
|
const ALfloat lfScale{props->Direct.LFReference / Frequency};
|
|
|
|
const ALfloat gainHF{maxf(DryGainHF, 0.001f)}; /* Limit -60dB */
|
|
|
|
const ALfloat gainLF{maxf(DryGainLF, 0.001f)};
|
2017-05-21 03:38:19 -07:00
|
|
|
|
2017-05-21 03:47:52 -07:00
|
|
|
voice->Direct.FilterType = AF_None;
|
|
|
|
if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
|
|
|
|
if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
|
2018-12-04 22:31:08 -08:00
|
|
|
voice->Direct.Params[0].LowPass.setParams(BiquadType::HighShelf,
|
2017-05-21 03:38:19 -07:00
|
|
|
gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
|
|
|
|
);
|
2018-12-04 22:31:08 -08:00
|
|
|
voice->Direct.Params[0].HighPass.setParams(BiquadType::LowShelf,
|
2017-05-21 03:38:19 -07:00
|
|
|
gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
|
|
|
|
);
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{1};c < num_channels;c++)
|
2014-05-17 07:17:48 -07:00
|
|
|
{
|
2018-12-04 22:31:08 -08:00
|
|
|
voice->Direct.Params[c].LowPass.copyParamsFrom(voice->Direct.Params[0].LowPass);
|
|
|
|
voice->Direct.Params[c].HighPass.copyParamsFrom(voice->Direct.Params[0].HighPass);
|
2014-05-17 07:17:48 -07:00
|
|
|
}
|
2013-05-27 19:14:02 -07:00
|
|
|
}
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2009-12-09 07:02:26 -08:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALfloat hfScale{props->Send[i].HFReference / Frequency};
|
|
|
|
const ALfloat lfScale{props->Send[i].LFReference / Frequency};
|
|
|
|
const ALfloat gainHF{maxf(WetGainHF[i], 0.001f)};
|
|
|
|
const ALfloat gainLF{maxf(WetGainLF[i], 0.001f)};
|
2017-05-21 03:38:19 -07:00
|
|
|
|
2017-05-21 03:47:52 -07:00
|
|
|
voice->Send[i].FilterType = AF_None;
|
|
|
|
if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
|
|
|
|
if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
|
2018-12-04 22:31:08 -08:00
|
|
|
voice->Send[i].Params[0].LowPass.setParams(BiquadType::HighShelf,
|
2017-05-21 03:38:19 -07:00
|
|
|
gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
|
|
|
|
);
|
2018-12-04 22:31:08 -08:00
|
|
|
voice->Send[i].Params[0].HighPass.setParams(BiquadType::LowShelf,
|
2017-05-21 03:38:19 -07:00
|
|
|
gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
|
|
|
|
);
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei c{1};c < num_channels;c++)
|
2014-05-17 07:17:48 -07:00
|
|
|
{
|
2018-12-04 22:31:08 -08:00
|
|
|
voice->Send[i].Params[c].LowPass.copyParamsFrom(voice->Send[i].Params[0].LowPass);
|
|
|
|
voice->Send[i].Params[c].HighPass.copyParamsFrom(voice->Send[i].Params[0].HighPass);
|
2014-05-17 07:17:48 -07:00
|
|
|
}
|
2009-12-09 07:02:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-30 19:04:38 -08:00
|
|
|
void CalcNonAttnSourceParams(ALvoice *voice, const ALvoicePropsBase *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
|
2017-05-04 04:35:53 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALCdevice *Device{ALContext->Device};
|
2017-05-04 04:35:53 -07:00
|
|
|
ALeffectslot *SendSlots[MAX_SENDS];
|
|
|
|
|
|
|
|
voice->Direct.Buffer = Device->Dry.Buffer;
|
|
|
|
voice->Direct.Channels = Device->Dry.NumChannels;
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < Device->NumAuxSends;i++)
|
2017-05-04 04:35:53 -07:00
|
|
|
{
|
|
|
|
SendSlots[i] = props->Send[i].Slot;
|
|
|
|
if(!SendSlots[i] && i == 0)
|
2018-11-20 05:01:08 -08:00
|
|
|
SendSlots[i] = ALContext->DefaultSlot.get();
|
2017-05-04 04:35:53 -07:00
|
|
|
if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
|
|
|
|
{
|
|
|
|
SendSlots[i] = NULL;
|
|
|
|
voice->Send[i].Buffer = NULL;
|
|
|
|
voice->Send[i].Channels = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
|
|
|
|
voice->Send[i].Channels = SendSlots[i]->NumChannels;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate the stepping value */
|
2018-12-12 01:09:04 -08:00
|
|
|
const auto Pitch = static_cast<ALfloat>(ALBuffer->Frequency) /
|
|
|
|
static_cast<ALfloat>(Device->Frequency) * props->Pitch;
|
2017-05-04 04:35:53 -07:00
|
|
|
if(Pitch > (ALfloat)MAX_PITCH)
|
|
|
|
voice->Step = MAX_PITCH<<FRACTIONBITS;
|
|
|
|
else
|
2018-05-04 02:05:26 -07:00
|
|
|
voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
|
2018-12-24 19:29:01 -08:00
|
|
|
if(props->mResampler == BSinc24Resampler)
|
2017-08-27 10:16:36 -07:00
|
|
|
BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
|
2018-12-24 19:29:01 -08:00
|
|
|
else if(props->mResampler == BSinc12Resampler)
|
2017-08-27 10:16:36 -07:00
|
|
|
BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
|
2018-12-24 19:29:01 -08:00
|
|
|
voice->Resampler = SelectResampler(props->mResampler);
|
2017-05-04 04:35:53 -07:00
|
|
|
|
|
|
|
/* Calculate gains */
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALlistener &Listener = ALContext->Listener;
|
|
|
|
ALfloat DryGain{clampf(props->Gain, props->MinGain, props->MaxGain)};
|
2018-11-17 23:41:11 -08:00
|
|
|
DryGain *= props->Direct.Gain * Listener.Params.Gain;
|
2017-05-04 04:35:53 -07:00
|
|
|
DryGain = minf(DryGain, GAIN_MIX_MAX);
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat DryGainHF{props->Direct.GainHF};
|
|
|
|
ALfloat DryGainLF{props->Direct.GainLF};
|
|
|
|
ALfloat WetGain[MAX_SENDS], WetGainHF[MAX_SENDS], WetGainLF[MAX_SENDS];
|
|
|
|
for(ALsizei i{0};i < Device->NumAuxSends;i++)
|
2017-05-04 04:35:53 -07:00
|
|
|
{
|
|
|
|
WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
|
2018-11-17 23:41:11 -08:00
|
|
|
WetGain[i] *= props->Send[i].Gain * Listener.Params.Gain;
|
2017-05-04 04:35:53 -07:00
|
|
|
WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
|
|
|
|
WetGainHF[i] = props->Send[i].GainHF;
|
|
|
|
WetGainLF[i] = props->Send[i].GainLF;
|
|
|
|
}
|
|
|
|
|
2018-03-29 12:11:37 -07:00
|
|
|
CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
|
2017-05-04 11:09:45 -07:00
|
|
|
WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
|
2017-05-04 04:35:53 -07:00
|
|
|
}
|
|
|
|
|
2018-11-30 19:04:38 -08:00
|
|
|
void CalcAttnSourceParams(ALvoice *voice, const ALvoicePropsBase *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALCdevice *Device{ALContext->Device};
|
|
|
|
const ALsizei NumSends{Device->NumAuxSends};
|
2018-11-17 23:41:11 -08:00
|
|
|
const ALlistener &Listener = ALContext->Listener;
|
2018-12-12 01:09:04 -08:00
|
|
|
|
|
|
|
/* Set mixing buffers and get send parameters. */
|
|
|
|
voice->Direct.Buffer = Device->Dry.Buffer;
|
|
|
|
voice->Direct.Channels = Device->Dry.NumChannels;
|
2016-01-28 00:02:46 -08:00
|
|
|
ALeffectslot *SendSlots[MAX_SENDS];
|
2009-04-11 20:04:46 -07:00
|
|
|
ALfloat RoomRolloff[MAX_SENDS];
|
2011-07-05 11:00:52 -07:00
|
|
|
ALfloat DecayDistance[MAX_SENDS];
|
2018-03-11 22:40:08 -07:00
|
|
|
ALfloat DecayLFDistance[MAX_SENDS];
|
2017-05-19 18:59:04 -07:00
|
|
|
ALfloat DecayHFDistance[MAX_SENDS];
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2011-07-03 03:34:40 -07:00
|
|
|
{
|
2017-03-08 03:38:28 -08:00
|
|
|
SendSlots[i] = props->Send[i].Slot;
|
2016-01-28 00:02:46 -08:00
|
|
|
if(!SendSlots[i] && i == 0)
|
2018-11-20 05:01:08 -08:00
|
|
|
SendSlots[i] = ALContext->DefaultSlot.get();
|
2016-05-12 18:26:33 -07:00
|
|
|
if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
|
2011-07-05 11:00:52 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
SendSlots[i] = nullptr;
|
2011-07-05 11:00:52 -07:00
|
|
|
RoomRolloff[i] = 0.0f;
|
|
|
|
DecayDistance[i] = 0.0f;
|
2018-03-11 22:40:08 -07:00
|
|
|
DecayLFDistance[i] = 0.0f;
|
2017-05-19 18:59:04 -07:00
|
|
|
DecayHFDistance[i] = 0.0f;
|
2011-07-05 11:00:52 -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
|
|
|
else if(SendSlots[i]->Params.AuxSendAuto)
|
2011-07-05 11:00:52 -07:00
|
|
|
{
|
2017-05-04 11:09:45 -07:00
|
|
|
RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
|
2018-03-11 22:18:11 -07:00
|
|
|
/* Calculate the distances to where this effect's decay reaches
|
|
|
|
* -60dB.
|
|
|
|
*/
|
2017-09-22 05:42:04 -07:00
|
|
|
DecayDistance[i] = SendSlots[i]->Params.DecayTime *
|
2018-11-17 23:41:11 -08:00
|
|
|
Listener.Params.ReverbSpeedOfSound;
|
2018-03-11 22:40:08 -07:00
|
|
|
DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
|
2017-05-19 18:59:04 -07:00
|
|
|
DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
|
2017-05-27 22:33:40 -07:00
|
|
|
if(SendSlots[i]->Params.DecayHFLimit)
|
2017-05-19 18:59:04 -07:00
|
|
|
{
|
2017-05-27 22:33:40 -07:00
|
|
|
ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
|
|
|
|
if(airAbsorption < 1.0f)
|
|
|
|
{
|
2018-03-11 22:18:11 -07:00
|
|
|
/* Calculate the distance to where this effect's air
|
|
|
|
* absorption reaches -60dB, and limit the effect's HF
|
|
|
|
* decay distance (so it doesn't take any longer to decay
|
|
|
|
* than the air would allow).
|
|
|
|
*/
|
|
|
|
ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
|
|
|
|
DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
|
2017-05-27 22:33:40 -07:00
|
|
|
}
|
2017-05-19 18:59:04 -07:00
|
|
|
}
|
2011-07-05 11:00:52 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* If the slot's auxiliary send auto is off, the data sent to the
|
|
|
|
* effect slot is the same as the dry path, sans filter effects */
|
2017-05-05 03:19:50 -07:00
|
|
|
RoomRolloff[i] = props->RolloffFactor;
|
2011-07-05 11:00:52 -07:00
|
|
|
DecayDistance[i] = 0.0f;
|
2018-03-11 22:40:08 -07:00
|
|
|
DecayLFDistance[i] = 0.0f;
|
2017-05-19 18:59:04 -07:00
|
|
|
DecayHFDistance[i] = 0.0f;
|
2011-07-05 11:00:52 -07:00
|
|
|
}
|
2011-07-05 14:14:20 -07:00
|
|
|
|
2016-01-28 00:02:46 -08:00
|
|
|
if(!SendSlots[i])
|
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
voice->Send[i].Buffer = nullptr;
|
2017-02-15 17:40:26 -08:00
|
|
|
voice->Send[i].Channels = 0;
|
2016-01-28 00:02:46 -08:00
|
|
|
}
|
2013-10-06 10:11:01 -07:00
|
|
|
else
|
2016-01-28 00:02:46 -08:00
|
|
|
{
|
2017-02-15 17:40:26 -08:00
|
|
|
voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
|
|
|
|
voice->Send[i].Channels = SendSlots[i]->NumChannels;
|
2016-01-28 00:02:46 -08:00
|
|
|
}
|
2011-07-03 03:34:40 -07:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2012-04-26 00:59:17 -07:00
|
|
|
/* Transform source to listener space (convert to head relative) */
|
2018-12-12 04:22:11 -08:00
|
|
|
alu::Vector Position{props->Position[0], props->Position[1], props->Position[2], 1.0f};
|
|
|
|
alu::Vector Velocity{props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f};
|
|
|
|
alu::Vector Direction{props->Direction[0], props->Direction[1], props->Direction[2], 0.0f};
|
2017-03-08 03:38:28 -08:00
|
|
|
if(props->HeadRelative == AL_FALSE)
|
2009-10-19 13:25:40 -07:00
|
|
|
{
|
2012-04-26 00:59:17 -07:00
|
|
|
/* Transform source vectors */
|
2018-12-12 04:22:11 -08:00
|
|
|
Position = Listener.Params.Matrix * Position;
|
|
|
|
Velocity = Listener.Params.Matrix * Velocity;
|
|
|
|
Direction = Listener.Params.Matrix * Direction;
|
2009-10-19 13:25:40 -07:00
|
|
|
}
|
|
|
|
else
|
2011-10-30 05:49:17 -07:00
|
|
|
{
|
2012-03-09 23:53:45 -08:00
|
|
|
/* Offset the source velocity to be relative of the listener velocity */
|
2018-12-12 04:22:11 -08:00
|
|
|
Velocity += Listener.Params.Velocity;
|
2011-10-30 05:49:17 -07:00
|
|
|
}
|
2009-11-22 22:36:20 -08:00
|
|
|
|
2018-12-12 04:22:11 -08:00
|
|
|
const bool directional{Direction.normalize() > 0.0f};
|
|
|
|
alu::Vector SourceToListener{-Position[0], -Position[1], -Position[2], 0.0f};
|
|
|
|
const ALfloat Distance{SourceToListener.normalize()};
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2017-05-17 22:49:34 -07:00
|
|
|
/* Initial source gain */
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat DryGain{props->Gain};
|
|
|
|
ALfloat DryGainHF{1.0f};
|
|
|
|
ALfloat DryGainLF{1.0f};
|
|
|
|
ALfloat WetGain[MAX_SENDS], WetGainHF[MAX_SENDS], WetGainLF[MAX_SENDS];
|
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2017-05-17 22:49:34 -07:00
|
|
|
{
|
|
|
|
WetGain[i] = props->Gain;
|
|
|
|
WetGainHF[i] = 1.0f;
|
|
|
|
WetGainLF[i] = 1.0f;
|
|
|
|
}
|
|
|
|
|
2012-04-26 00:59:17 -07:00
|
|
|
/* Calculate distance attenuation */
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat ClampedDist{Distance};
|
2009-10-19 13:25:40 -07:00
|
|
|
|
2018-11-17 23:41:11 -08:00
|
|
|
switch(Listener.Params.SourceDistanceModel ?
|
2018-11-18 03:39:32 -08:00
|
|
|
props->mDistanceModel : Listener.Params.mDistanceModel)
|
2009-10-19 13:25:40 -07:00
|
|
|
{
|
2018-11-17 23:02:27 -08:00
|
|
|
case DistanceModel::InverseClamped:
|
2017-05-04 11:09:45 -07:00
|
|
|
ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
|
2018-12-12 01:09:04 -08:00
|
|
|
if(props->MaxDistance < props->RefDistance) break;
|
2012-04-26 00:59:17 -07:00
|
|
|
/*fall-through*/
|
2018-11-17 23:02:27 -08:00
|
|
|
case DistanceModel::Inverse:
|
2017-05-30 05:13:54 -07:00
|
|
|
if(!(props->RefDistance > 0.0f))
|
|
|
|
ClampedDist = props->RefDistance;
|
|
|
|
else
|
2009-10-19 13:25:40 -07:00
|
|
|
{
|
2017-05-05 03:19:50 -07:00
|
|
|
ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
|
2017-05-17 22:49:34 -07:00
|
|
|
if(dist > 0.0f) DryGain *= props->RefDistance / dist;
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2007-12-18 19:03:40 -08:00
|
|
|
{
|
2017-05-04 11:09:45 -07:00
|
|
|
dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
|
2017-05-17 22:49:34 -07:00
|
|
|
if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
|
2007-12-18 19:03:40 -08:00
|
|
|
}
|
2009-10-19 13:25:40 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-11-17 23:02:27 -08:00
|
|
|
case DistanceModel::LinearClamped:
|
2017-05-04 11:09:45 -07:00
|
|
|
ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
|
2018-12-12 01:09:04 -08:00
|
|
|
if(props->MaxDistance < props->RefDistance) break;
|
2012-04-26 00:59:17 -07:00
|
|
|
/*fall-through*/
|
2018-11-17 23:02:27 -08:00
|
|
|
case DistanceModel::Linear:
|
2017-05-30 05:13:54 -07:00
|
|
|
if(!(props->MaxDistance != props->RefDistance))
|
|
|
|
ClampedDist = props->RefDistance;
|
|
|
|
else
|
2009-10-19 13:25:40 -07:00
|
|
|
{
|
2017-05-17 22:49:34 -07:00
|
|
|
ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
|
2017-05-30 05:13:54 -07:00
|
|
|
(props->MaxDistance-props->RefDistance);
|
2017-05-17 22:49:34 -07:00
|
|
|
DryGain *= maxf(1.0f - attn, 0.0f);
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2010-09-24 13:16:09 -07:00
|
|
|
{
|
2017-05-17 22:49:34 -07:00
|
|
|
attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
|
|
|
|
(props->MaxDistance-props->RefDistance);
|
|
|
|
WetGain[i] *= maxf(1.0f - attn, 0.0f);
|
2010-09-24 13:16:09 -07:00
|
|
|
}
|
2009-10-19 13:25:40 -07:00
|
|
|
}
|
|
|
|
break;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-17 23:02:27 -08:00
|
|
|
case DistanceModel::ExponentClamped:
|
2017-05-04 11:09:45 -07:00
|
|
|
ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
|
2018-12-12 01:09:04 -08:00
|
|
|
if(props->MaxDistance < props->RefDistance) break;
|
2012-04-26 00:59:17 -07:00
|
|
|
/*fall-through*/
|
2018-11-17 23:02:27 -08:00
|
|
|
case DistanceModel::Exponent:
|
2017-05-30 05:13:54 -07:00
|
|
|
if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
|
|
|
|
ClampedDist = props->RefDistance;
|
|
|
|
else
|
2009-10-19 13:25:40 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
DryGain *= std::pow(ClampedDist/props->RefDistance, -props->RolloffFactor);
|
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
|
|
|
WetGain[i] *= std::pow(ClampedDist/props->RefDistance, -RoomRolloff[i]);
|
2009-10-19 13:25:40 -07:00
|
|
|
}
|
|
|
|
break;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-17 23:02:27 -08:00
|
|
|
case DistanceModel::Disable:
|
2017-05-04 11:09:45 -07:00
|
|
|
ClampedDist = props->RefDistance;
|
2009-10-19 13:25:40 -07:00
|
|
|
break;
|
|
|
|
}
|
2009-04-11 20:27:55 -07:00
|
|
|
|
2011-09-30 17:51:21 -07:00
|
|
|
/* Calculate directional soundcones */
|
2017-05-20 02:22:11 -07:00
|
|
|
if(directional && props->InnerAngle < 360.0f)
|
2009-10-19 13:25:40 -07:00
|
|
|
{
|
2018-12-22 16:01:14 -08:00
|
|
|
const ALfloat Angle{Rad2Deg(std::acos(aluDotproduct(Direction, SourceToListener)) *
|
|
|
|
ConeScale * 2.0f)};
|
2018-12-12 01:09:04 -08:00
|
|
|
|
|
|
|
ALfloat ConeVolume, ConeHF;
|
2017-05-20 02:22:11 -07:00
|
|
|
if(!(Angle > props->InnerAngle))
|
2016-05-16 22:42:41 -07:00
|
|
|
{
|
2017-05-20 02:22:11 -07:00
|
|
|
ConeVolume = 1.0f;
|
|
|
|
ConeHF = 1.0f;
|
|
|
|
}
|
|
|
|
else if(Angle < props->OuterAngle)
|
|
|
|
{
|
|
|
|
ALfloat scale = ( Angle-props->InnerAngle) /
|
|
|
|
(props->OuterAngle-props->InnerAngle);
|
|
|
|
ConeVolume = lerp(1.0f, props->OuterGain, scale);
|
|
|
|
ConeHF = lerp(1.0f, props->OuterGainHF, scale);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ConeVolume = props->OuterGain;
|
|
|
|
ConeHF = props->OuterGainHF;
|
2016-05-16 22:42:41 -07:00
|
|
|
}
|
|
|
|
|
2017-05-20 02:22:11 -07:00
|
|
|
DryGain *= ConeVolume;
|
|
|
|
if(props->DryGainHFAuto)
|
|
|
|
DryGainHF *= ConeHF;
|
2017-05-04 11:09:45 -07:00
|
|
|
if(props->WetGainAuto)
|
2018-12-12 01:09:04 -08:00
|
|
|
std::transform(std::begin(WetGain), std::begin(WetGain)+NumSends, std::begin(WetGain),
|
|
|
|
[ConeVolume](ALfloat gain) noexcept -> ALfloat { return gain * ConeVolume; }
|
|
|
|
);
|
2017-05-04 11:09:45 -07:00
|
|
|
if(props->WetGainHFAuto)
|
2018-12-12 01:09:04 -08:00
|
|
|
std::transform(std::begin(WetGainHF), std::begin(WetGainHF)+NumSends,
|
|
|
|
std::begin(WetGainHF),
|
|
|
|
[ConeHF](ALfloat gain) noexcept -> ALfloat { return gain * ConeHF; }
|
|
|
|
);
|
2011-07-05 11:00:52 -07:00
|
|
|
}
|
2009-12-08 14:18:07 -08:00
|
|
|
|
2012-04-26 00:59:17 -07:00
|
|
|
/* Apply gain and frequency filters */
|
2018-12-12 01:09:04 -08:00
|
|
|
DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
|
|
|
|
DryGain = minf(DryGain*props->Direct.Gain*Listener.Params.Gain, GAIN_MIX_MAX);
|
2017-03-08 03:38:28 -08:00
|
|
|
DryGainHF *= props->Direct.GainHF;
|
|
|
|
DryGainLF *= props->Direct.GainLF;
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2011-07-05 11:00:52 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
|
|
|
|
WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener.Params.Gain, GAIN_MIX_MAX);
|
2017-03-08 03:38:28 -08:00
|
|
|
WetGainHF[i] *= props->Send[i].GainHF;
|
|
|
|
WetGainLF[i] *= props->Send[i].GainLF;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2008-01-19 00:49:05 -08:00
|
|
|
|
2018-03-29 15:41:51 -07:00
|
|
|
/* Distance-based air absorption and initial send decay. */
|
|
|
|
if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
|
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat meters_base{(ClampedDist-props->RefDistance) * props->RolloffFactor *
|
|
|
|
Listener.Params.MetersPerUnit};
|
2018-03-29 15:41:51 -07:00
|
|
|
if(props->AirAbsorptionFactor > 0.0f)
|
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat hfattn{std::pow(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor)};
|
2018-03-29 15:41:51 -07:00
|
|
|
DryGainHF *= hfattn;
|
2018-12-12 01:09:04 -08:00
|
|
|
std::transform(std::begin(WetGainHF), std::begin(WetGainHF)+NumSends,
|
|
|
|
std::begin(WetGainHF),
|
|
|
|
[hfattn](ALfloat gain) noexcept -> ALfloat { return gain * hfattn; }
|
|
|
|
);
|
2018-03-29 15:41:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if(props->WetGainAuto)
|
|
|
|
{
|
|
|
|
/* Apply a decay-time transformation to the wet path, based on the
|
|
|
|
* source distance in meters. The initial decay of the reverb
|
|
|
|
* effect is calculated and applied to the wet path.
|
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumSends;i++)
|
2018-03-29 15:41:51 -07:00
|
|
|
{
|
|
|
|
if(!(DecayDistance[i] > 0.0f))
|
|
|
|
continue;
|
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALfloat gain{std::pow(REVERB_DECAY_GAIN, meters_base/DecayDistance[i])};
|
2018-03-29 15:41:51 -07:00
|
|
|
WetGain[i] *= gain;
|
|
|
|
/* Yes, the wet path's air absorption is applied with
|
|
|
|
* WetGainAuto on, rather than WetGainHFAuto.
|
|
|
|
*/
|
|
|
|
if(gain > 0.0f)
|
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat gainhf{std::pow(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i])};
|
2018-03-29 15:41:51 -07:00
|
|
|
WetGainHF[i] *= minf(gainhf / gain, 1.0f);
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat gainlf{std::pow(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i])};
|
2018-03-29 15:41:51 -07:00
|
|
|
WetGainLF[i] *= minf(gainlf / gain, 1.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-17 22:49:34 -07:00
|
|
|
|
|
|
|
/* Initial source pitch */
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat Pitch{props->Pitch};
|
2017-05-17 22:49:34 -07:00
|
|
|
|
2012-04-26 00:59:17 -07:00
|
|
|
/* Calculate velocity-based doppler effect */
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat DopplerFactor{props->DopplerFactor * Listener.Params.DopplerFactor};
|
2012-03-18 08:20:08 -07:00
|
|
|
if(DopplerFactor > 0.0f)
|
2009-12-01 03:32:04 -08:00
|
|
|
{
|
2018-12-12 04:22:11 -08:00
|
|
|
const alu::Vector &lvelocity = Listener.Params.Velocity;
|
|
|
|
ALfloat vss{aluDotproduct(Velocity, SourceToListener) * DopplerFactor};
|
|
|
|
ALfloat vls{aluDotproduct(lvelocity, SourceToListener) * DopplerFactor};
|
2017-05-20 03:28:40 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALfloat SpeedOfSound{Listener.Params.SpeedOfSound};
|
2017-05-20 03:28:40 -07:00
|
|
|
if(!(vls < SpeedOfSound))
|
2012-03-18 08:20:08 -07:00
|
|
|
{
|
2017-05-20 03:28:40 -07:00
|
|
|
/* Listener moving away from the source at the speed of sound.
|
|
|
|
* Sound waves can't catch it.
|
|
|
|
*/
|
|
|
|
Pitch = 0.0f;
|
|
|
|
}
|
|
|
|
else if(!(vss < SpeedOfSound))
|
|
|
|
{
|
|
|
|
/* Source moving toward the listener at the speed of sound. Sound
|
|
|
|
* waves bunch up to extreme frequencies.
|
|
|
|
*/
|
2018-12-22 16:01:14 -08:00
|
|
|
Pitch = std::numeric_limits<float>::infinity();
|
2017-05-20 03:28:40 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Source and listener movement is nominal. Calculate the proper
|
|
|
|
* doppler shift.
|
|
|
|
*/
|
|
|
|
Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
|
2012-03-18 08:20:08 -07:00
|
|
|
}
|
2009-12-01 03:32:04 -08:00
|
|
|
}
|
2010-08-07 00:38:02 -07:00
|
|
|
|
2017-05-17 22:49:34 -07:00
|
|
|
/* Adjust pitch based on the buffer and output frequencies, and calculate
|
|
|
|
* fixed-point stepping value.
|
2016-05-09 14:22:26 -07:00
|
|
|
*/
|
2017-05-17 22:49:34 -07:00
|
|
|
Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
|
2016-05-09 14:22:26 -07:00
|
|
|
if(Pitch > (ALfloat)MAX_PITCH)
|
|
|
|
voice->Step = MAX_PITCH<<FRACTIONBITS;
|
|
|
|
else
|
2018-05-04 02:05:26 -07:00
|
|
|
voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
|
2018-12-24 19:29:01 -08:00
|
|
|
if(props->mResampler == BSinc24Resampler)
|
2017-08-27 10:16:36 -07:00
|
|
|
BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
|
2018-12-24 19:29:01 -08:00
|
|
|
else if(props->mResampler == BSinc12Resampler)
|
2017-08-27 10:16:36 -07:00
|
|
|
BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
|
2018-12-24 19:29:01 -08:00
|
|
|
voice->Resampler = SelectResampler(props->mResampler);
|
2009-12-01 03:32:04 -08:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat ev{0.0f}, az{0.0f};
|
2018-03-29 12:11:37 -07:00
|
|
|
if(Distance > 0.0f)
|
2014-11-23 10:49:54 -08:00
|
|
|
{
|
2017-05-04 11:09:45 -07:00
|
|
|
/* Clamp Y, in case rounding errors caused it to end up outside of
|
|
|
|
* -1...+1.
|
2015-10-23 20:16:11 -07:00
|
|
|
*/
|
2018-12-12 04:22:11 -08:00
|
|
|
ev = std::asin(clampf(-SourceToListener[1], -1.0f, 1.0f));
|
2018-03-29 12:11:37 -07:00
|
|
|
/* Double negation on Z cancels out; negate once for changing source-
|
|
|
|
* to-listener to listener-to-source, and again for right-handed coords
|
|
|
|
* with -Z in front.
|
|
|
|
*/
|
2018-12-12 04:22:11 -08:00
|
|
|
az = std::atan2(-SourceToListener[0], SourceToListener[2]*ZScale);
|
2014-11-23 10:49:54 -08:00
|
|
|
}
|
2018-03-29 12:11:37 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat spread{0.0f};
|
2017-05-04 11:09:45 -07:00
|
|
|
if(props->Radius > Distance)
|
|
|
|
spread = F_TAU - Distance/props->Radius*F_PI;
|
2018-03-29 12:11:37 -07:00
|
|
|
else if(Distance > 0.0f)
|
2018-12-12 01:09:04 -08:00
|
|
|
spread = std::asin(props->Radius/Distance) * 2.0f;
|
2009-12-09 07:21:59 -08:00
|
|
|
|
2018-03-29 12:11:37 -07:00
|
|
|
CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain,
|
2017-05-04 11:09:45 -07:00
|
|
|
WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
|
2016-05-16 14:46:06 -07:00
|
|
|
{
|
2018-11-23 13:12:48 -08:00
|
|
|
ALvoiceProps *props{voice->Update.exchange(nullptr, std::memory_order_acq_rel)};
|
2016-08-24 02:17:55 -07:00
|
|
|
if(!props && !force) return;
|
|
|
|
|
|
|
|
if(props)
|
2016-08-23 18:56:01 -07:00
|
|
|
{
|
2018-11-30 19:04:38 -08:00
|
|
|
voice->Props = *props;
|
2016-08-23 18:56:01 -07:00
|
|
|
|
2018-11-19 01:20:03 -08:00
|
|
|
AtomicReplaceHead(context->FreeVoiceProps, props);
|
2016-08-23 18:56:01 -07:00
|
|
|
}
|
2016-08-24 02:17:55 -07:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
ALbufferlistitem *BufferListItem{voice->current_buffer.load(std::memory_order_relaxed)};
|
|
|
|
while(BufferListItem)
|
2016-08-24 02:17:55 -07:00
|
|
|
{
|
2018-11-23 13:12:48 -08:00
|
|
|
auto buffers_end = BufferListItem->buffers+BufferListItem->num_buffers;
|
|
|
|
auto buffer = std::find_if(BufferListItem->buffers, buffers_end,
|
|
|
|
[](const ALbuffer *buffer) noexcept -> bool
|
|
|
|
{ return buffer != nullptr; }
|
|
|
|
);
|
|
|
|
if(LIKELY(buffer != buffers_end))
|
2016-08-24 02:17:55 -07:00
|
|
|
{
|
2018-12-24 19:29:01 -08:00
|
|
|
if(voice->Props.mSpatializeMode==SpatializeOn ||
|
|
|
|
(voice->Props.mSpatializeMode==SpatializeAuto && (*buffer)->mFmtChannels==FmtMono))
|
2018-11-30 19:04:38 -08:00
|
|
|
CalcAttnSourceParams(voice, &voice->Props, *buffer, context);
|
2016-08-24 02:17:55 -07:00
|
|
|
else
|
2018-11-30 19:04:38 -08:00
|
|
|
CalcNonAttnSourceParams(voice, &voice->Props, *buffer, context);
|
2016-08-24 02:17:55 -07:00
|
|
|
break;
|
|
|
|
}
|
2018-11-23 13:12:48 -08:00
|
|
|
BufferListItem = BufferListItem->next.load(std::memory_order_acquire);
|
2016-08-24 02:17:55 -07:00
|
|
|
}
|
2016-05-16 14:46:06 -07:00
|
|
|
}
|
|
|
|
|
2009-08-26 19:15:17 -07:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
void ProcessParamUpdates(ALCcontext *ctx, const ALeffectslotArray *slots)
|
2015-09-18 00:48:43 -07:00
|
|
|
{
|
2016-05-15 17:14:58 -07:00
|
|
|
IncrementRef(&ctx->UpdateCount);
|
2018-11-23 13:12:48 -08:00
|
|
|
if(LIKELY(!ctx->HoldUpdates.load(std::memory_order_acquire)))
|
2016-05-15 17:14:58 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
bool cforce{CalcContextParams(ctx)};
|
|
|
|
bool force{CalcListenerParams(ctx) || cforce};
|
2018-11-23 13:12:48 -08:00
|
|
|
std::for_each(slots->slot, slots->slot+slots->count,
|
|
|
|
[ctx,cforce,&force](ALeffectslot *slot) -> void
|
|
|
|
{ force |= CalcEffectSlotParams(slot, ctx, cforce); }
|
|
|
|
);
|
2016-05-12 18:26:33 -07:00
|
|
|
|
2018-11-23 16:16:31 -08:00
|
|
|
std::for_each(ctx->Voices, ctx->Voices+ctx->VoiceCount.load(std::memory_order_acquire),
|
2018-11-23 13:12:48 -08:00
|
|
|
[ctx,force](ALvoice *voice) -> void
|
|
|
|
{
|
2018-11-29 22:49:01 -08:00
|
|
|
ALuint sid{voice->SourceID.load(std::memory_order_acquire)};
|
|
|
|
if(sid) CalcSourceParams(voice, ctx, force);
|
2018-11-23 13:12:48 -08:00
|
|
|
}
|
|
|
|
);
|
2015-09-18 00:48:43 -07:00
|
|
|
}
|
2016-05-15 17:14:58 -07:00
|
|
|
IncrementRef(&ctx->UpdateCount);
|
2015-09-18 00:48:43 -07:00
|
|
|
}
|
|
|
|
|
2018-12-22 22:14:25 -08:00
|
|
|
void ProcessContext(ALCcontext *ctx, const ALsizei SamplesToDo)
|
2018-11-23 13:12:48 -08:00
|
|
|
{
|
2018-12-22 22:14:25 -08:00
|
|
|
ASSUME(SamplesToDo > 0);
|
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
const ALeffectslotArray *auxslots{ctx->ActiveAuxSlots.load(std::memory_order_acquire)};
|
|
|
|
|
|
|
|
/* Process pending propery updates for objects on the context. */
|
|
|
|
ProcessParamUpdates(ctx, auxslots);
|
|
|
|
|
|
|
|
/* Clear auxiliary effect slot mixing buffers. */
|
|
|
|
std::for_each(auxslots->slot, auxslots->slot+auxslots->count,
|
|
|
|
[SamplesToDo](ALeffectslot *slot) -> void
|
|
|
|
{
|
|
|
|
std::for_each(slot->WetBuffer, slot->WetBuffer+slot->NumChannels,
|
|
|
|
[SamplesToDo](ALfloat *buffer) -> void
|
|
|
|
{ std::fill_n(buffer, SamplesToDo, 0.0f); }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
/* Process voices that have a playing source. */
|
2018-11-23 16:16:31 -08:00
|
|
|
std::for_each(ctx->Voices, ctx->Voices+ctx->VoiceCount.load(std::memory_order_acquire),
|
2018-11-23 13:12:48 -08:00
|
|
|
[SamplesToDo,ctx](ALvoice *voice) -> void
|
|
|
|
{
|
2018-12-04 14:48:08 -08:00
|
|
|
if(!voice->Playing.load(std::memory_order_acquire)) return;
|
|
|
|
ALuint sid{voice->SourceID.load(std::memory_order_relaxed)};
|
|
|
|
if(!sid || voice->Step < 1) return;
|
2018-11-23 13:12:48 -08:00
|
|
|
|
2018-11-29 22:49:01 -08:00
|
|
|
if(!MixSource(voice, sid, ctx, SamplesToDo))
|
2018-11-23 13:12:48 -08:00
|
|
|
{
|
2018-11-29 22:49:01 -08:00
|
|
|
voice->SourceID.store(0u, std::memory_order_relaxed);
|
2018-11-23 13:12:48 -08:00
|
|
|
voice->Playing.store(false, std::memory_order_release);
|
2018-11-29 22:49:01 -08:00
|
|
|
SendSourceStoppedEvent(ctx, sid);
|
2018-11-23 13:12:48 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
/* Process effects. */
|
2018-12-22 22:14:25 -08:00
|
|
|
if(auxslots->count < 1) return;
|
|
|
|
auto slots = auxslots->slot;
|
|
|
|
auto slots_end = slots + auxslots->count;
|
|
|
|
|
|
|
|
/* First sort the slots into scratch storage, so that effects come before
|
|
|
|
* their effect target (or their targets' target).
|
|
|
|
*/
|
|
|
|
auto sorted_slots = const_cast<ALeffectslot**>(slots_end);
|
|
|
|
auto sorted_slots_end = sorted_slots;
|
|
|
|
auto in_chain = [](const ALeffectslot *slot1, const ALeffectslot *slot2) noexcept -> bool
|
|
|
|
{
|
|
|
|
while((slot1=slot1->Params.Target) != nullptr) {
|
|
|
|
if(slot1 == slot2) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
*sorted_slots_end = *slots;
|
|
|
|
++sorted_slots_end;
|
|
|
|
while(++slots != slots_end)
|
|
|
|
{
|
|
|
|
/* If this effect slot targets an effect slot already in the list (i.e.
|
|
|
|
* slots outputs to something in sorted_slots), directly or indirectly,
|
|
|
|
* insert it prior to that element.
|
|
|
|
*/
|
|
|
|
auto checker = sorted_slots;
|
|
|
|
do {
|
|
|
|
if(in_chain(*slots, *checker)) break;
|
|
|
|
} while(++checker != sorted_slots_end);
|
|
|
|
|
|
|
|
checker = std::move_backward(checker, sorted_slots_end, sorted_slots_end+1);
|
|
|
|
*--checker = *slots;
|
|
|
|
++sorted_slots_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::for_each(sorted_slots, sorted_slots_end,
|
2018-11-23 13:12:48 -08:00
|
|
|
[SamplesToDo](const ALeffectslot *slot) -> void
|
|
|
|
{
|
|
|
|
EffectState *state{slot->Params.mEffectState};
|
|
|
|
state->process(SamplesToDo, slot->WetBuffer, state->mOutBuffer,
|
|
|
|
state->mOutChannels);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2015-09-18 00:48:43 -07:00
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*RESTRICT Buffer)[BUFFERSIZE],
|
2018-12-23 17:56:01 -08:00
|
|
|
int lidx, int ridx, int cidx, const ALsizei SamplesToDo,
|
|
|
|
const ALsizei NumChannels)
|
2017-07-31 23:49:48 -07:00
|
|
|
{
|
2018-12-23 17:56:01 -08:00
|
|
|
ASSUME(SamplesToDo > 0);
|
|
|
|
ASSUME(NumChannels > 0);
|
|
|
|
|
2017-07-31 23:49:48 -07:00
|
|
|
/* Apply an all-pass to all channels, except the front-left and front-
|
|
|
|
* right, so they maintain the same relative phase.
|
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < NumChannels;i++)
|
2017-07-31 23:49:48 -07:00
|
|
|
{
|
|
|
|
if(i == lidx || i == ridx)
|
|
|
|
continue;
|
2018-12-05 15:38:05 -08:00
|
|
|
Stablizer->APFilter[i].process(Buffer[i], SamplesToDo);
|
2017-07-31 23:49:48 -07:00
|
|
|
}
|
|
|
|
|
2018-12-23 20:56:27 -08:00
|
|
|
ALfloat (&lsplit)[2][BUFFERSIZE] = Stablizer->LSplit;
|
|
|
|
ALfloat (&rsplit)[2][BUFFERSIZE] = Stablizer->RSplit;
|
2018-12-05 15:38:05 -08:00
|
|
|
Stablizer->LFilter.process(lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
|
|
|
|
Stablizer->RFilter.process(rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
|
2017-07-31 23:49:48 -07:00
|
|
|
|
2018-12-12 01:09:04 -08:00
|
|
|
for(ALsizei i{0};i < SamplesToDo;i++)
|
2017-07-31 23:49:48 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat lfsum{lsplit[0][i] + rsplit[0][i]};
|
|
|
|
ALfloat hfsum{lsplit[1][i] + rsplit[1][i]};
|
|
|
|
ALfloat s{lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i]};
|
2017-07-31 23:49:48 -07:00
|
|
|
|
|
|
|
/* This pans the separate low- and high-frequency sums between being on
|
|
|
|
* the center channel and the left/right channels. The low-frequency
|
|
|
|
* sum is 1/3rd toward center (2/3rds on left/right) and the high-
|
|
|
|
* frequency sum is 1/4th toward center (3/4ths on left/right). These
|
|
|
|
* values can be tweaked.
|
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat m{lfsum*std::cos(1.0f/3.0f * F_PI_2) + hfsum*std::cos(1.0f/4.0f * F_PI_2)};
|
|
|
|
ALfloat c{lfsum*std::sin(1.0f/3.0f * F_PI_2) + hfsum*std::sin(1.0f/4.0f * F_PI_2)};
|
2017-07-31 23:49:48 -07:00
|
|
|
|
|
|
|
/* The generated center channel signal adds to the existing signal,
|
|
|
|
* while the modified left and right channels replace.
|
|
|
|
*/
|
|
|
|
Buffer[lidx][i] = (m + s) * 0.5f;
|
|
|
|
Buffer[ridx][i] = (m - s) * 0.5f;
|
|
|
|
Buffer[cidx][i] += c * 0.5f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-24 07:30:01 -08:00
|
|
|
void ApplyDistanceComp(ALfloat (*Samples)[BUFFERSIZE], const DistanceComp &distcomp,
|
|
|
|
ALfloat (&Values)[BUFFERSIZE], const ALsizei SamplesToDo, const ALsizei numchans)
|
2017-05-24 23:21:08 -07:00
|
|
|
{
|
2018-12-23 17:56:01 -08:00
|
|
|
ASSUME(SamplesToDo > 0);
|
|
|
|
ASSUME(numchans > 0);
|
|
|
|
|
2018-12-24 07:30:01 -08:00
|
|
|
ALfloat *RESTRICT tempvals{al::assume_aligned<16>(&Values[0])};
|
2018-11-21 05:06:31 -08:00
|
|
|
for(ALsizei c{0};c < numchans;c++)
|
2017-05-24 23:21:08 -07:00
|
|
|
{
|
2018-12-23 20:56:27 -08:00
|
|
|
ALfloat *RESTRICT inout{al::assume_aligned<16>(Samples[c])};
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALfloat gain{distcomp[c].Gain};
|
|
|
|
const ALsizei base{distcomp[c].Length};
|
2018-12-23 20:56:27 -08:00
|
|
|
ALfloat *RESTRICT distbuf{al::assume_aligned<16>(distcomp[c].Buffer)};
|
2017-05-24 23:21:08 -07:00
|
|
|
|
2018-12-23 17:56:01 -08:00
|
|
|
if(base <= 0)
|
2017-05-24 23:21:08 -07:00
|
|
|
{
|
|
|
|
if(gain < 1.0f)
|
2018-12-23 17:56:01 -08:00
|
|
|
std::transform(inout, inout+SamplesToDo, inout,
|
|
|
|
[gain](const ALfloat in) noexcept -> ALfloat
|
|
|
|
{ return in * gain; }
|
2018-11-21 05:06:31 -08:00
|
|
|
);
|
2017-05-24 23:21:08 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-05-23 19:22:21 -07:00
|
|
|
if(LIKELY(SamplesToDo >= base))
|
2017-05-24 23:21:08 -07:00
|
|
|
{
|
2018-12-24 07:30:01 -08:00
|
|
|
auto out = std::copy_n(distbuf, base, tempvals);
|
2018-11-21 05:06:31 -08:00
|
|
|
std::copy_n(inout, SamplesToDo-base, out);
|
|
|
|
std::copy_n(inout+SamplesToDo-base, base, distbuf);
|
2017-05-24 23:21:08 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-24 07:30:01 -08:00
|
|
|
std::copy_n(distbuf, SamplesToDo, tempvals);
|
2018-11-21 05:06:31 -08:00
|
|
|
auto out = std::copy(distbuf+SamplesToDo, distbuf+base, distbuf);
|
|
|
|
std::copy_n(inout, SamplesToDo, out);
|
2017-05-24 23:21:08 -07:00
|
|
|
}
|
2018-12-24 07:30:01 -08:00
|
|
|
std::transform(tempvals, tempvals+SamplesToDo, inout,
|
2018-12-23 17:56:01 -08:00
|
|
|
[gain](const ALfloat in) noexcept -> ALfloat { return in * gain; }
|
2018-11-21 05:06:31 -08:00
|
|
|
);
|
2017-05-24 23:21:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-23 20:56:27 -08:00
|
|
|
void ApplyDither(ALfloat (*Samples)[BUFFERSIZE], ALuint *dither_seed, const ALfloat quant_scale,
|
|
|
|
const ALsizei SamplesToDo, const ALsizei numchans)
|
2017-06-17 01:11:21 -07:00
|
|
|
{
|
2018-05-23 19:22:21 -07:00
|
|
|
ASSUME(numchans > 0);
|
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
/* Dithering. Generate whitenoise (uniform distribution of random values
|
|
|
|
* between -1 and +1) and add it to the sample values, after scaling up to
|
|
|
|
* the desired quantization depth amd before rounding.
|
2017-06-17 01:11:21 -07:00
|
|
|
*/
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALfloat invscale{1.0f / quant_scale};
|
|
|
|
ALuint seed{*dither_seed};
|
2018-12-23 20:56:27 -08:00
|
|
|
auto dither_channel = [&seed,invscale,quant_scale,SamplesToDo](ALfloat *input) -> void
|
2017-06-17 01:11:21 -07:00
|
|
|
{
|
2018-11-23 13:12:48 -08:00
|
|
|
ASSUME(SamplesToDo > 0);
|
2018-12-23 20:56:27 -08:00
|
|
|
ALfloat *buffer{al::assume_aligned<16>(input)};
|
2018-11-23 13:12:48 -08:00
|
|
|
std::transform(buffer, buffer+SamplesToDo, buffer,
|
|
|
|
[&seed,invscale,quant_scale](ALfloat sample) noexcept -> ALfloat
|
|
|
|
{
|
|
|
|
ALfloat val = sample * quant_scale;
|
|
|
|
ALuint rng0 = dither_rng(&seed);
|
|
|
|
ALuint rng1 = dither_rng(&seed);
|
|
|
|
val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
|
|
|
|
return fast_roundf(val) * invscale;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
std::for_each(Samples, Samples+numchans, dither_channel);
|
2017-06-17 01:11:21 -07:00
|
|
|
*dither_seed = seed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
/* Base template left undefined. Should be marked =delete, but Clang 3.8.1
|
|
|
|
* chokes on that given the inline specializations.
|
|
|
|
*/
|
|
|
|
template<typename T>
|
2018-11-23 13:12:48 -08:00
|
|
|
inline T SampleConv(ALfloat) noexcept;
|
2018-11-20 02:12:04 -08:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
template<> inline ALfloat SampleConv(ALfloat val) noexcept
|
2017-05-23 00:02:04 -07:00
|
|
|
{ return val; }
|
2018-11-23 13:12:48 -08:00
|
|
|
template<> inline ALint SampleConv(ALfloat val) noexcept
|
2015-09-07 03:27:25 -07:00
|
|
|
{
|
2018-05-21 06:16:03 -07:00
|
|
|
/* Floats have a 23-bit mantissa. There is an implied 1 bit in the mantissa
|
|
|
|
* along with the sign bit, giving 25 bits total, so [-16777216, +16777216]
|
|
|
|
* is the max value a normalized float can be scaled to before losing
|
2018-04-28 18:52:40 -07:00
|
|
|
* precision.
|
2015-09-07 03:27:25 -07:00
|
|
|
*/
|
2017-05-23 00:02:04 -07:00
|
|
|
return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
|
|
|
|
}
|
2018-11-23 13:12:48 -08:00
|
|
|
template<> inline ALshort SampleConv(ALfloat val) noexcept
|
2017-05-23 00:02:04 -07:00
|
|
|
{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
|
2018-11-23 13:12:48 -08:00
|
|
|
template<> inline ALbyte SampleConv(ALfloat val) noexcept
|
2017-05-23 00:02:04 -07:00
|
|
|
{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
|
|
|
|
|
|
|
|
/* Define unsigned output variations. */
|
2018-11-23 13:12:48 -08:00
|
|
|
template<> inline ALuint SampleConv(ALfloat val) noexcept
|
2018-11-20 02:12:04 -08:00
|
|
|
{ return SampleConv<ALint>(val) + 2147483648u; }
|
2018-11-23 13:12:48 -08:00
|
|
|
template<> inline ALushort SampleConv(ALfloat val) noexcept
|
2018-11-20 02:12:04 -08:00
|
|
|
{ return SampleConv<ALshort>(val) + 32768; }
|
2018-11-23 13:12:48 -08:00
|
|
|
template<> inline ALubyte SampleConv(ALfloat val) noexcept
|
2018-11-20 02:12:04 -08:00
|
|
|
{ return SampleConv<ALbyte>(val) + 128; }
|
|
|
|
|
|
|
|
template<DevFmtType T>
|
2018-12-12 01:09:04 -08:00
|
|
|
void Write(const ALfloat (*InBuffer)[BUFFERSIZE], ALvoid *OutBuffer, ALsizei Offset,
|
|
|
|
ALsizei SamplesToDo, ALsizei numchans)
|
2018-11-20 02:12:04 -08:00
|
|
|
{
|
|
|
|
using SampleType = typename DevFmtTypeTraits<T>::Type;
|
2010-12-01 21:50:49 -08:00
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
ASSUME(numchans > 0);
|
2018-11-23 13:12:48 -08:00
|
|
|
SampleType *outbase = static_cast<SampleType*>(OutBuffer) + Offset*numchans;
|
|
|
|
auto conv_channel = [&outbase,SamplesToDo,numchans](const ALfloat *inbuf) -> void
|
2018-11-20 02:12:04 -08:00
|
|
|
{
|
2018-11-23 13:12:48 -08:00
|
|
|
ASSUME(SamplesToDo > 0);
|
|
|
|
SampleType *out{outbase++};
|
|
|
|
std::for_each<const ALfloat*RESTRICT>(inbuf, inbuf+SamplesToDo,
|
|
|
|
[numchans,&out](const ALfloat s) noexcept -> void
|
|
|
|
{
|
|
|
|
*out = SampleConv<SampleType>(s);
|
|
|
|
out += numchans;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
std::for_each(InBuffer, InBuffer+numchans, conv_channel);
|
2018-11-20 02:12:04 -08:00
|
|
|
}
|
2010-12-01 21:50:49 -08:00
|
|
|
|
2018-11-20 02:12:04 -08:00
|
|
|
} // namespace
|
2010-12-01 21:50:49 -08:00
|
|
|
|
2017-07-15 23:13:08 -07:00
|
|
|
void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
2010-11-21 02:51:18 -08:00
|
|
|
{
|
2018-11-21 09:07:02 -08:00
|
|
|
FPUCtl mixer_mode{};
|
2018-11-23 13:12:48 -08:00
|
|
|
for(ALsizei SamplesDone{0};SamplesDone < NumSamples;)
|
2010-11-21 02:51:18 -08:00
|
|
|
{
|
2018-11-23 13:12:48 -08:00
|
|
|
const ALsizei SamplesToDo{mini(NumSamples-SamplesDone, BUFFERSIZE)};
|
|
|
|
|
|
|
|
/* Clear main mixing buffers. */
|
|
|
|
std::for_each(device->MixBuffer.begin(), device->MixBuffer.end(),
|
|
|
|
[SamplesToDo](std::array<ALfloat,BUFFERSIZE> &buffer) -> void
|
|
|
|
{ std::fill_n(buffer.begin(), SamplesToDo, 0.0f); }
|
|
|
|
);
|
2010-11-21 02:51:18 -08:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
/* Increment the mix count at the start (lsb should now be 1). */
|
2016-05-23 01:03:37 -07:00
|
|
|
IncrementRef(&device->MixCount);
|
2015-09-12 23:36:24 -07:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
/* For each context on this device, process and mix its sources and
|
|
|
|
* effects.
|
|
|
|
*/
|
|
|
|
ALCcontext *ctx{device->ContextList.load(std::memory_order_acquire)};
|
2011-08-28 19:28:41 -07:00
|
|
|
while(ctx)
|
2010-11-21 02:51:18 -08:00
|
|
|
{
|
2018-11-23 13:12:48 -08:00
|
|
|
ProcessContext(ctx, SamplesToDo);
|
2012-10-09 06:19:36 -07:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
ctx = ctx->next.load(std::memory_order_relaxed);
|
2016-01-27 08:16:47 -08:00
|
|
|
}
|
2014-02-01 16:37:11 -08:00
|
|
|
|
|
|
|
/* Increment the clock time. Every second's worth of samples is
|
|
|
|
* converted and added to clock base so that large sample counts don't
|
2018-11-23 13:12:48 -08:00
|
|
|
* overflow during conversion. This also guarantees a stable
|
|
|
|
* conversion.
|
|
|
|
*/
|
2014-02-01 16:37:11 -08:00
|
|
|
device->SamplesDone += SamplesToDo;
|
2018-11-22 12:53:16 -08:00
|
|
|
device->ClockBase += std::chrono::seconds{device->SamplesDone / device->Frequency};
|
2014-02-01 16:37:11 -08:00
|
|
|
device->SamplesDone %= device->Frequency;
|
2018-11-23 13:12:48 -08:00
|
|
|
|
|
|
|
/* Increment the mix count at the end (lsb should now be 0). */
|
2016-05-23 01:03:37 -07:00
|
|
|
IncrementRef(&device->MixCount);
|
2010-11-21 02:51:18 -08:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
/* Apply any needed post-process for finalizing the Dry mix to the
|
|
|
|
* RealOut (Ambisonic decode, UHJ encode, etc).
|
2018-02-10 15:50:05 -08:00
|
|
|
*/
|
|
|
|
if(LIKELY(device->PostProcess))
|
|
|
|
device->PostProcess(device, SamplesToDo);
|
2010-11-21 02:51:18 -08:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
/* Apply front image stablization for surround sound, if applicable. */
|
2018-03-01 16:16:37 -08:00
|
|
|
if(device->Stablizer)
|
2010-11-21 02:51:18 -08:00
|
|
|
{
|
2018-12-20 13:26:39 -08:00
|
|
|
const int lidx{GetChannelIdxByName(device->RealOut, FrontLeft)};
|
|
|
|
const int ridx{GetChannelIdxByName(device->RealOut, FrontRight)};
|
|
|
|
const int cidx{GetChannelIdxByName(device->RealOut, FrontCenter)};
|
2018-03-01 16:16:37 -08:00
|
|
|
assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
|
2017-04-26 18:38:09 -07:00
|
|
|
|
2018-11-22 04:53:29 -08:00
|
|
|
ApplyStablizer(device->Stablizer.get(), device->RealOut.Buffer, lidx, ridx, cidx,
|
2018-03-01 16:16:37 -08:00
|
|
|
SamplesToDo, device->RealOut.NumChannels);
|
|
|
|
}
|
2017-07-31 23:49:48 -07:00
|
|
|
|
2018-12-24 07:30:01 -08:00
|
|
|
/* Apply compression, limiting sample amplitude if needed or desired. */
|
2018-12-24 09:17:00 -08:00
|
|
|
if(Compressor *comp{device->Limiter.get()})
|
|
|
|
comp->process(SamplesToDo, device->RealOut.Buffer);
|
2018-12-24 07:30:01 -08:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
/* Apply delays and attenuation for mismatched speaker distances. */
|
2018-03-01 16:16:37 -08:00
|
|
|
ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
|
|
|
|
SamplesToDo, device->RealOut.NumChannels);
|
2017-07-31 23:49:48 -07:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
/* Apply dithering. The compressor should have left enough headroom for
|
|
|
|
* the dither noise to not saturate.
|
|
|
|
*/
|
2018-03-01 16:16:37 -08:00
|
|
|
if(device->DitherDepth > 0.0f)
|
|
|
|
ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
|
|
|
|
SamplesToDo, device->RealOut.NumChannels);
|
2016-03-09 22:57:38 -08:00
|
|
|
|
2018-04-29 18:03:33 -07:00
|
|
|
if(LIKELY(OutBuffer))
|
2018-03-01 16:16:37 -08:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
ALfloat (*Buffer)[BUFFERSIZE]{device->RealOut.Buffer};
|
|
|
|
ALsizei Channels{device->RealOut.NumChannels};
|
2017-06-17 23:09:51 -07:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
/* Finally, interleave and convert samples, writing to the device's
|
|
|
|
* output buffer.
|
|
|
|
*/
|
2011-08-12 15:42:36 -07:00
|
|
|
switch(device->FmtType)
|
|
|
|
{
|
2018-11-20 02:12:04 -08:00
|
|
|
#define HANDLE_WRITE(T) case T: \
|
|
|
|
Write<T>(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break;
|
|
|
|
HANDLE_WRITE(DevFmtByte)
|
|
|
|
HANDLE_WRITE(DevFmtUByte)
|
|
|
|
HANDLE_WRITE(DevFmtShort)
|
|
|
|
HANDLE_WRITE(DevFmtUShort)
|
|
|
|
HANDLE_WRITE(DevFmtInt)
|
|
|
|
HANDLE_WRITE(DevFmtUInt)
|
|
|
|
HANDLE_WRITE(DevFmtFloat)
|
2018-05-27 01:03:18 -07:00
|
|
|
#undef HANDLE_WRITE
|
2011-08-12 15:42:36 -07:00
|
|
|
}
|
2010-11-21 02:51:18 -08:00
|
|
|
}
|
|
|
|
|
2017-07-15 23:13:08 -07:00
|
|
|
SamplesDone += SamplesToDo;
|
2010-11-21 02:51:18 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-03 13:54:42 -08:00
|
|
|
void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
|
2009-08-26 19:15:17 -07:00
|
|
|
{
|
2018-11-19 03:21:58 -08:00
|
|
|
if(!device->Connected.exchange(AL_FALSE, std::memory_order_acq_rel))
|
2018-02-04 00:01:12 -08:00
|
|
|
return;
|
2011-09-10 19:22:46 -07:00
|
|
|
|
2018-12-06 22:24:20 -08:00
|
|
|
AsyncEvent evt{EventType_Disconnected};
|
2018-09-20 21:59:38 -07:00
|
|
|
evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
|
|
|
|
evt.u.user.id = 0;
|
|
|
|
evt.u.user.param = 0;
|
2018-02-03 13:54:42 -08:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
va_list args;
|
2018-02-03 13:54:42 -08:00
|
|
|
va_start(args, msg);
|
2018-11-23 13:12:48 -08:00
|
|
|
int msglen{vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args)};
|
2018-02-03 13:54:42 -08:00
|
|
|
va_end(args);
|
|
|
|
|
2018-09-20 21:59:38 -07:00
|
|
|
if(msglen < 0 || (size_t)msglen >= sizeof(evt.u.user.msg))
|
|
|
|
evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0;
|
2018-02-03 13:54:42 -08:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
ALCcontext *ctx{device->ContextList.load()};
|
2017-06-25 05:42:35 -07:00
|
|
|
while(ctx)
|
2009-08-26 19:15:17 -07:00
|
|
|
{
|
2018-12-12 01:09:04 -08:00
|
|
|
const ALbitfieldSOFT enabledevt{ctx->EnabledEvts.load(std::memory_order_acquire)};
|
2018-12-25 09:32:38 -08:00
|
|
|
if((enabledevt&EventType_Disconnected))
|
|
|
|
{
|
|
|
|
auto evt_data = ll_ringbuffer_get_write_vector(ctx->AsyncEvents).first;
|
|
|
|
if(evt_data.len > 0)
|
|
|
|
{
|
|
|
|
new (evt_data.buf) AsyncEvent{evt};
|
|
|
|
ll_ringbuffer_write_advance(ctx->AsyncEvents, 1);
|
|
|
|
ctx->EventSem.post();
|
|
|
|
}
|
|
|
|
}
|
2018-02-03 01:07:06 -08:00
|
|
|
|
2018-11-23 16:16:31 -08:00
|
|
|
std::for_each(ctx->Voices, ctx->Voices+ctx->VoiceCount.load(std::memory_order_acquire),
|
2018-11-23 13:12:48 -08:00
|
|
|
[ctx](ALvoice *voice) -> void
|
2018-02-24 09:24:18 -08:00
|
|
|
{
|
2018-12-04 14:48:08 -08:00
|
|
|
if(!voice->Playing.load(std::memory_order_acquire)) return;
|
2018-11-29 22:49:01 -08:00
|
|
|
ALuint sid{voice->SourceID.load(std::memory_order_relaxed)};
|
2018-12-04 14:48:08 -08:00
|
|
|
if(!sid) return;
|
2018-11-28 14:38:26 -08:00
|
|
|
|
2018-11-29 22:49:01 -08:00
|
|
|
voice->SourceID.store(0u, std::memory_order_relaxed);
|
2018-11-23 13:12:48 -08:00
|
|
|
voice->Playing.store(false, std::memory_order_release);
|
2018-11-28 14:38:26 -08:00
|
|
|
/* If the source's voice was playing, it's now effectively
|
|
|
|
* stopped (the source state will be updated the next time it's
|
|
|
|
* checked).
|
|
|
|
*/
|
2018-11-29 22:49:01 -08:00
|
|
|
SendSourceStoppedEvent(ctx, sid);
|
2018-02-24 09:24:18 -08:00
|
|
|
}
|
2018-11-23 13:12:48 -08:00
|
|
|
);
|
2011-08-28 19:28:41 -07:00
|
|
|
|
2018-11-23 13:12:48 -08:00
|
|
|
ctx = ctx->next.load(std::memory_order_relaxed);
|
2009-08-26 19:15:17 -07:00
|
|
|
}
|
|
|
|
}
|