2010-08-03 00:21:36 -07:00
|
|
|
/**
|
|
|
|
* OpenAL cross platform audio library
|
|
|
|
* Copyright (C) 1999-2010 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.
|
2010-08-03 00:21:36 -07:00
|
|
|
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
2018-12-10 21:30:22 -08:00
|
|
|
#include <cmath>
|
2018-12-09 17:24:00 -08:00
|
|
|
#include <numeric>
|
2018-12-08 14:22:20 -08:00
|
|
|
#include <algorithm>
|
2018-12-10 02:13:57 -08:00
|
|
|
#include <functional>
|
2018-12-08 14:22:20 -08:00
|
|
|
|
2010-08-03 00:21:36 -07:00
|
|
|
#include "alMain.h"
|
2016-01-28 00:02:46 -08:00
|
|
|
#include "alAuxEffectSlot.h"
|
2010-08-03 00:21:36 -07:00
|
|
|
#include "alu.h"
|
2018-01-11 07:56:54 -08:00
|
|
|
#include "alconfig.h"
|
2016-03-15 05:08:05 -07:00
|
|
|
#include "ambdec.h"
|
|
|
|
#include "bformatdec.h"
|
2018-04-21 23:23:46 -07:00
|
|
|
#include "filters/splitter.h"
|
2016-04-14 15:25:12 -07:00
|
|
|
#include "uhjfilter.h"
|
|
|
|
#include "bs2b.h"
|
2010-08-03 00:21:36 -07:00
|
|
|
|
|
|
|
|
2018-12-20 03:26:46 -08:00
|
|
|
constexpr std::array<float,MAX_AMBI_COEFFS> AmbiScale::FromN3D;
|
|
|
|
constexpr std::array<float,MAX_AMBI_COEFFS> AmbiScale::FromSN3D;
|
|
|
|
constexpr std::array<float,MAX_AMBI_COEFFS> AmbiScale::FromFuMa;
|
|
|
|
constexpr std::array<int,MAX_AMBI_COEFFS> AmbiIndex::FromFuMa;
|
|
|
|
constexpr std::array<int,MAX_AMBI_COEFFS> AmbiIndex::FromACN;
|
2018-12-20 04:19:35 -08:00
|
|
|
constexpr std::array<int,MAX_AMBI2D_COEFFS> AmbiIndex::From2D;
|
|
|
|
constexpr std::array<int,MAX_AMBI_COEFFS> AmbiIndex::From3D;
|
2018-11-12 19:02:38 -08:00
|
|
|
|
2018-12-15 23:28:49 -08:00
|
|
|
|
|
|
|
namespace {
|
2015-08-28 10:58:30 -07:00
|
|
|
|
2018-12-15 00:38:38 -08:00
|
|
|
inline const char *GetLabelFromChannel(Channel channel)
|
2014-11-15 00:19:56 -08:00
|
|
|
{
|
|
|
|
switch(channel)
|
|
|
|
{
|
|
|
|
case FrontLeft: return "front-left";
|
|
|
|
case FrontRight: return "front-right";
|
|
|
|
case FrontCenter: return "front-center";
|
|
|
|
case LFE: return "lfe";
|
|
|
|
case BackLeft: return "back-left";
|
|
|
|
case BackRight: return "back-right";
|
|
|
|
case BackCenter: return "back-center";
|
|
|
|
case SideLeft: return "side-left";
|
|
|
|
case SideRight: return "side-right";
|
2014-11-22 04:20:17 -08:00
|
|
|
|
2016-02-20 00:53:01 -08:00
|
|
|
case UpperFrontLeft: return "upper-front-left";
|
|
|
|
case UpperFrontRight: return "upper-front-right";
|
|
|
|
case UpperBackLeft: return "upper-back-left";
|
|
|
|
case UpperBackRight: return "upper-back-right";
|
|
|
|
case LowerFrontLeft: return "lower-front-left";
|
|
|
|
case LowerFrontRight: return "lower-front-right";
|
|
|
|
case LowerBackLeft: return "lower-back-left";
|
|
|
|
case LowerBackRight: return "lower-back-right";
|
|
|
|
|
2016-03-16 06:49:35 -07:00
|
|
|
case Aux0: return "aux-0";
|
|
|
|
case Aux1: return "aux-1";
|
|
|
|
case Aux2: return "aux-2";
|
|
|
|
case Aux3: return "aux-3";
|
2016-03-23 10:39:14 -07:00
|
|
|
case Aux4: return "aux-4";
|
|
|
|
case Aux5: return "aux-5";
|
|
|
|
case Aux6: return "aux-6";
|
|
|
|
case Aux7: return "aux-7";
|
|
|
|
case Aux8: return "aux-8";
|
2016-04-19 18:58:19 -07:00
|
|
|
case Aux9: return "aux-9";
|
|
|
|
case Aux10: return "aux-10";
|
|
|
|
case Aux11: return "aux-11";
|
|
|
|
case Aux12: return "aux-12";
|
|
|
|
case Aux13: return "aux-13";
|
|
|
|
case Aux14: return "aux-14";
|
|
|
|
case Aux15: return "aux-15";
|
2014-11-25 22:20:00 -08:00
|
|
|
|
2014-11-15 00:19:56 -08:00
|
|
|
case InvalidChannel: break;
|
|
|
|
}
|
|
|
|
return "(unknown)";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-22 22:53:22 -08:00
|
|
|
struct ChannelMap {
|
|
|
|
Channel ChanName;
|
2019-01-05 19:21:25 -08:00
|
|
|
ALfloat Config[MAX_AMBI2D_COEFFS];
|
2018-11-22 22:53:22 -08:00
|
|
|
};
|
2014-11-05 03:46:00 -08:00
|
|
|
|
2018-12-10 14:49:57 -08:00
|
|
|
bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
2018-12-10 14:49:57 -08:00
|
|
|
auto map_spkr = [device](const AmbDecConf::SpeakerConf &speaker) -> ALsizei
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
|
|
|
/* NOTE: AmbDec does not define any standard speaker names, however
|
|
|
|
* for this to work we have to by able to find the output channel
|
|
|
|
* the speaker definition corresponds to. Therefore, OpenAL Soft
|
|
|
|
* requires these channel labels to be recognized:
|
|
|
|
*
|
|
|
|
* LF = Front left
|
|
|
|
* RF = Front right
|
|
|
|
* LS = Side left
|
|
|
|
* RS = Side right
|
|
|
|
* LB = Back left
|
|
|
|
* RB = Back right
|
|
|
|
* CE = Front center
|
|
|
|
* CB = Back center
|
|
|
|
*
|
|
|
|
* Additionally, surround51 will acknowledge back speakers for side
|
|
|
|
* channels, and surround51rear will acknowledge side speakers for
|
|
|
|
* back channels, to avoid issues with an ambdec expecting 5.1 to
|
|
|
|
* use the side channels when the device is configured for back,
|
|
|
|
* and vice-versa.
|
|
|
|
*/
|
2018-12-10 14:49:57 -08:00
|
|
|
Channel ch{};
|
|
|
|
if(speaker.Name == "LF")
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = FrontLeft;
|
2018-12-10 14:49:57 -08:00
|
|
|
else if(speaker.Name == "RF")
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = FrontRight;
|
2018-12-10 14:49:57 -08:00
|
|
|
else if(speaker.Name == "CE")
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = FrontCenter;
|
2018-12-10 14:49:57 -08:00
|
|
|
else if(speaker.Name == "LS")
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
|
|
|
if(device->FmtChans == DevFmtX51Rear)
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = BackLeft;
|
2016-03-15 05:08:05 -07:00
|
|
|
else
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = SideLeft;
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
2018-12-10 14:49:57 -08:00
|
|
|
else if(speaker.Name == "RS")
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
|
|
|
if(device->FmtChans == DevFmtX51Rear)
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = BackRight;
|
2016-03-15 05:08:05 -07:00
|
|
|
else
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = SideRight;
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
2018-12-10 14:49:57 -08:00
|
|
|
else if(speaker.Name == "LB")
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
|
|
|
if(device->FmtChans == DevFmtX51)
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = SideLeft;
|
2016-03-15 05:08:05 -07:00
|
|
|
else
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = BackLeft;
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
2018-12-10 14:49:57 -08:00
|
|
|
else if(speaker.Name == "RB")
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
|
|
|
if(device->FmtChans == DevFmtX51)
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = SideRight;
|
2016-03-15 05:08:05 -07:00
|
|
|
else
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = BackRight;
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
2018-12-10 14:49:57 -08:00
|
|
|
else if(speaker.Name == "CB")
|
2018-02-21 19:53:18 -08:00
|
|
|
ch = BackCenter;
|
2016-03-15 05:08:05 -07:00
|
|
|
else
|
|
|
|
{
|
2018-12-10 14:49:57 -08:00
|
|
|
const char *name{speaker.Name.c_str()};
|
2016-05-12 23:41:23 -07:00
|
|
|
unsigned int n;
|
2018-02-21 19:53:18 -08:00
|
|
|
char c;
|
2016-05-12 23:41:23 -07:00
|
|
|
|
2018-02-21 19:53:18 -08:00
|
|
|
if(sscanf(name, "AUX%u%c", &n, &c) == 1 && n < 16)
|
2018-12-10 14:49:57 -08:00
|
|
|
ch = static_cast<Channel>(Aux0+n);
|
2016-05-12 23:41:23 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
ERR("AmbDec speaker label \"%s\" not recognized\n", name);
|
2018-12-10 14:49:57 -08:00
|
|
|
return -1;
|
2016-05-12 23:41:23 -07:00
|
|
|
}
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
2018-12-20 13:26:39 -08:00
|
|
|
const int chidx{GetChannelIdxByName(device->RealOut, ch)};
|
2018-02-21 19:53:18 -08:00
|
|
|
if(chidx == -1)
|
2018-12-10 14:49:57 -08:00
|
|
|
ERR("Failed to lookup AmbDec speaker label %s\n", speaker.Name.c_str());
|
|
|
|
return chidx;
|
|
|
|
};
|
2018-12-15 02:42:04 -08:00
|
|
|
std::transform(conf->Speakers.begin(), conf->Speakers.end(), std::begin(speakermap), map_spkr);
|
2018-12-10 14:49:57 -08:00
|
|
|
/* Return success if no invalid entries are found. */
|
2018-12-15 02:42:04 -08:00
|
|
|
auto speakermap_end = std::begin(speakermap) + conf->Speakers.size();
|
2018-12-10 14:49:57 -08:00
|
|
|
return std::find(std::begin(speakermap), speakermap_end, -1) == speakermap_end;
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
|
|
|
|
2016-04-14 10:44:57 -07:00
|
|
|
|
2018-12-10 02:08:54 -08:00
|
|
|
constexpr ChannelMap MonoCfg[1] = {
|
2017-01-21 11:05:05 -08:00
|
|
|
{ FrontCenter, { 1.0f } },
|
2016-04-14 10:44:57 -07:00
|
|
|
}, StereoCfg[2] = {
|
2019-01-05 19:21:25 -08:00
|
|
|
{ FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 5.52305643e-2f } },
|
|
|
|
{ FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 5.52305643e-2f } },
|
2016-04-14 10:44:57 -07:00
|
|
|
}, QuadCfg[4] = {
|
2019-01-05 19:21:25 -08:00
|
|
|
{ BackLeft, { 3.53553391e-1f, 2.04124145e-1f, -2.04124145e-1f } },
|
|
|
|
{ FrontLeft, { 3.53553391e-1f, 2.04124145e-1f, 2.04124145e-1f } },
|
|
|
|
{ FrontRight, { 3.53553391e-1f, -2.04124145e-1f, 2.04124145e-1f } },
|
|
|
|
{ BackRight, { 3.53553391e-1f, -2.04124145e-1f, -2.04124145e-1f } },
|
2017-07-31 00:13:14 -07:00
|
|
|
}, X51SideCfg[4] = {
|
2019-01-05 19:21:25 -08:00
|
|
|
{ SideLeft, { 3.33000782e-1f, 1.89084803e-1f, -2.00042375e-1f, -2.12307769e-2f, -1.14579885e-2f } },
|
|
|
|
{ FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 1.66295695e-1f, 7.30571517e-2f, 2.10901184e-2f } },
|
|
|
|
{ FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 1.66295695e-1f, -7.30571517e-2f, 2.10901184e-2f } },
|
|
|
|
{ SideRight, { 3.33000782e-1f, -1.89084803e-1f, -2.00042375e-1f, 2.12307769e-2f, -1.14579885e-2f } },
|
2017-07-31 00:13:14 -07:00
|
|
|
}, X51RearCfg[4] = {
|
2019-01-05 19:21:25 -08:00
|
|
|
{ BackLeft, { 3.33000782e-1f, 1.89084803e-1f, -2.00042375e-1f, -2.12307769e-2f, -1.14579885e-2f } },
|
|
|
|
{ FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 1.66295695e-1f, 7.30571517e-2f, 2.10901184e-2f } },
|
|
|
|
{ FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 1.66295695e-1f, -7.30571517e-2f, 2.10901184e-2f } },
|
|
|
|
{ BackRight, { 3.33000782e-1f, -1.89084803e-1f, -2.00042375e-1f, 2.12307769e-2f, -1.14579885e-2f } },
|
2016-04-14 10:44:57 -07:00
|
|
|
}, X61Cfg[6] = {
|
2019-01-05 19:21:25 -08:00
|
|
|
{ SideLeft, { 2.04460341e-1f, 2.17177926e-1f, -4.39996780e-2f, -2.60790269e-2f, -6.87239792e-2f } },
|
|
|
|
{ FrontLeft, { 1.58923161e-1f, 9.21772680e-2f, 1.59658796e-1f, 6.66278083e-2f, 3.84686854e-2f } },
|
|
|
|
{ FrontRight, { 1.58923161e-1f, -9.21772680e-2f, 1.59658796e-1f, -6.66278083e-2f, 3.84686854e-2f } },
|
|
|
|
{ SideRight, { 2.04460341e-1f, -2.17177926e-1f, -4.39996780e-2f, 2.60790269e-2f, -6.87239792e-2f } },
|
|
|
|
{ BackCenter, { 2.50001688e-1f, 0.00000000e+0f, -2.50000094e-1f, 0.00000000e+0f, 6.05133395e-2f } },
|
2017-01-21 11:05:05 -08:00
|
|
|
}, X71Cfg[6] = {
|
2019-01-05 19:21:25 -08:00
|
|
|
{ BackLeft, { 2.04124145e-1f, 1.08880247e-1f, -1.88586120e-1f, -1.29099444e-1f, 7.45355993e-2f, 3.73460789e-2f, 0.00000000e+0f } },
|
|
|
|
{ SideLeft, { 2.04124145e-1f, 2.17760495e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.49071198e-1f, -3.73460789e-2f, 0.00000000e+0f } },
|
|
|
|
{ FrontLeft, { 2.04124145e-1f, 1.08880247e-1f, 1.88586120e-1f, 1.29099444e-1f, 7.45355993e-2f, 3.73460789e-2f, 0.00000000e+0f } },
|
|
|
|
{ FrontRight, { 2.04124145e-1f, -1.08880247e-1f, 1.88586120e-1f, -1.29099444e-1f, 7.45355993e-2f, -3.73460789e-2f, 0.00000000e+0f } },
|
|
|
|
{ SideRight, { 2.04124145e-1f, -2.17760495e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.49071198e-1f, 3.73460789e-2f, 0.00000000e+0f } },
|
|
|
|
{ BackRight, { 2.04124145e-1f, -1.08880247e-1f, -1.88586120e-1f, 1.29099444e-1f, 7.45355993e-2f, -3.73460789e-2f, 0.00000000e+0f } },
|
2016-04-14 10:44:57 -07:00
|
|
|
};
|
|
|
|
|
2018-12-10 02:08:54 -08:00
|
|
|
void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order, const ALsizei *RESTRICT chans_per_order)
|
2017-03-10 04:35:32 -08:00
|
|
|
{
|
2018-12-10 14:49:57 -08:00
|
|
|
/* NFC is only used when AvgSpeakerDist is greater than 0, and can only be
|
|
|
|
* used when rendering to an ambisonic buffer.
|
|
|
|
*/
|
|
|
|
const char *devname{device->DeviceName.c_str()};
|
|
|
|
if(!GetConfigValueBool(devname, "decoder", "nfc", 1) || !(ctrl_dist > 0.0f))
|
|
|
|
return;
|
|
|
|
|
|
|
|
device->AvgSpeakerDist = minf(ctrl_dist, 10.0f);
|
|
|
|
TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
|
|
|
|
|
|
|
|
auto iter = std::copy(chans_per_order, chans_per_order+order+1,
|
|
|
|
std::begin(device->NumChannelsPerOrder));
|
|
|
|
std::fill(iter, std::end(device->NumChannelsPerOrder), 0);
|
2017-03-10 04:35:32 -08:00
|
|
|
}
|
|
|
|
|
2018-12-10 14:49:57 -08:00
|
|
|
void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
|
2017-03-10 04:35:32 -08:00
|
|
|
{
|
2018-12-10 14:49:57 -08:00
|
|
|
using namespace std::placeholders;
|
2017-03-10 04:35:32 -08:00
|
|
|
|
2018-12-10 14:49:57 -08:00
|
|
|
const ALfloat maxdist{
|
2018-12-15 02:42:04 -08:00
|
|
|
std::accumulate(conf->Speakers.begin(), conf->Speakers.end(), float{0.0f},
|
2018-12-10 14:49:57 -08:00
|
|
|
std::bind(maxf, _1, std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance), _2))
|
|
|
|
)
|
|
|
|
};
|
2017-03-10 04:35:32 -08:00
|
|
|
|
2018-12-10 14:49:57 -08:00
|
|
|
const char *devname{device->DeviceName.c_str()};
|
|
|
|
if(!GetConfigValueBool(devname, "decoder", "distance-comp", 1) || !(maxdist > 0.0f))
|
|
|
|
return;
|
2017-03-10 04:35:32 -08:00
|
|
|
|
2018-12-10 14:49:57 -08:00
|
|
|
auto srate = static_cast<ALfloat>(device->Frequency);
|
|
|
|
size_t total{0u};
|
2018-12-15 02:42:04 -08:00
|
|
|
for(size_t i{0u};i < conf->Speakers.size();i++)
|
2018-12-10 14:49:57 -08:00
|
|
|
{
|
|
|
|
const AmbDecConf::SpeakerConf &speaker = conf->Speakers[i];
|
|
|
|
const ALsizei chan{speakermap[i]};
|
|
|
|
|
|
|
|
/* Distance compensation only delays in steps of the sample rate. This
|
|
|
|
* is a bit less accurate since the delay time falls to the nearest
|
|
|
|
* sample time, but it's far simpler as it doesn't have to deal with
|
|
|
|
* phase offsets. This means at 48khz, for instance, the distance delay
|
|
|
|
* will be in steps of about 7 millimeters.
|
|
|
|
*/
|
|
|
|
const ALfloat delay{
|
|
|
|
std::floor((maxdist - speaker.Distance)/SPEEDOFSOUNDMETRESPERSEC*srate + 0.5f)
|
|
|
|
};
|
|
|
|
if(delay >= (ALfloat)MAX_DELAY_LENGTH)
|
|
|
|
ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %d)\n",
|
|
|
|
speaker.Name.c_str(), delay, MAX_DELAY_LENGTH);
|
|
|
|
|
|
|
|
device->ChannelDelay[chan].Length = static_cast<ALsizei>(clampf(
|
|
|
|
delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1)
|
|
|
|
));
|
|
|
|
device->ChannelDelay[chan].Gain = speaker.Distance / maxdist;
|
|
|
|
TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan,
|
|
|
|
speaker.Name.c_str(), device->ChannelDelay[chan].Length,
|
|
|
|
device->ChannelDelay[chan].Gain
|
|
|
|
);
|
|
|
|
|
|
|
|
/* Round up to the next 4th sample, so each channel buffer starts
|
|
|
|
* 16-byte aligned.
|
|
|
|
*/
|
|
|
|
total += RoundUp(device->ChannelDelay[chan].Length, 4);
|
2017-03-10 04:35:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if(total > 0)
|
|
|
|
{
|
2018-11-21 05:06:31 -08:00
|
|
|
device->ChannelDelay.resize(total);
|
|
|
|
device->ChannelDelay[0].Buffer = device->ChannelDelay.data();
|
2018-12-10 14:49:57 -08:00
|
|
|
auto set_bufptr = [](const DistanceComp::DistData &last, const DistanceComp::DistData &cur) -> DistanceComp::DistData
|
2017-03-10 04:35:32 -08:00
|
|
|
{
|
2018-12-10 14:49:57 -08:00
|
|
|
DistanceComp::DistData ret{cur};
|
|
|
|
ret.Buffer = last.Buffer + RoundUp(last.Length, 4);
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
std::partial_sum(device->ChannelDelay.begin(), device->ChannelDelay.end(),
|
|
|
|
device->ChannelDelay.begin(), set_bufptr);
|
2017-03-10 04:35:32 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-10 22:32:10 -08:00
|
|
|
|
2018-12-20 03:26:46 -08:00
|
|
|
auto GetAmbiScales(AmbiNorm scaletype) noexcept -> const std::array<float,MAX_AMBI_COEFFS>&
|
2018-12-10 22:32:10 -08:00
|
|
|
{
|
2018-12-20 02:46:59 -08:00
|
|
|
if(scaletype == AmbiNorm::FuMa) return AmbiScale::FromFuMa;
|
|
|
|
if(scaletype == AmbiNorm::SN3D) return AmbiScale::FromSN3D;
|
|
|
|
return AmbiScale::FromN3D;
|
2018-12-10 22:32:10 -08:00
|
|
|
}
|
|
|
|
|
2018-12-20 03:26:46 -08:00
|
|
|
auto GetAmbiLayout(AmbiLayout layouttype) noexcept -> const std::array<int,MAX_AMBI_COEFFS>&
|
2018-12-10 22:32:10 -08:00
|
|
|
{
|
2018-12-20 02:46:59 -08:00
|
|
|
if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa;
|
|
|
|
return AmbiIndex::FromACN;
|
2018-12-10 22:32:10 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-10 02:08:54 -08:00
|
|
|
void InitPanning(ALCdevice *device)
|
2016-04-14 10:44:57 -07:00
|
|
|
{
|
2018-12-15 00:38:38 -08:00
|
|
|
const ChannelMap *chanmap{nullptr};
|
|
|
|
ALsizei coeffcount{0};
|
|
|
|
ALsizei count{0};
|
2016-04-14 10:44:57 -07:00
|
|
|
|
2014-09-10 17:53:01 -07:00
|
|
|
switch(device->FmtChans)
|
2010-08-03 00:21:36 -07:00
|
|
|
{
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtMono:
|
2018-12-12 21:18:31 -08:00
|
|
|
count = static_cast<ALsizei>(COUNTOF(MonoCfg));
|
2014-11-05 03:46:00 -08:00
|
|
|
chanmap = MonoCfg;
|
2016-04-15 17:31:04 -07:00
|
|
|
coeffcount = 1;
|
2010-08-03 00:21:36 -07:00
|
|
|
break;
|
|
|
|
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtStereo:
|
2018-12-12 21:18:31 -08:00
|
|
|
count = static_cast<ALsizei>(COUNTOF(StereoCfg));
|
2014-11-05 03:46:00 -08:00
|
|
|
chanmap = StereoCfg;
|
2019-01-05 19:21:25 -08:00
|
|
|
coeffcount = 3;
|
2010-08-03 00:21:36 -07:00
|
|
|
break;
|
|
|
|
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtQuad:
|
2018-12-12 21:18:31 -08:00
|
|
|
count = static_cast<ALsizei>(COUNTOF(QuadCfg));
|
2014-11-05 03:46:00 -08:00
|
|
|
chanmap = QuadCfg;
|
2019-01-05 19:21:25 -08:00
|
|
|
coeffcount = 3;
|
2010-08-03 00:21:36 -07:00
|
|
|
break;
|
|
|
|
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtX51:
|
2018-12-12 21:18:31 -08:00
|
|
|
count = static_cast<ALsizei>(COUNTOF(X51SideCfg));
|
2014-11-05 03:46:00 -08:00
|
|
|
chanmap = X51SideCfg;
|
2019-01-05 19:21:25 -08:00
|
|
|
coeffcount = 5;
|
2011-05-28 19:35:32 -07:00
|
|
|
break;
|
|
|
|
|
2014-11-07 00:54:16 -08:00
|
|
|
case DevFmtX51Rear:
|
2018-12-12 21:18:31 -08:00
|
|
|
count = static_cast<ALsizei>(COUNTOF(X51RearCfg));
|
2014-11-07 00:54:16 -08:00
|
|
|
chanmap = X51RearCfg;
|
2019-01-05 19:21:25 -08:00
|
|
|
coeffcount = 5;
|
2014-11-07 00:54:16 -08:00
|
|
|
break;
|
|
|
|
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtX61:
|
2018-12-12 21:18:31 -08:00
|
|
|
count = static_cast<ALsizei>(COUNTOF(X61Cfg));
|
2014-11-05 03:46:00 -08:00
|
|
|
chanmap = X61Cfg;
|
2019-01-05 19:21:25 -08:00
|
|
|
coeffcount = 5;
|
2010-08-03 00:21:36 -07:00
|
|
|
break;
|
|
|
|
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtX71:
|
2018-12-12 21:18:31 -08:00
|
|
|
count = static_cast<ALsizei>(COUNTOF(X71Cfg));
|
2014-11-05 03:46:00 -08:00
|
|
|
chanmap = X71Cfg;
|
2019-01-05 19:21:25 -08:00
|
|
|
coeffcount = 7;
|
2010-08-03 00:21:36 -07:00
|
|
|
break;
|
2014-11-25 22:20:00 -08:00
|
|
|
|
2017-04-12 18:26:07 -07:00
|
|
|
case DevFmtAmbi3D:
|
2014-11-25 22:20:00 -08:00
|
|
|
break;
|
2010-08-03 00:21:36 -07:00
|
|
|
}
|
2014-11-12 19:26:53 -08:00
|
|
|
|
2017-04-12 18:26:07 -07:00
|
|
|
if(device->FmtChans == DevFmtAmbi3D)
|
2016-07-29 21:55:43 -07:00
|
|
|
{
|
2018-12-10 21:30:22 -08:00
|
|
|
const char *devname{device->DeviceName.c_str()};
|
2018-12-20 03:26:46 -08:00
|
|
|
const std::array<int,MAX_AMBI_COEFFS> &acnmap = GetAmbiLayout(device->mAmbiLayout);
|
|
|
|
const std::array<float,MAX_AMBI_COEFFS> &n3dscale = GetAmbiScales(device->mAmbiScale);
|
2016-07-29 21:55:43 -07:00
|
|
|
|
2018-11-18 08:01:50 -08:00
|
|
|
count = (device->mAmbiOrder == 3) ? 16 :
|
|
|
|
(device->mAmbiOrder == 2) ? 9 :
|
|
|
|
(device->mAmbiOrder == 1) ? 4 : 1;
|
2019-01-05 21:55:14 -08:00
|
|
|
std::transform(acnmap.begin(), acnmap.begin()+count, std::begin(device->Dry.AmbiMap),
|
2018-12-15 00:38:38 -08:00
|
|
|
[&n3dscale](const ALsizei &acn) noexcept -> BFChannelConfig
|
|
|
|
{ return BFChannelConfig{1.0f/n3dscale[acn], acn}; }
|
|
|
|
);
|
2016-07-29 21:55:43 -07:00
|
|
|
device->Dry.NumChannels = count;
|
|
|
|
|
2018-11-18 08:01:50 -08:00
|
|
|
if(device->mAmbiOrder < 2)
|
2016-07-29 21:55:43 -07:00
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap = device->Dry.AmbiMap;
|
2017-02-20 16:57:25 -08:00
|
|
|
device->FOAOut.NumChannels = 0;
|
2016-07-29 21:55:43 -07:00
|
|
|
}
|
2016-07-31 07:46:38 -07:00
|
|
|
else
|
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap.fill(BFChannelConfig{});
|
2018-12-20 04:19:35 -08:00
|
|
|
std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+4,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->FOAOut.AmbiMap),
|
2018-12-15 00:38:38 -08:00
|
|
|
[](const ALsizei &acn) noexcept { return BFChannelConfig{1.0f, acn}; }
|
|
|
|
);
|
2017-02-20 16:57:25 -08:00
|
|
|
device->FOAOut.NumChannels = 4;
|
2016-07-30 09:29:21 -07:00
|
|
|
|
2018-12-17 07:29:55 -08:00
|
|
|
device->AmbiUp.reset(new AmbiUpsampler{});
|
2019-01-05 21:55:14 -08:00
|
|
|
device->AmbiUp->reset(device->mAmbiOrder,
|
|
|
|
400.0f / static_cast<ALfloat>(device->Frequency));
|
2016-07-31 07:46:38 -07:00
|
|
|
}
|
2017-03-10 04:35:32 -08:00
|
|
|
|
2018-12-10 21:30:22 -08:00
|
|
|
ALfloat nfc_delay{0.0f};
|
2017-05-14 18:50:22 -07:00
|
|
|
if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f)
|
2017-03-10 04:35:32 -08:00
|
|
|
{
|
2018-12-10 21:30:22 -08:00
|
|
|
static constexpr ALsizei chans_per_order[MAX_AMBI_ORDER+1]{ 1, 3, 5, 7 };
|
2017-03-10 04:35:32 -08:00
|
|
|
nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f);
|
|
|
|
InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC,
|
2018-11-18 08:01:50 -08:00
|
|
|
device->mAmbiOrder, chans_per_order);
|
2017-03-10 04:35:32 -08:00
|
|
|
}
|
2019-01-05 19:21:25 -08:00
|
|
|
|
|
|
|
device->RealOut.NumChannels = 0;
|
2016-07-29 21:55:43 -07:00
|
|
|
}
|
2016-04-15 22:05:47 -07:00
|
|
|
else
|
2016-03-25 23:25:13 -07:00
|
|
|
{
|
2019-01-05 19:21:25 -08:00
|
|
|
ChannelDec chancoeffs[MAX_OUTPUT_CHANNELS]{};
|
|
|
|
ALsizei idxmap[MAX_OUTPUT_CHANNELS]{};
|
|
|
|
for(ALsizei i{0};i < count;++i)
|
|
|
|
{
|
|
|
|
const ALint idx{GetChannelIdxByName(device->RealOut, chanmap[i].ChanName)};
|
|
|
|
if(idx < 0)
|
|
|
|
{
|
|
|
|
ERR("Failed to find %s channel in device\n",
|
|
|
|
GetLabelFromChannel(chanmap[i].ChanName));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
idxmap[i] = idx;
|
|
|
|
std::copy_n(chanmap[i].Config, coeffcount, chancoeffs[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+coeffcount,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->Dry.AmbiMap),
|
2019-01-05 19:21:25 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
|
|
|
device->Dry.NumChannels = coeffcount;
|
|
|
|
|
|
|
|
TRACE("Enabling %s-order%s ambisonic decoder\n",
|
|
|
|
(coeffcount > 5) ? "third" :
|
|
|
|
(coeffcount > 3) ? "second" : "first",
|
|
|
|
""
|
|
|
|
);
|
|
|
|
device->AmbiDecoder.reset(new BFormatDec{});
|
|
|
|
device->AmbiDecoder->reset(coeffcount, device->Frequency, count, chancoeffs, idxmap);
|
2016-04-15 22:05:47 -07:00
|
|
|
|
2018-12-17 07:29:55 -08:00
|
|
|
if(coeffcount <= 4)
|
2016-04-15 22:05:47 -07:00
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap = device->Dry.AmbiMap;
|
2018-12-17 07:29:55 -08:00
|
|
|
device->FOAOut.NumChannels = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap.fill(BFChannelConfig{});
|
2019-01-05 19:21:25 -08:00
|
|
|
std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+3,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->FOAOut.AmbiMap),
|
2018-12-17 07:29:55 -08:00
|
|
|
[](const ALsizei &acn) noexcept { return BFChannelConfig{1.0f, acn}; }
|
|
|
|
);
|
2019-01-05 19:21:25 -08:00
|
|
|
device->FOAOut.NumChannels = 3;
|
2016-04-15 22:05:47 -07:00
|
|
|
}
|
2019-01-05 19:21:25 -08:00
|
|
|
|
|
|
|
device->RealOut.NumChannels = device->channelsFromFmt();
|
2016-03-25 23:25:13 -07:00
|
|
|
}
|
2010-08-03 00:21:36 -07:00
|
|
|
}
|
2016-01-28 00:02:46 -08:00
|
|
|
|
2018-12-10 02:08:54 -08:00
|
|
|
void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
|
2016-04-14 16:42:32 -07:00
|
|
|
{
|
|
|
|
if(conf->FreqBands != 1)
|
|
|
|
ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
|
|
|
|
conf->XOverFreq);
|
|
|
|
|
2019-01-05 19:21:25 -08:00
|
|
|
ALsizei count;
|
|
|
|
if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
|
2016-04-14 16:42:32 -07:00
|
|
|
{
|
2019-01-05 19:21:25 -08:00
|
|
|
count = (conf->ChanMask > AMBI_2ORDER_MASK) ? 16 :
|
|
|
|
(conf->ChanMask > AMBI_1ORDER_MASK) ? 9 : 4;
|
|
|
|
std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+count,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->Dry.AmbiMap),
|
2019-01-05 19:21:25 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
count = (conf->ChanMask > AMBI_2ORDER_MASK) ? 7 :
|
|
|
|
(conf->ChanMask > AMBI_1ORDER_MASK) ? 5 : 3;
|
|
|
|
std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+count,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->Dry.AmbiMap),
|
2019-01-05 19:21:25 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
2016-04-14 16:42:32 -07:00
|
|
|
}
|
2019-01-05 19:21:25 -08:00
|
|
|
device->Dry.NumChannels = count;
|
2016-04-14 16:42:32 -07:00
|
|
|
|
2019-01-05 19:21:25 -08:00
|
|
|
TRACE("Enabling %s-order%s ambisonic decoder\n",
|
|
|
|
(conf->ChanMask > AMBI_2ORDER_MASK) ? "third" :
|
|
|
|
(conf->ChanMask > AMBI_1ORDER_MASK) ? "second" : "first",
|
|
|
|
(conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
|
|
|
|
);
|
|
|
|
device->AmbiDecoder.reset(new BFormatDec{});
|
|
|
|
device->AmbiDecoder->reset(conf, false, count, device->Frequency, speakermap);
|
2016-04-14 16:42:32 -07:00
|
|
|
|
2018-12-17 07:29:55 -08:00
|
|
|
if(conf->ChanMask <= AMBI_1ORDER_MASK)
|
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap = device->Dry.AmbiMap;
|
2018-12-17 07:29:55 -08:00
|
|
|
device->FOAOut.NumChannels = 0;
|
|
|
|
}
|
|
|
|
else
|
2016-04-14 16:42:32 -07:00
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap.fill(BFChannelConfig{});
|
2019-01-05 19:21:25 -08:00
|
|
|
if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
|
|
|
|
{
|
|
|
|
count = 4;
|
|
|
|
std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+count,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->FOAOut.AmbiMap),
|
2019-01-05 19:21:25 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
count = 3;
|
|
|
|
std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+count,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->FOAOut.AmbiMap),
|
2019-01-05 19:21:25 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
|
|
|
}
|
2018-12-17 07:29:55 -08:00
|
|
|
device->FOAOut.NumChannels = count;
|
2016-04-14 16:42:32 -07:00
|
|
|
}
|
2017-02-20 16:57:25 -08:00
|
|
|
|
2019-01-05 19:21:25 -08:00
|
|
|
device->RealOut.NumChannels = device->channelsFromFmt();
|
2017-02-20 09:08:57 -08:00
|
|
|
|
|
|
|
InitDistanceComp(device, conf, speakermap);
|
2016-04-14 16:42:32 -07:00
|
|
|
}
|
|
|
|
|
2018-12-10 02:08:54 -08:00
|
|
|
void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
|
2016-04-14 16:42:32 -07:00
|
|
|
{
|
2018-12-08 01:32:43 -08:00
|
|
|
static constexpr ALsizei chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 };
|
|
|
|
static constexpr ALsizei chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 };
|
2016-04-14 16:42:32 -07:00
|
|
|
|
2018-12-10 21:30:22 -08:00
|
|
|
ALsizei count;
|
2016-06-01 05:30:06 -07:00
|
|
|
if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
|
2016-04-15 22:05:47 -07:00
|
|
|
{
|
2018-12-14 23:26:44 -08:00
|
|
|
count = (conf->ChanMask > AMBI_2ORDER_MASK) ? 16 :
|
|
|
|
(conf->ChanMask > AMBI_1ORDER_MASK) ? 9 : 4;
|
2018-12-20 04:19:35 -08:00
|
|
|
std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+count,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->Dry.AmbiMap),
|
2018-12-09 22:34:21 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
2016-04-15 22:05:47 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-14 23:26:44 -08:00
|
|
|
count = (conf->ChanMask > AMBI_2ORDER_MASK) ? 7 :
|
|
|
|
(conf->ChanMask > AMBI_1ORDER_MASK) ? 5 : 3;
|
2018-12-20 04:19:35 -08:00
|
|
|
std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+count,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->Dry.AmbiMap),
|
2018-12-09 22:34:21 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
2016-04-15 22:05:47 -07:00
|
|
|
}
|
|
|
|
device->Dry.NumChannels = count;
|
2016-04-14 16:42:32 -07:00
|
|
|
|
|
|
|
TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
|
|
|
|
(conf->FreqBands == 1) ? "single" : "dual",
|
2018-12-14 23:26:44 -08:00
|
|
|
(conf->ChanMask > AMBI_2ORDER_MASK) ? "third" :
|
|
|
|
(conf->ChanMask > AMBI_1ORDER_MASK) ? "second" : "first",
|
2016-06-01 05:30:06 -07:00
|
|
|
(conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
|
2016-04-14 16:42:32 -07:00
|
|
|
);
|
2018-12-17 07:29:55 -08:00
|
|
|
device->AmbiDecoder.reset(new BFormatDec{});
|
2019-01-05 19:21:25 -08:00
|
|
|
device->AmbiDecoder->reset(conf, true, count, device->Frequency, speakermap);
|
2016-04-14 16:42:32 -07:00
|
|
|
|
2018-12-14 23:26:44 -08:00
|
|
|
if(conf->ChanMask <= AMBI_1ORDER_MASK)
|
2016-04-15 22:05:47 -07:00
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap = device->Dry.AmbiMap;
|
2017-02-20 16:57:25 -08:00
|
|
|
device->FOAOut.NumChannels = 0;
|
2016-04-15 22:05:47 -07:00
|
|
|
}
|
2016-04-14 16:42:32 -07:00
|
|
|
else
|
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap.fill(BFChannelConfig{});
|
2017-02-19 17:45:27 -08:00
|
|
|
if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
|
2017-02-20 16:57:25 -08:00
|
|
|
{
|
|
|
|
count = 4;
|
2018-12-20 04:19:35 -08:00
|
|
|
std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+count,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->FOAOut.AmbiMap),
|
2018-12-09 22:34:21 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
2017-02-20 16:57:25 -08:00
|
|
|
}
|
2017-02-19 17:45:27 -08:00
|
|
|
else
|
2016-04-15 22:05:47 -07:00
|
|
|
{
|
2017-02-20 16:57:25 -08:00
|
|
|
count = 3;
|
2018-12-20 04:19:35 -08:00
|
|
|
std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+count,
|
2019-01-05 21:55:14 -08:00
|
|
|
std::begin(device->FOAOut.AmbiMap),
|
2018-12-09 22:34:21 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
2016-04-15 22:05:47 -07:00
|
|
|
}
|
2017-02-20 16:57:25 -08:00
|
|
|
device->FOAOut.NumChannels = count;
|
2016-04-14 16:42:32 -07:00
|
|
|
}
|
2017-02-19 22:47:59 -08:00
|
|
|
|
2018-12-19 05:57:36 -08:00
|
|
|
device->RealOut.NumChannels = device->channelsFromFmt();
|
2017-02-20 16:57:25 -08:00
|
|
|
|
2018-12-09 22:34:21 -08:00
|
|
|
using namespace std::placeholders;
|
|
|
|
auto accum_spkr_dist = std::bind(
|
|
|
|
std::plus<float>{}, _1, std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance), _2)
|
|
|
|
);
|
|
|
|
const ALfloat avg_dist{
|
2018-12-15 02:42:04 -08:00
|
|
|
std::accumulate(conf->Speakers.begin(), conf->Speakers.end(), float{0.0f},
|
|
|
|
accum_spkr_dist) / static_cast<ALfloat>(conf->Speakers.size())
|
2018-12-09 22:34:21 -08:00
|
|
|
};
|
2017-03-10 04:35:32 -08:00
|
|
|
InitNearFieldCtrl(device, avg_dist,
|
2018-12-14 23:26:44 -08:00
|
|
|
(conf->ChanMask > AMBI_2ORDER_MASK) ? 3 :
|
|
|
|
(conf->ChanMask > AMBI_1ORDER_MASK) ? 2 : 1,
|
2018-05-01 20:00:30 -07:00
|
|
|
(conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d
|
2017-03-10 04:35:32 -08:00
|
|
|
);
|
|
|
|
|
2017-02-20 09:08:57 -08:00
|
|
|
InitDistanceComp(device, conf, speakermap);
|
2016-04-14 16:42:32 -07:00
|
|
|
}
|
|
|
|
|
2018-12-10 02:08:54 -08:00
|
|
|
void InitHrtfPanning(ALCdevice *device)
|
2016-04-14 10:44:57 -07:00
|
|
|
{
|
2018-12-21 08:55:22 -08:00
|
|
|
/* NOTE: In degrees, and azimuth goes clockwise. */
|
2018-12-21 06:32:18 -08:00
|
|
|
static constexpr AngularPoint AmbiPoints[]{
|
2018-12-21 08:55:22 -08:00
|
|
|
{ 35.264390f, -45.000000f },
|
|
|
|
{ 35.264390f, 45.000000f },
|
|
|
|
{ 35.264390f, 135.000000f },
|
|
|
|
{ 35.264390f, -135.000000f },
|
|
|
|
{ -35.264390f, -45.000000f },
|
|
|
|
{ -35.264390f, 45.000000f },
|
|
|
|
{ -35.264390f, 135.000000f },
|
|
|
|
{ -35.264390f, -135.000000f },
|
|
|
|
{ 0.000000f, -20.905157f },
|
|
|
|
{ 0.000000f, 20.905157f },
|
|
|
|
{ 0.000000f, 159.094843f },
|
|
|
|
{ 0.000000f, -159.094843f },
|
|
|
|
{ 20.905157f, -90.000000f },
|
|
|
|
{ -20.905157f, -90.000000f },
|
|
|
|
{ -20.905157f, 90.000000f },
|
|
|
|
{ 20.905157f, 90.000000f },
|
|
|
|
{ 69.094843f, 0.000000f },
|
|
|
|
{ -69.094843f, 0.000000f },
|
|
|
|
{ -69.094843f, 180.000000f },
|
|
|
|
{ 69.094843f, 180.000000f },
|
2018-02-10 05:16:28 -08:00
|
|
|
};
|
2018-12-21 06:32:18 -08:00
|
|
|
static constexpr ALfloat AmbiMatrix[][MAX_AMBI_COEFFS]{
|
|
|
|
{ 5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, 6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, 6.33865691e-02f, 1.01126676e-01f, -7.36485380e-02f, -1.09260065e-02f, 7.08683387e-02f, -1.01622099e-01f },
|
|
|
|
{ 5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, -6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, -6.33865691e-02f, -1.01126676e-01f, -7.36485380e-02f, -1.09260065e-02f, 7.08683387e-02f, -1.01622099e-01f },
|
|
|
|
{ 5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, 6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, 6.33865691e-02f, -1.01126676e-01f, -7.36485380e-02f, 1.09260065e-02f, 7.08683387e-02f, 1.01622099e-01f },
|
|
|
|
{ 5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, -6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, -6.33865691e-02f, 1.01126676e-01f, -7.36485380e-02f, 1.09260065e-02f, 7.08683387e-02f, 1.01622099e-01f },
|
|
|
|
{ 5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, 6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, -6.33865691e-02f, 1.01126676e-01f, 7.36485380e-02f, -1.09260065e-02f, -7.08683387e-02f, -1.01622099e-01f },
|
|
|
|
{ 5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, -6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, 6.33865691e-02f, -1.01126676e-01f, 7.36485380e-02f, -1.09260065e-02f, -7.08683387e-02f, -1.01622099e-01f },
|
|
|
|
{ 5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, 6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, -6.33865691e-02f, -1.01126676e-01f, 7.36485380e-02f, 1.09260065e-02f, -7.08683387e-02f, 1.01622099e-01f },
|
|
|
|
{ 5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, -6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, 6.33865691e-02f, 1.01126676e-01f, 7.36485380e-02f, 1.09260065e-02f, -7.08683387e-02f, 1.01622099e-01f },
|
|
|
|
{ 5.00000000e-02f, 3.09016994e-02f, 0.00000000e+00f, 8.09016994e-02f, 6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, 7.76323754e-02f, 0.00000000e+00f, -1.49775925e-01f, 0.00000000e+00f, -2.95083663e-02f, 0.00000000e+00f, 7.76323754e-02f },
|
|
|
|
{ 5.00000000e-02f, -3.09016994e-02f, 0.00000000e+00f, 8.09016994e-02f, -6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, -7.76323754e-02f, 0.00000000e+00f, 1.49775925e-01f, 0.00000000e+00f, -2.95083663e-02f, 0.00000000e+00f, 7.76323754e-02f },
|
|
|
|
{ 5.00000000e-02f, -3.09016994e-02f, 0.00000000e+00f, -8.09016994e-02f, 6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, -7.76323754e-02f, 0.00000000e+00f, 1.49775925e-01f, 0.00000000e+00f, 2.95083663e-02f, 0.00000000e+00f, -7.76323754e-02f },
|
|
|
|
{ 5.00000000e-02f, 3.09016994e-02f, 0.00000000e+00f, -8.09016994e-02f, -6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, 7.76323754e-02f, 0.00000000e+00f, -1.49775925e-01f, 0.00000000e+00f, 2.95083663e-02f, 0.00000000e+00f, -7.76323754e-02f },
|
|
|
|
{ 5.00000000e-02f, 8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, -4.79794466e-02f, 0.00000000e+00f, -6.77901327e-02f, 3.03448665e-02f, 0.00000000e+00f, -1.65948192e-01f, 0.00000000e+00f },
|
|
|
|
{ 5.00000000e-02f, 8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, -6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, -4.79794466e-02f, 0.00000000e+00f, -6.77901327e-02f, -3.03448665e-02f, 0.00000000e+00f, 1.65948192e-01f, 0.00000000e+00f },
|
|
|
|
{ 5.00000000e-02f, -8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, 4.79794466e-02f, 0.00000000e+00f, 6.77901327e-02f, -3.03448665e-02f, 0.00000000e+00f, 1.65948192e-01f, 0.00000000e+00f },
|
|
|
|
{ 5.00000000e-02f, -8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, -6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, 4.79794466e-02f, 0.00000000e+00f, 6.77901327e-02f, 3.03448665e-02f, 0.00000000e+00f, -1.65948192e-01f, 0.00000000e+00f },
|
|
|
|
{ 5.00000000e-02f, 0.00000000e+00f, 8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, 6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, 7.94438918e-02f, 1.12611206e-01f, -2.42115150e-02f, 1.25611822e-01f },
|
|
|
|
{ 5.00000000e-02f, 0.00000000e+00f, -8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, -6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -7.94438918e-02f, 1.12611206e-01f, 2.42115150e-02f, 1.25611822e-01f },
|
|
|
|
{ 5.00000000e-02f, 0.00000000e+00f, -8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, 6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -7.94438918e-02f, -1.12611206e-01f, 2.42115150e-02f, -1.25611822e-01f },
|
|
|
|
{ 5.00000000e-02f, 0.00000000e+00f, 8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, -6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, 7.94438918e-02f, -1.12611206e-01f, -2.42115150e-02f, -1.25611822e-01f }
|
2017-01-18 19:16:24 -08:00
|
|
|
};
|
2018-12-21 06:32:18 -08:00
|
|
|
static constexpr ALfloat AmbiOrderHFGainFOA[MAX_AMBI_ORDER+1]{
|
|
|
|
3.16227766e+00f, 1.82574186e+00f
|
|
|
|
}, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1]{
|
|
|
|
2.35702260e+00f, 1.82574186e+00f, 9.42809042e-01f
|
|
|
|
/* 1.86508671e+00f, 1.60609389e+00f, 1.14205530e+00f, 5.68379553e-01f */
|
2017-01-18 19:16:24 -08:00
|
|
|
};
|
2018-12-21 06:32:18 -08:00
|
|
|
static constexpr ALsizei IndexMap[9]{ 0, 1, 2, 3, 4, 5, 6, 7, 8 };
|
|
|
|
static constexpr ALsizei ChansPerOrder[MAX_AMBI_ORDER+1]{ 1, 3, 5, 0 };
|
|
|
|
const ALfloat *AmbiOrderHFGain{AmbiOrderHFGainFOA};
|
2018-12-09 22:34:21 -08:00
|
|
|
ALsizei count{4};
|
2016-04-14 10:44:57 -07:00
|
|
|
|
2018-12-21 06:32:18 -08:00
|
|
|
static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrix), "Ambisonic HRTF mismatch");
|
2017-01-15 13:57:22 -08:00
|
|
|
|
2018-12-17 07:29:55 -08:00
|
|
|
/* Don't bother with HOA when using full HRTF rendering. Nothing needs it,
|
|
|
|
* and it eases the CPU/memory load.
|
|
|
|
*/
|
2018-12-24 19:29:01 -08:00
|
|
|
if(device->mRenderMode != HrtfRender)
|
2018-02-10 05:16:28 -08:00
|
|
|
{
|
2018-12-17 07:29:55 -08:00
|
|
|
device->AmbiUp.reset(new AmbiUpsampler{});
|
|
|
|
|
2018-02-10 05:16:28 -08:00
|
|
|
AmbiOrderHFGain = AmbiOrderHFGainHOA;
|
2018-12-12 21:18:31 -08:00
|
|
|
count = static_cast<ALsizei>(COUNTOF(IndexMap));
|
2018-02-10 05:16:28 -08:00
|
|
|
}
|
|
|
|
|
2018-11-22 07:54:29 -08:00
|
|
|
device->mHrtfState.reset(
|
|
|
|
new (al_calloc(16, FAM_SIZE(DirectHrtfState, Chan, count))) DirectHrtfState{});
|
2017-03-11 06:20:04 -08:00
|
|
|
|
2019-01-05 21:55:14 -08:00
|
|
|
std::transform(std::begin(IndexMap), std::begin(IndexMap)+count, std::begin(device->Dry.AmbiMap),
|
2018-12-09 22:34:21 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
Decode directly from B-Format to HRTF instead of a cube
Last time this attempted to average the HRIRs according to their contribution
to a given B-Format channel as if they were loudspeakers, as well as averaging
the HRIR delays. The latter part resulted in the loss of the ITD (inter-aural
time delay), a key component of HRTF.
This time, the HRIRs are averaged similar to above, except instead of averaging
the delays, they're applied to the resulting coefficients (for example, a delay
of 8 would apply the HRIR starting at the 8th sample of the target HRIR). This
does roughly double the IR length, as the largest delay is about 35 samples
while the filter is normally 32 samples. However, this is still smaller the
original data set IR (which was 256 samples), it also only needs to be applied
to 4 channels for first-order ambisonics, rather than the 8-channel cube. So
it's doing twice as much work per sample, but only working on half the number
of samples.
Additionally, since the resulting HRIRs no longer rely on an extra delay line,
a more efficient HRTF mixing function can be made that doesn't use one. Such a
function can also avoid the per-sample stepping parameters the original uses.
2016-08-11 23:20:35 -07:00
|
|
|
device->Dry.NumChannels = count;
|
2016-04-14 10:44:57 -07:00
|
|
|
|
2017-04-18 17:39:10 -07:00
|
|
|
if(device->AmbiUp)
|
2017-01-15 13:57:22 -08:00
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap.fill(BFChannelConfig{});
|
|
|
|
std::transform(std::begin(IndexMap), std::begin(IndexMap)+4, std::begin(device->FOAOut.AmbiMap),
|
2018-12-09 22:34:21 -08:00
|
|
|
[](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
|
|
|
);
|
2017-02-20 16:57:25 -08:00
|
|
|
device->FOAOut.NumChannels = 4;
|
2017-01-15 13:57:22 -08:00
|
|
|
|
2019-01-05 21:55:14 -08:00
|
|
|
device->AmbiUp->reset(2, 400.0f / static_cast<ALfloat>(device->Frequency));
|
2017-01-15 13:57:22 -08:00
|
|
|
}
|
2017-04-18 17:39:10 -07:00
|
|
|
else
|
|
|
|
{
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap = device->Dry.AmbiMap;
|
2017-04-18 17:39:10 -07:00
|
|
|
device->FOAOut.NumChannels = 0;
|
|
|
|
}
|
2016-04-14 10:44:57 -07:00
|
|
|
|
2018-12-19 05:57:36 -08:00
|
|
|
device->RealOut.NumChannels = device->channelsFromFmt();
|
2017-02-20 16:57:25 -08:00
|
|
|
|
2018-12-22 09:20:50 -08:00
|
|
|
BuildBFormatHrtf(device->mHrtf,
|
2018-11-22 07:54:29 -08:00
|
|
|
device->mHrtfState.get(), device->Dry.NumChannels, AmbiPoints, AmbiMatrix,
|
2018-12-12 21:18:31 -08:00
|
|
|
static_cast<ALsizei>(COUNTOF(AmbiPoints)), AmbiOrderHFGain
|
2016-08-24 00:25:28 -07:00
|
|
|
);
|
2017-10-23 13:30:01 -07:00
|
|
|
|
2018-12-22 09:20:50 -08:00
|
|
|
InitNearFieldCtrl(device, device->mHrtf->distance, device->AmbiUp ? 2 : 1,
|
2018-05-01 20:00:30 -07:00
|
|
|
ChansPerOrder);
|
2016-04-14 10:44:57 -07:00
|
|
|
}
|
|
|
|
|
2018-12-10 02:08:54 -08:00
|
|
|
void InitUhjPanning(ALCdevice *device)
|
2016-04-14 10:44:57 -07:00
|
|
|
{
|
2018-12-09 22:34:21 -08:00
|
|
|
static constexpr ALsizei count{3};
|
2016-04-14 10:44:57 -07:00
|
|
|
|
2018-12-20 03:26:46 -08:00
|
|
|
auto acnmap_end = AmbiIndex::FromFuMa.begin() + count;
|
2019-01-05 21:55:14 -08:00
|
|
|
std::transform(AmbiIndex::FromFuMa.begin(), acnmap_end, std::begin(device->Dry.AmbiMap),
|
2018-12-09 22:34:21 -08:00
|
|
|
[](const ALsizei &acn) noexcept -> BFChannelConfig
|
2018-12-20 02:46:59 -08:00
|
|
|
{ return BFChannelConfig{1.0f/AmbiScale::FromFuMa[acn], acn}; }
|
2018-12-09 22:34:21 -08:00
|
|
|
);
|
2016-04-15 22:05:47 -07:00
|
|
|
device->Dry.NumChannels = count;
|
2016-04-14 10:44:57 -07:00
|
|
|
|
2019-01-05 21:55:14 -08:00
|
|
|
device->FOAOut.AmbiMap = device->Dry.AmbiMap;
|
2017-02-20 16:57:25 -08:00
|
|
|
device->FOAOut.NumChannels = 0;
|
|
|
|
|
2018-12-19 05:57:36 -08:00
|
|
|
device->RealOut.NumChannels = device->channelsFromFmt();
|
2016-04-14 10:44:57 -07:00
|
|
|
}
|
|
|
|
|
2018-12-10 02:08:54 -08:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
|
|
|
|
ALfloat (&coeffs)[MAX_AMBI_COEFFS])
|
|
|
|
{
|
|
|
|
/* Zeroth-order */
|
|
|
|
coeffs[0] = 1.0f; /* ACN 0 = 1 */
|
|
|
|
/* First-order */
|
|
|
|
coeffs[1] = SQRTF_3 * y; /* ACN 1 = sqrt(3) * Y */
|
|
|
|
coeffs[2] = SQRTF_3 * z; /* ACN 2 = sqrt(3) * Z */
|
|
|
|
coeffs[3] = SQRTF_3 * x; /* ACN 3 = sqrt(3) * X */
|
|
|
|
/* Second-order */
|
|
|
|
coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
|
|
|
|
coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
|
2018-12-15 20:28:52 -08:00
|
|
|
coeffs[6] = 1.118033989f * (z*z*3.0f - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
|
2018-12-10 02:08:54 -08:00
|
|
|
coeffs[7] = 3.872983346f * x * z; /* ACN 7 = sqrt(15) * X * Z */
|
|
|
|
coeffs[8] = 1.936491673f * (x*x - y*y); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
|
|
|
|
/* Third-order */
|
2018-12-15 20:28:52 -08:00
|
|
|
coeffs[9] = 2.091650066f * y * (x*x*3.0f - y*y); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
|
2018-12-10 02:08:54 -08:00
|
|
|
coeffs[10] = 10.246950766f * z * x * y; /* ACN 10 = sqrt(105) * Z * X * Y */
|
2018-12-15 20:28:52 -08:00
|
|
|
coeffs[11] = 1.620185175f * y * (z*z*5.0f - 1.0f); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
|
|
|
|
coeffs[12] = 1.322875656f * z * (z*z*5.0f - 3.0f); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
|
|
|
|
coeffs[13] = 1.620185175f * x * (z*z*5.0f - 1.0f); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
|
2018-12-10 02:08:54 -08:00
|
|
|
coeffs[14] = 5.123475383f * z * (x*x - y*y); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
|
2018-12-15 20:28:52 -08:00
|
|
|
coeffs[15] = 2.091650066f * x * (x*x - y*y*3.0f); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
|
2018-12-15 19:23:42 -08:00
|
|
|
/* Fourth-order */
|
|
|
|
/* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */
|
|
|
|
/* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */
|
|
|
|
/* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */
|
|
|
|
/* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */
|
|
|
|
/* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */
|
|
|
|
/* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */
|
|
|
|
/* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */
|
|
|
|
/* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */
|
|
|
|
/* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */
|
2018-12-10 02:08:54 -08:00
|
|
|
|
|
|
|
if(spread > 0.0f)
|
|
|
|
{
|
|
|
|
/* Implement the spread by using a spherical source that subtends the
|
|
|
|
* angle spread. See:
|
|
|
|
* http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
|
|
|
|
*
|
|
|
|
* When adjusted for N3D normalization instead of SN3D, these
|
|
|
|
* calculations are:
|
|
|
|
*
|
|
|
|
* ZH0 = -sqrt(pi) * (-1+ca);
|
|
|
|
* ZH1 = 0.5*sqrt(pi) * sa*sa;
|
|
|
|
* ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
|
|
|
|
* ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
|
|
|
|
* ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
|
|
|
|
* ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
|
|
|
|
*
|
|
|
|
* The gain of the source is compensated for size, so that the
|
2018-12-15 19:23:42 -08:00
|
|
|
* loudness doesn't depend on the spread. Thus:
|
2018-12-10 02:08:54 -08:00
|
|
|
*
|
|
|
|
* ZH0 = 1.0f;
|
|
|
|
* ZH1 = 0.5f * (ca+1.0f);
|
|
|
|
* ZH2 = 0.5f * (ca+1.0f)*ca;
|
|
|
|
* ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
|
|
|
|
* ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
|
|
|
|
* ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
|
|
|
|
*/
|
2018-12-10 21:30:22 -08:00
|
|
|
ALfloat ca = std::cos(spread * 0.5f);
|
2018-12-10 02:08:54 -08:00
|
|
|
/* Increase the source volume by up to +3dB for a full spread. */
|
2018-12-10 21:30:22 -08:00
|
|
|
ALfloat scale = std::sqrt(1.0f + spread/F_TAU);
|
2018-12-10 02:08:54 -08:00
|
|
|
|
|
|
|
ALfloat ZH0_norm = scale;
|
|
|
|
ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale;
|
|
|
|
ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale;
|
|
|
|
ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale;
|
|
|
|
|
|
|
|
/* Zeroth-order */
|
|
|
|
coeffs[0] *= ZH0_norm;
|
|
|
|
/* First-order */
|
|
|
|
coeffs[1] *= ZH1_norm;
|
|
|
|
coeffs[2] *= ZH1_norm;
|
|
|
|
coeffs[3] *= ZH1_norm;
|
|
|
|
/* Second-order */
|
|
|
|
coeffs[4] *= ZH2_norm;
|
|
|
|
coeffs[5] *= ZH2_norm;
|
|
|
|
coeffs[6] *= ZH2_norm;
|
|
|
|
coeffs[7] *= ZH2_norm;
|
|
|
|
coeffs[8] *= ZH2_norm;
|
|
|
|
/* Third-order */
|
|
|
|
coeffs[9] *= ZH3_norm;
|
|
|
|
coeffs[10] *= ZH3_norm;
|
|
|
|
coeffs[11] *= ZH3_norm;
|
|
|
|
coeffs[12] *= ZH3_norm;
|
|
|
|
coeffs[13] *= ZH3_norm;
|
|
|
|
coeffs[14] *= ZH3_norm;
|
|
|
|
coeffs[15] *= ZH3_norm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS])
|
|
|
|
{
|
|
|
|
ASSUME(numchans > 0);
|
|
|
|
auto iter = std::transform(chanmap, chanmap+numchans, std::begin(gains),
|
|
|
|
[coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> ALfloat
|
|
|
|
{
|
|
|
|
ASSUME(chanmap.Index >= 0);
|
|
|
|
return chanmap.Scale * coeffs[chanmap.Index] * ingain;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
std::fill(iter, std::end(gains), 0.0f);
|
|
|
|
}
|
|
|
|
|
2018-12-23 08:51:28 -08:00
|
|
|
void ComputePanGains(const ALeffectslot *slot, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS])
|
|
|
|
{ ComputePanningGainsBF(slot->ChanMap, slot->NumChannels, coeffs, ingain, gains); }
|
|
|
|
|
2018-12-10 02:08:54 -08:00
|
|
|
|
2018-12-15 00:38:38 -08:00
|
|
|
void aluInitRenderer(ALCdevice *device, ALint hrtf_id, HrtfRequestMode hrtf_appreq, HrtfRequestMode hrtf_userreq)
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
2017-04-06 13:00:29 -07:00
|
|
|
/* Hold the HRTF the device last used, in case it's used again. */
|
2018-12-22 09:20:50 -08:00
|
|
|
HrtfEntry *old_hrtf{device->mHrtf};
|
2016-04-14 15:25:12 -07:00
|
|
|
|
2018-11-22 06:59:32 -08:00
|
|
|
device->mHrtfState = nullptr;
|
2018-12-22 09:20:50 -08:00
|
|
|
device->mHrtf = nullptr;
|
2018-11-18 19:28:01 -08:00
|
|
|
device->HrtfName.clear();
|
2018-12-24 19:29:01 -08:00
|
|
|
device->mRenderMode = NormalRender;
|
2016-04-14 15:25:12 -07:00
|
|
|
|
2019-01-05 21:55:14 -08:00
|
|
|
device->Dry.AmbiMap.fill(BFChannelConfig{});
|
2016-04-15 17:31:04 -07:00
|
|
|
device->Dry.NumChannels = 0;
|
2018-12-05 16:35:06 -08:00
|
|
|
std::fill(std::begin(device->NumChannelsPerOrder), std::end(device->NumChannelsPerOrder), 0);
|
2016-04-15 17:31:04 -07:00
|
|
|
|
2017-03-10 04:35:32 -08:00
|
|
|
device->AvgSpeakerDist = 0.0f;
|
2018-11-21 05:06:31 -08:00
|
|
|
device->ChannelDelay.clear();
|
2017-02-19 22:47:59 -08:00
|
|
|
|
2018-12-17 07:29:55 -08:00
|
|
|
device->AmbiDecoder = nullptr;
|
|
|
|
device->AmbiUp = nullptr;
|
2018-11-22 04:53:29 -08:00
|
|
|
device->Stablizer = nullptr;
|
2017-07-31 23:49:48 -07:00
|
|
|
|
2016-04-14 15:25:12 -07:00
|
|
|
if(device->FmtChans != DevFmtStereo)
|
|
|
|
{
|
2017-04-06 13:00:29 -07:00
|
|
|
if(old_hrtf)
|
|
|
|
Hrtf_DecRef(old_hrtf);
|
2018-12-15 00:38:38 -08:00
|
|
|
old_hrtf = nullptr;
|
2016-04-14 15:25:12 -07:00
|
|
|
if(hrtf_appreq == Hrtf_Enable)
|
2017-03-10 10:47:43 -08:00
|
|
|
device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
2016-04-14 15:25:12 -07:00
|
|
|
|
2018-12-15 00:38:38 -08:00
|
|
|
const char *layout{nullptr};
|
2016-04-16 14:00:22 -07:00
|
|
|
switch(device->FmtChans)
|
|
|
|
{
|
|
|
|
case DevFmtQuad: layout = "quad"; break;
|
2017-02-19 22:59:55 -08:00
|
|
|
case DevFmtX51: /* fall-through */
|
|
|
|
case DevFmtX51Rear: layout = "surround51"; break;
|
2016-04-16 14:00:22 -07:00
|
|
|
case DevFmtX61: layout = "surround61"; break;
|
|
|
|
case DevFmtX71: layout = "surround71"; break;
|
2016-07-29 21:55:43 -07:00
|
|
|
/* Mono, Stereo, and Ambisonics output don't use custom decoders. */
|
2016-04-16 14:00:22 -07:00
|
|
|
case DevFmtMono:
|
|
|
|
case DevFmtStereo:
|
2017-04-12 18:26:07 -07:00
|
|
|
case DevFmtAmbi3D:
|
2016-04-16 14:00:22 -07:00
|
|
|
break;
|
|
|
|
}
|
2018-12-15 00:38:38 -08:00
|
|
|
|
|
|
|
const char *devname{device->DeviceName.c_str()};
|
|
|
|
ALsizei speakermap[MAX_OUTPUT_CHANNELS];
|
|
|
|
AmbDecConf *pconf{nullptr};
|
|
|
|
AmbDecConf conf{};
|
2016-04-14 16:42:32 -07:00
|
|
|
if(layout)
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
2016-04-14 16:42:32 -07:00
|
|
|
const char *fname;
|
|
|
|
if(ConfigValueStr(devname, "decoder", layout, &fname))
|
|
|
|
{
|
2018-11-03 19:51:23 -07:00
|
|
|
if(!conf.load(fname))
|
2016-04-14 16:42:32 -07:00
|
|
|
ERR("Failed to load layout file %s\n", fname);
|
2018-12-15 02:42:04 -08:00
|
|
|
else if(conf.Speakers.size() > MAX_OUTPUT_CHANNELS)
|
|
|
|
ERR("Unsupported speaker count " SZFMT " (max %d)\n", conf.Speakers.size(),
|
|
|
|
MAX_OUTPUT_CHANNELS);
|
2018-12-15 00:38:38 -08:00
|
|
|
else if(conf.ChanMask > AMBI_3ORDER_MASK)
|
|
|
|
ERR("Unsupported channel mask 0x%04x (max 0x%x)\n", conf.ChanMask,
|
|
|
|
AMBI_3ORDER_MASK);
|
|
|
|
else if(MakeSpeakerMap(device, &conf, speakermap))
|
|
|
|
pconf = &conf;
|
2016-04-14 16:42:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!pconf)
|
|
|
|
InitPanning(device);
|
2018-12-17 07:29:55 -08:00
|
|
|
else if(GetConfigValueBool(devname, "decoder", "hq-mode", 0))
|
2016-04-14 16:42:32 -07:00
|
|
|
InitHQPanning(device, pconf, speakermap);
|
|
|
|
else
|
|
|
|
InitCustomPanning(device, pconf, speakermap);
|
|
|
|
|
2017-07-31 23:49:48 -07:00
|
|
|
/* Enable the stablizer only for formats that have front-left, front-
|
|
|
|
* right, and front-center outputs.
|
|
|
|
*/
|
|
|
|
switch(device->FmtChans)
|
|
|
|
{
|
|
|
|
case DevFmtX51:
|
|
|
|
case DevFmtX51Rear:
|
|
|
|
case DevFmtX61:
|
|
|
|
case DevFmtX71:
|
2018-12-15 00:38:38 -08:00
|
|
|
if(GetConfigValueBool(devname, nullptr, "front-stablizer", 0))
|
2017-07-31 23:49:48 -07:00
|
|
|
{
|
2019-01-01 14:33:01 -08:00
|
|
|
auto stablizer = al::make_unique<FrontStablizer>();
|
2017-07-31 23:49:48 -07:00
|
|
|
/* Initialize band-splitting filters for the front-left and
|
|
|
|
* front-right channels, with a crossover at 5khz (could be
|
|
|
|
* higher).
|
|
|
|
*/
|
2019-01-01 14:33:01 -08:00
|
|
|
const ALfloat scale{(ALfloat)(5000.0 / device->Frequency)};
|
2017-07-31 23:49:48 -07:00
|
|
|
|
2018-12-05 15:38:05 -08:00
|
|
|
stablizer->LFilter.init(scale);
|
2017-07-31 23:49:48 -07:00
|
|
|
stablizer->RFilter = stablizer->LFilter;
|
|
|
|
|
|
|
|
/* Initialize all-pass filters for all other channels. */
|
2018-12-05 15:38:05 -08:00
|
|
|
stablizer->APFilter[0].init(scale);
|
2018-12-05 16:35:06 -08:00
|
|
|
std::fill(std::begin(stablizer->APFilter)+1, std::end(stablizer->APFilter),
|
2019-01-01 14:33:01 -08:00
|
|
|
stablizer->APFilter[0]);
|
2017-07-31 23:49:48 -07:00
|
|
|
|
2018-11-22 04:53:29 -08:00
|
|
|
device->Stablizer = std::move(stablizer);
|
2017-07-31 23:49:48 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DevFmtMono:
|
|
|
|
case DevFmtStereo:
|
|
|
|
case DevFmtQuad:
|
|
|
|
case DevFmtAmbi3D:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled");
|
|
|
|
|
2016-04-14 15:25:12 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:37:35 -08:00
|
|
|
device->AmbiDecoder = nullptr;
|
2016-04-14 15:25:12 -07:00
|
|
|
|
2018-12-05 16:35:06 -08:00
|
|
|
bool headphones{device->IsHeadphones != AL_FALSE};
|
2016-04-14 15:25:12 -07:00
|
|
|
if(device->Type != Loopback)
|
|
|
|
{
|
|
|
|
const char *mode;
|
2018-12-15 00:38:38 -08:00
|
|
|
if(ConfigValueStr(device->DeviceName.c_str(), nullptr, "stereo-mode", &mode))
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
|
|
|
if(strcasecmp(mode, "headphones") == 0)
|
|
|
|
headphones = true;
|
|
|
|
else if(strcasecmp(mode, "speakers") == 0)
|
|
|
|
headphones = false;
|
|
|
|
else if(strcasecmp(mode, "auto") != 0)
|
|
|
|
ERR("Unexpected stereo-mode: %s\n", mode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(hrtf_userreq == Hrtf_Default)
|
|
|
|
{
|
2016-04-16 14:00:22 -07:00
|
|
|
bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
|
|
|
|
(hrtf_appreq == Hrtf_Enable);
|
|
|
|
if(!usehrtf) goto no_hrtf;
|
|
|
|
|
2017-03-10 10:47:43 -08:00
|
|
|
device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
|
2016-04-14 15:25:12 -07:00
|
|
|
if(headphones && hrtf_appreq != Hrtf_Disable)
|
2017-03-10 10:47:43 -08:00
|
|
|
device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
|
2016-04-14 15:25:12 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-16 14:00:22 -07:00
|
|
|
if(hrtf_userreq != Hrtf_Enable)
|
|
|
|
{
|
|
|
|
if(hrtf_appreq == Hrtf_Enable)
|
2017-03-10 10:47:43 -08:00
|
|
|
device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
|
2016-04-16 14:00:22 -07:00
|
|
|
goto no_hrtf;
|
|
|
|
}
|
2017-03-10 10:47:43 -08:00
|
|
|
device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
|
2016-04-14 15:25:12 -07:00
|
|
|
}
|
|
|
|
|
2018-11-18 19:19:35 -08:00
|
|
|
if(device->HrtfList.empty())
|
2018-11-18 18:45:45 -08:00
|
|
|
device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
|
2016-04-14 15:25:12 -07:00
|
|
|
|
2018-11-18 19:19:35 -08:00
|
|
|
if(hrtf_id >= 0 && (size_t)hrtf_id < device->HrtfList.size())
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
2018-11-18 19:19:35 -08:00
|
|
|
const EnumeratedHrtf &entry = device->HrtfList[hrtf_id];
|
2018-12-22 09:20:50 -08:00
|
|
|
HrtfEntry *hrtf{GetLoadedHrtf(entry.hrtf)};
|
2017-04-05 12:27:30 -07:00
|
|
|
if(hrtf && hrtf->sampleRate == device->Frequency)
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
2018-12-22 09:20:50 -08:00
|
|
|
device->mHrtf = hrtf;
|
2018-11-18 19:28:01 -08:00
|
|
|
device->HrtfName = entry.name;
|
2016-04-14 15:25:12 -07:00
|
|
|
}
|
2017-04-06 13:00:29 -07:00
|
|
|
else if(hrtf)
|
|
|
|
Hrtf_DecRef(hrtf);
|
|
|
|
}
|
|
|
|
|
2018-12-22 09:20:50 -08:00
|
|
|
if(!device->mHrtf)
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
2018-12-05 16:35:06 -08:00
|
|
|
auto find_hrtf = [device](const EnumeratedHrtf &entry) -> bool
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
2018-12-22 09:20:50 -08:00
|
|
|
HrtfEntry *hrtf{GetLoadedHrtf(entry.hrtf)};
|
2018-12-05 16:35:06 -08:00
|
|
|
if(!hrtf) return false;
|
|
|
|
if(hrtf->sampleRate != device->Frequency)
|
|
|
|
{
|
|
|
|
Hrtf_DecRef(hrtf);
|
|
|
|
return false;
|
|
|
|
}
|
2018-12-22 09:20:50 -08:00
|
|
|
device->mHrtf = hrtf;
|
2018-11-18 19:28:01 -08:00
|
|
|
device->HrtfName = entry.name;
|
2018-12-05 16:35:06 -08:00
|
|
|
return true;
|
|
|
|
};
|
|
|
|
std::find_if(device->HrtfList.cbegin(), device->HrtfList.cend(), find_hrtf);
|
2016-04-14 15:25:12 -07:00
|
|
|
}
|
|
|
|
|
2018-12-22 09:20:50 -08:00
|
|
|
if(device->mHrtf)
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
2017-04-06 13:00:29 -07:00
|
|
|
if(old_hrtf)
|
|
|
|
Hrtf_DecRef(old_hrtf);
|
2018-12-15 00:38:38 -08:00
|
|
|
old_hrtf = nullptr;
|
2017-04-06 13:00:29 -07:00
|
|
|
|
2018-12-24 19:29:01 -08:00
|
|
|
device->mRenderMode = HrtfRender;
|
2018-12-05 16:35:06 -08:00
|
|
|
const char *mode;
|
2018-12-15 00:38:38 -08:00
|
|
|
if(ConfigValueStr(device->DeviceName.c_str(), nullptr, "hrtf-mode", &mode))
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
|
|
|
if(strcasecmp(mode, "full") == 0)
|
2018-12-24 19:29:01 -08:00
|
|
|
device->mRenderMode = HrtfRender;
|
2016-04-14 15:25:12 -07:00
|
|
|
else if(strcasecmp(mode, "basic") == 0)
|
2018-12-24 19:29:01 -08:00
|
|
|
device->mRenderMode = NormalRender;
|
2016-04-14 15:25:12 -07:00
|
|
|
else
|
|
|
|
ERR("Unexpected hrtf-mode: %s\n", mode);
|
|
|
|
}
|
|
|
|
|
2016-11-01 14:07:14 -07:00
|
|
|
TRACE("%s HRTF rendering enabled, using \"%s\"\n",
|
2018-12-24 19:29:01 -08:00
|
|
|
((device->mRenderMode == HrtfRender) ? "Full" : "Basic"), device->HrtfName.c_str()
|
2016-11-01 14:07:14 -07:00
|
|
|
);
|
2017-04-18 17:39:10 -07:00
|
|
|
InitHrtfPanning(device);
|
2016-04-14 15:25:12 -07:00
|
|
|
return;
|
|
|
|
}
|
2017-03-10 10:47:43 -08:00
|
|
|
device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
2016-04-14 15:25:12 -07:00
|
|
|
|
|
|
|
no_hrtf:
|
2017-04-06 13:00:29 -07:00
|
|
|
if(old_hrtf)
|
|
|
|
Hrtf_DecRef(old_hrtf);
|
2018-11-22 06:49:37 -08:00
|
|
|
old_hrtf = nullptr;
|
2016-04-14 15:25:12 -07:00
|
|
|
TRACE("HRTF disabled\n");
|
|
|
|
|
2018-12-24 19:29:01 -08:00
|
|
|
device->mRenderMode = StereoPair;
|
2017-02-22 19:18:01 -08:00
|
|
|
|
2018-12-05 16:35:06 -08:00
|
|
|
int bs2blevel{((headphones && hrtf_appreq != Hrtf_Disable) ||
|
|
|
|
(hrtf_appreq == Hrtf_Enable)) ? 5 : 0};
|
2016-04-14 15:25:12 -07:00
|
|
|
if(device->Type != Loopback)
|
2018-12-15 00:38:38 -08:00
|
|
|
ConfigValueInt(device->DeviceName.c_str(), nullptr, "cf_level", &bs2blevel);
|
2016-04-14 15:25:12 -07:00
|
|
|
if(bs2blevel > 0 && bs2blevel <= 6)
|
|
|
|
{
|
2018-11-22 06:49:37 -08:00
|
|
|
device->Bs2b.reset(new bs2b{});
|
|
|
|
bs2b_set_params(device->Bs2b.get(), bs2blevel, device->Frequency);
|
2016-04-14 15:25:12 -07:00
|
|
|
TRACE("BS2B enabled\n");
|
|
|
|
InitPanning(device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("BS2B disabled\n");
|
|
|
|
|
2018-12-05 16:35:06 -08:00
|
|
|
const char *mode;
|
2018-12-15 00:38:38 -08:00
|
|
|
if(ConfigValueStr(device->DeviceName.c_str(), nullptr, "stereo-encoding", &mode))
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
2017-02-22 19:18:01 -08:00
|
|
|
if(strcasecmp(mode, "uhj") == 0)
|
2018-12-24 19:29:01 -08:00
|
|
|
device->mRenderMode = NormalRender;
|
2017-02-22 19:18:01 -08:00
|
|
|
else if(strcasecmp(mode, "panpot") != 0)
|
|
|
|
ERR("Unexpected stereo-encoding: %s\n", mode);
|
2016-04-14 15:25:12 -07:00
|
|
|
}
|
2018-12-24 19:29:01 -08:00
|
|
|
if(device->mRenderMode == NormalRender)
|
2016-04-14 15:25:12 -07:00
|
|
|
{
|
2018-11-21 15:31:32 -08:00
|
|
|
device->Uhj_Encoder.reset(new Uhj2Encoder{});
|
2016-04-14 15:25:12 -07:00
|
|
|
TRACE("UHJ enabled\n");
|
|
|
|
InitUhjPanning(device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("UHJ disabled\n");
|
|
|
|
InitPanning(device);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-28 00:02:46 -08:00
|
|
|
void aluInitEffectPanning(ALeffectslot *slot)
|
|
|
|
{
|
2018-12-15 00:38:38 -08:00
|
|
|
const size_t count{countof(slot->ChanMap)};
|
2018-12-20 04:19:35 -08:00
|
|
|
auto acnmap_end = AmbiIndex::From3D.begin() + count;
|
|
|
|
std::transform(AmbiIndex::From3D.begin(), acnmap_end, std::begin(slot->ChanMap),
|
2018-12-15 00:38:38 -08:00
|
|
|
[](const ALsizei &acn) noexcept { return BFChannelConfig{1.0f, acn}; }
|
|
|
|
);
|
|
|
|
slot->NumChannels = static_cast<ALsizei>(count);
|
2016-01-28 00:02:46 -08:00
|
|
|
}
|