Add missing linear resampler to the option setting list
[openal-soft.git] / alc / panning.cpp
blob964d56e8a81f2a214c460d994c9f21b708a4dc1c
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2010 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <algorithm>
24 #include <array>
25 #include <chrono>
26 #include <cmath>
27 #include <cstdio>
28 #include <cstring>
29 #include <functional>
30 #include <iterator>
31 #include <memory>
32 #include <new>
33 #include <numeric>
34 #include <string>
36 #include "AL/al.h"
37 #include "AL/alc.h"
38 #include "AL/alext.h"
40 #include "al/auxeffectslot.h"
41 #include "alcmain.h"
42 #include "alconfig.h"
43 #include "almalloc.h"
44 #include "alnumeric.h"
45 #include "aloptional.h"
46 #include "alspan.h"
47 #include "alstring.h"
48 #include "alu.h"
49 #include "ambdec.h"
50 #include "ambidefs.h"
51 #include "bformatdec.h"
52 #include "bs2b.h"
53 #include "devformat.h"
54 #include "front_stablizer.h"
55 #include "hrtf.h"
56 #include "logging.h"
57 #include "math_defs.h"
58 #include "opthelpers.h"
59 #include "uhjfilter.h"
62 constexpr std::array<float,MAX_AMBI_CHANNELS> AmbiScale::FromN3D;
63 constexpr std::array<float,MAX_AMBI_CHANNELS> AmbiScale::FromSN3D;
64 constexpr std::array<float,MAX_AMBI_CHANNELS> AmbiScale::FromFuMa;
65 constexpr std::array<uint8_t,MAX_AMBI_CHANNELS> AmbiIndex::FromFuMa;
66 constexpr std::array<uint8_t,MAX_AMBI2D_CHANNELS> AmbiIndex::FromFuMa2D;
67 constexpr std::array<uint8_t,MAX_AMBI_CHANNELS> AmbiIndex::FromACN;
68 constexpr std::array<uint8_t,MAX_AMBI2D_CHANNELS> AmbiIndex::From2D;
69 constexpr std::array<uint8_t,MAX_AMBI_CHANNELS> AmbiIndex::OrderFromChannel;
70 constexpr std::array<uint8_t,MAX_AMBI2D_CHANNELS> AmbiIndex::OrderFrom2DChannel;
73 namespace {
75 using namespace std::placeholders;
76 using std::chrono::seconds;
77 using std::chrono::nanoseconds;
79 inline const char *GetLabelFromChannel(Channel channel)
81 switch(channel)
83 case FrontLeft: return "front-left";
84 case FrontRight: return "front-right";
85 case FrontCenter: return "front-center";
86 case LFE: return "lfe";
87 case BackLeft: return "back-left";
88 case BackRight: return "back-right";
89 case BackCenter: return "back-center";
90 case SideLeft: return "side-left";
91 case SideRight: return "side-right";
93 case TopFrontLeft: return "top-front-left";
94 case TopFrontCenter: return "top-front-center";
95 case TopFrontRight: return "top-front-right";
96 case TopCenter: return "top-center";
97 case TopBackLeft: return "top-back-left";
98 case TopBackCenter: return "top-back-center";
99 case TopBackRight: return "top-back-right";
101 case MaxChannels: break;
103 return "(unknown)";
107 std::unique_ptr<FrontStablizer> CreateStablizer(const size_t outchans, const ALuint srate)
109 auto stablizer = FrontStablizer::Create(outchans);
110 for(auto &buf : stablizer->DelayBuf)
111 std::fill(buf.begin(), buf.end(), 0.0f);
113 /* Initialize band-splitting filter for the mid signal, with a crossover at
114 * 5khz (could be higher).
116 stablizer->MidFilter.init(5000.0f / static_cast<float>(srate));
118 return stablizer;
121 void AllocChannels(ALCdevice *device, const size_t main_chans, const size_t real_chans)
123 TRACE("Channel config, Main: %zu, Real: %zu\n", main_chans, real_chans);
125 /* Allocate extra channels for any post-filter output. */
126 const size_t num_chans{main_chans + real_chans};
128 TRACE("Allocating %zu channels, %zu bytes\n", num_chans,
129 num_chans*sizeof(device->MixBuffer[0]));
130 device->MixBuffer.resize(num_chans);
131 al::span<FloatBufferLine> buffer{device->MixBuffer};
133 device->Dry.Buffer = buffer.first(main_chans);
134 buffer = buffer.subspan(main_chans);
135 if(real_chans != 0)
137 device->RealOut.Buffer = buffer.first(real_chans);
138 buffer = buffer.subspan(real_chans);
140 else
141 device->RealOut.Buffer = device->Dry.Buffer;
145 struct ChannelMap {
146 Channel ChanName;
147 float Config[MAX_AMBI2D_CHANNELS];
150 bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALuint (&speakermap)[MAX_OUTPUT_CHANNELS])
152 auto map_spkr = [device](const AmbDecConf::SpeakerConf &speaker) -> ALuint
154 /* NOTE: AmbDec does not define any standard speaker names, however
155 * for this to work we have to by able to find the output channel
156 * the speaker definition corresponds to. Therefore, OpenAL Soft
157 * requires these channel labels to be recognized:
159 * LF = Front left
160 * RF = Front right
161 * LS = Side left
162 * RS = Side right
163 * LB = Back left
164 * RB = Back right
165 * CE = Front center
166 * CB = Back center
168 * Additionally, surround51 will acknowledge back speakers for side
169 * channels, and surround51rear will acknowledge side speakers for
170 * back channels, to avoid issues with an ambdec expecting 5.1 to
171 * use the side channels when the device is configured for back,
172 * and vice-versa.
174 Channel ch{};
175 if(speaker.Name == "LF")
176 ch = FrontLeft;
177 else if(speaker.Name == "RF")
178 ch = FrontRight;
179 else if(speaker.Name == "CE")
180 ch = FrontCenter;
181 else if(speaker.Name == "LS")
183 if(device->FmtChans == DevFmtX51Rear)
184 ch = BackLeft;
185 else
186 ch = SideLeft;
188 else if(speaker.Name == "RS")
190 if(device->FmtChans == DevFmtX51Rear)
191 ch = BackRight;
192 else
193 ch = SideRight;
195 else if(speaker.Name == "LB")
197 if(device->FmtChans == DevFmtX51)
198 ch = SideLeft;
199 else
200 ch = BackLeft;
202 else if(speaker.Name == "RB")
204 if(device->FmtChans == DevFmtX51)
205 ch = SideRight;
206 else
207 ch = BackRight;
209 else if(speaker.Name == "CB")
210 ch = BackCenter;
211 else
213 ERR("AmbDec speaker label \"%s\" not recognized\n", speaker.Name.c_str());
214 return INVALID_CHANNEL_INDEX;
216 const ALuint chidx{GetChannelIdxByName(device->RealOut, ch)};
217 if(chidx == INVALID_CHANNEL_INDEX)
218 ERR("Failed to lookup AmbDec speaker label %s\n", speaker.Name.c_str());
219 return chidx;
221 std::transform(conf->Speakers.begin(), conf->Speakers.end(), std::begin(speakermap), map_spkr);
222 /* Return success if no invalid entries are found. */
223 auto spkrmap_end = std::begin(speakermap) + conf->Speakers.size();
224 return std::find(std::begin(speakermap), spkrmap_end, INVALID_CHANNEL_INDEX) == spkrmap_end;
228 void InitNearFieldCtrl(ALCdevice *device, float ctrl_dist, ALuint order, bool is3d)
230 static const ALuint chans_per_order2d[MAX_AMBI_ORDER+1]{ 1, 2, 2, 2 };
231 static const ALuint chans_per_order3d[MAX_AMBI_ORDER+1]{ 1, 3, 5, 7 };
233 /* NFC is only used when AvgSpeakerDist is greater than 0. */
234 const char *devname{device->DeviceName.c_str()};
235 if(!GetConfigValueBool(devname, "decoder", "nfc", 0) || !(ctrl_dist > 0.0f))
236 return;
238 device->AvgSpeakerDist = clampf(ctrl_dist, 0.1f, 10.0f);
239 TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
241 auto iter = std::copy_n(is3d ? chans_per_order3d : chans_per_order2d, order+1u,
242 std::begin(device->NumChannelsPerOrder));
243 std::fill(iter, std::end(device->NumChannelsPerOrder), 0u);
246 void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf,
247 const ALuint (&speakermap)[MAX_OUTPUT_CHANNELS])
249 auto get_max = std::bind(maxf, _1,
250 std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance), _2));
251 const float maxdist{std::accumulate(conf->Speakers.begin(), conf->Speakers.end(), 0.0f,
252 get_max)};
254 const char *devname{device->DeviceName.c_str()};
255 if(!GetConfigValueBool(devname, "decoder", "distance-comp", 1) || !(maxdist > 0.0f))
256 return;
258 const auto distSampleScale = static_cast<float>(device->Frequency) / SPEEDOFSOUNDMETRESPERSEC;
259 const auto ChanDelay = device->ChannelDelay.as_span();
260 size_t total{0u};
261 for(size_t i{0u};i < conf->Speakers.size();i++)
263 const AmbDecConf::SpeakerConf &speaker = conf->Speakers[i];
264 const ALuint chan{speakermap[i]};
266 /* Distance compensation only delays in steps of the sample rate. This
267 * is a bit less accurate since the delay time falls to the nearest
268 * sample time, but it's far simpler as it doesn't have to deal with
269 * phase offsets. This means at 48khz, for instance, the distance delay
270 * will be in steps of about 7 millimeters.
272 float delay{std::floor((maxdist - speaker.Distance)*distSampleScale + 0.5f)};
273 if(delay > float{MAX_DELAY_LENGTH-1})
275 ERR("Delay for speaker \"%s\" exceeds buffer length (%f > %d)\n",
276 speaker.Name.c_str(), delay, MAX_DELAY_LENGTH-1);
277 delay = float{MAX_DELAY_LENGTH-1};
280 ChanDelay[chan].Length = static_cast<ALuint>(delay);
281 ChanDelay[chan].Gain = speaker.Distance / maxdist;
282 TRACE("Channel %u \"%s\" distance compensation: %u samples, %f gain\n", chan,
283 speaker.Name.c_str(), ChanDelay[chan].Length, ChanDelay[chan].Gain);
285 /* Round up to the next 4th sample, so each channel buffer starts
286 * 16-byte aligned.
288 total += RoundUp(ChanDelay[chan].Length, 4);
291 if(total > 0)
293 device->ChannelDelay.setSampleCount(total);
294 ChanDelay[0].Buffer = device->ChannelDelay.getSamples();
295 auto set_bufptr = [](const DistanceComp::DistData &last, const DistanceComp::DistData &cur) -> DistanceComp::DistData
297 DistanceComp::DistData ret{cur};
298 ret.Buffer = last.Buffer + RoundUp(last.Length, 4);
299 return ret;
301 std::partial_sum(ChanDelay.begin(), ChanDelay.end(), ChanDelay.begin(), set_bufptr);
306 auto GetAmbiScales(DevAmbiScaling scaletype) noexcept -> const std::array<float,MAX_AMBI_CHANNELS>&
308 if(scaletype == DevAmbiScaling::FuMa) return AmbiScale::FromFuMa;
309 if(scaletype == DevAmbiScaling::SN3D) return AmbiScale::FromSN3D;
310 return AmbiScale::FromN3D;
313 auto GetAmbiLayout(DevAmbiLayout layouttype) noexcept -> const std::array<uint8_t,MAX_AMBI_CHANNELS>&
315 if(layouttype == DevAmbiLayout::FuMa) return AmbiIndex::FromFuMa;
316 return AmbiIndex::FromACN;
320 using ChannelCoeffs = std::array<float,MAX_AMBI2D_CHANNELS>;
321 enum DecoderMode : bool {
322 SingleBand = false,
323 DualBand = true
326 template<DecoderMode Mode, size_t N>
327 struct DecoderConfig;
329 template<size_t N>
330 struct DecoderConfig<SingleBand, N> {
331 ALuint mOrder;
332 std::array<Channel,N> mChannels;
333 std::array<float,MAX_AMBI_ORDER+1> mOrderGain;
334 std::array<ChannelCoeffs,N> mCoeffs;
337 template<size_t N>
338 struct DecoderConfig<DualBand, N> {
339 ALuint mOrder;
340 std::array<Channel,N> mChannels;
341 std::array<float,MAX_AMBI_ORDER+1> mOrderGain;
342 std::array<ChannelCoeffs,N> mCoeffs;
343 std::array<float,MAX_AMBI_ORDER+1> mOrderGainLF;
344 std::array<ChannelCoeffs,N> mCoeffsLF;
347 template<>
348 struct DecoderConfig<DualBand, 0> {
349 ALuint mOrder;
350 al::span<const Channel> mChannels;
351 al::span<const float> mOrderGain;
352 al::span<const ChannelCoeffs> mCoeffs;
353 al::span<const float> mOrderGainLF;
354 al::span<const ChannelCoeffs> mCoeffsLF;
356 template<size_t N>
357 DecoderConfig& operator=(const DecoderConfig<SingleBand,N> &rhs) noexcept
359 mOrder = rhs.mOrder;
360 mChannels = rhs.mChannels;
361 mOrderGain = rhs.mOrderGain;
362 mCoeffs = rhs.mCoeffs;
363 mOrderGainLF = {};
364 mCoeffsLF = {};
365 return *this;
368 template<size_t N>
369 DecoderConfig& operator=(const DecoderConfig<DualBand,N> &rhs) noexcept
371 mOrder = rhs.mOrder;
372 mChannels = rhs.mChannels;
373 mOrderGain = rhs.mOrderGain;
374 mCoeffs = rhs.mCoeffs;
375 mOrderGainLF = rhs.mOrderGainLF;
376 mCoeffsLF = rhs.mCoeffsLF;
377 return *this;
380 using DecoderView = DecoderConfig<DualBand, 0>;
382 constexpr DecoderConfig<SingleBand, 1> MonoConfig{
383 0, {{FrontCenter}},
384 {{1.0f}},
385 {{ {{1.0f}} }}
387 constexpr DecoderConfig<SingleBand, 2> StereoConfig{
388 1, {{FrontLeft, FrontRight}},
389 {{1.0f, 1.0f}},
391 {{5.00000000e-1f, 2.88675135e-1f, 5.52305643e-2f}},
392 {{5.00000000e-1f, -2.88675135e-1f, 5.52305643e-2f}},
395 constexpr DecoderConfig<DualBand, 4> QuadConfig{
396 2, {{BackLeft, FrontLeft, FrontRight, BackRight}},
397 /*HF*/{{1.15470054e+0f, 1.00000000e+0f, 5.77350269e-1f}},
399 {{2.50000000e-1f, 2.04124145e-1f, -2.04124145e-1f, -1.29099445e-1f, 0.00000000e+0f}},
400 {{2.50000000e-1f, 2.04124145e-1f, 2.04124145e-1f, 1.29099445e-1f, 0.00000000e+0f}},
401 {{2.50000000e-1f, -2.04124145e-1f, 2.04124145e-1f, -1.29099445e-1f, 0.00000000e+0f}},
402 {{2.50000000e-1f, -2.04124145e-1f, -2.04124145e-1f, 1.29099445e-1f, 0.00000000e+0f}},
404 /*LF*/{{1.00000000e+0f, 1.00000000e+0f, 1.00000000e+0f}},
406 {{2.50000000e-1f, 2.04124145e-1f, -2.04124145e-1f, -1.29099445e-1f, 0.00000000e+0f}},
407 {{2.50000000e-1f, 2.04124145e-1f, 2.04124145e-1f, 1.29099445e-1f, 0.00000000e+0f}},
408 {{2.50000000e-1f, -2.04124145e-1f, 2.04124145e-1f, -1.29099445e-1f, 0.00000000e+0f}},
409 {{2.50000000e-1f, -2.04124145e-1f, -2.04124145e-1f, 1.29099445e-1f, 0.00000000e+0f}},
412 constexpr DecoderConfig<SingleBand, 4> X51Config{
413 2, {{SideLeft, FrontLeft, FrontRight, SideRight}},
414 {{1.0f, 1.0f, 1.0f}},
416 {{3.33000782e-1f, 1.89084803e-1f, -2.00042375e-1f, -2.12307769e-2f, -1.14579885e-2f}},
417 {{1.88542860e-1f, 1.27709292e-1f, 1.66295695e-1f, 7.30571517e-2f, 2.10901184e-2f}},
418 {{1.88542860e-1f, -1.27709292e-1f, 1.66295695e-1f, -7.30571517e-2f, 2.10901184e-2f}},
419 {{3.33000782e-1f, -1.89084803e-1f, -2.00042375e-1f, 2.12307769e-2f, -1.14579885e-2f}},
422 constexpr DecoderConfig<SingleBand, 4> X51RearConfig{
423 2, {{BackLeft, FrontLeft, FrontRight, BackRight}},
424 {{1.0f, 1.0f, 1.0f}},
426 {{3.33000782e-1f, 1.89084803e-1f, -2.00042375e-1f, -2.12307769e-2f, -1.14579885e-2f}},
427 {{1.88542860e-1f, 1.27709292e-1f, 1.66295695e-1f, 7.30571517e-2f, 2.10901184e-2f}},
428 {{1.88542860e-1f, -1.27709292e-1f, 1.66295695e-1f, -7.30571517e-2f, 2.10901184e-2f}},
429 {{3.33000782e-1f, -1.89084803e-1f, -2.00042375e-1f, 2.12307769e-2f, -1.14579885e-2f}},
432 constexpr DecoderConfig<SingleBand, 5> X61Config{
433 2, {{SideLeft, FrontLeft, FrontRight, SideRight, BackCenter}},
434 {{1.0f, 1.0f, 1.0f}},
436 {{2.04460341e-1f, 2.17177926e-1f, -4.39996780e-2f, -2.60790269e-2f, -6.87239792e-2f}},
437 {{1.58923161e-1f, 9.21772680e-2f, 1.59658796e-1f, 6.66278083e-2f, 3.84686854e-2f}},
438 {{1.58923161e-1f, -9.21772680e-2f, 1.59658796e-1f, -6.66278083e-2f, 3.84686854e-2f}},
439 {{2.04460341e-1f, -2.17177926e-1f, -4.39996780e-2f, 2.60790269e-2f, -6.87239792e-2f}},
440 {{2.50001688e-1f, 0.00000000e+0f, -2.50000094e-1f, 0.00000000e+0f, 6.05133395e-2f}},
443 constexpr DecoderConfig<DualBand, 6> X71Config{
444 3, {{BackLeft, SideLeft, FrontLeft, FrontRight, SideRight, BackRight}},
445 /*HF*/{{1.22474487e+0f, 1.13151672e+0f, 8.66025404e-1f, 4.68689571e-1f}},
447 {{1.66666667e-1f, 9.62250449e-2f, -1.66666667e-1f, -1.49071198e-1f, 8.60662966e-2f, 7.96819073e-2f, 0.00000000e+0f}},
448 {{1.66666667e-1f, 1.92450090e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.72132593e-1f, -7.96819073e-2f, 0.00000000e+0f}},
449 {{1.66666667e-1f, 9.62250449e-2f, 1.66666667e-1f, 1.49071198e-1f, 8.60662966e-2f, 7.96819073e-2f, 0.00000000e+0f}},
450 {{1.66666667e-1f, -9.62250449e-2f, 1.66666667e-1f, -1.49071198e-1f, 8.60662966e-2f, -7.96819073e-2f, 0.00000000e+0f}},
451 {{1.66666667e-1f, -1.92450090e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.72132593e-1f, 7.96819073e-2f, 0.00000000e+0f}},
452 {{1.66666667e-1f, -9.62250449e-2f, -1.66666667e-1f, 1.49071198e-1f, 8.60662966e-2f, -7.96819073e-2f, 0.00000000e+0f}},
454 /*LF*/{{1.00000000e+0f, 1.00000000e+0f, 1.00000000e+0f, 1.00000000e+0f}},
456 {{1.66666667e-1f, 9.62250449e-2f, -1.66666667e-1f, -1.49071198e-1f, 8.60662966e-2f, 7.96819073e-2f, 0.00000000e+0f}},
457 {{1.66666667e-1f, 1.92450090e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.72132593e-1f, -7.96819073e-2f, 0.00000000e+0f}},
458 {{1.66666667e-1f, 9.62250449e-2f, 1.66666667e-1f, 1.49071198e-1f, 8.60662966e-2f, 7.96819073e-2f, 0.00000000e+0f}},
459 {{1.66666667e-1f, -9.62250449e-2f, 1.66666667e-1f, -1.49071198e-1f, 8.60662966e-2f, -7.96819073e-2f, 0.00000000e+0f}},
460 {{1.66666667e-1f, -1.92450090e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.72132593e-1f, 7.96819073e-2f, 0.00000000e+0f}},
461 {{1.66666667e-1f, -9.62250449e-2f, -1.66666667e-1f, 1.49071198e-1f, 8.60662966e-2f, -7.96819073e-2f, 0.00000000e+0f}},
465 void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=false)
467 DecoderView decoder{};
468 switch(device->FmtChans)
470 case DevFmtMono:
471 decoder = MonoConfig;
472 break;
473 case DevFmtStereo:
474 decoder = StereoConfig;
475 break;
476 case DevFmtQuad:
477 decoder = QuadConfig;
478 break;
479 case DevFmtX51:
480 decoder = X51Config;
481 break;
482 case DevFmtX51Rear:
483 decoder = X51RearConfig;
484 break;
485 case DevFmtX61:
486 decoder = X61Config;
487 break;
488 case DevFmtX71:
489 decoder = X71Config;
490 break;
491 case DevFmtAmbi3D:
492 break;
495 if(device->FmtChans == DevFmtAmbi3D)
497 const char *devname{device->DeviceName.c_str()};
498 const std::array<uint8_t,MAX_AMBI_CHANNELS> &acnmap = GetAmbiLayout(device->mAmbiLayout);
499 const std::array<float,MAX_AMBI_CHANNELS> &n3dscale = GetAmbiScales(device->mAmbiScale);
501 /* For DevFmtAmbi3D, the ambisonic order is already set. */
502 const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
503 std::transform(acnmap.begin(), acnmap.begin()+count, std::begin(device->Dry.AmbiMap),
504 [&n3dscale](const uint8_t &acn) noexcept -> BFChannelConfig
505 { return BFChannelConfig{1.0f/n3dscale[acn], acn}; }
507 AllocChannels(device, count, 0);
509 float nfc_delay{ConfigValueFloat(devname, "decoder", "nfc-ref-delay").value_or(0.0f)};
510 if(nfc_delay > 0.0f)
511 InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC, device->mAmbiOrder,
512 true);
514 else
516 const bool dual_band{hqdec && !decoder.mCoeffsLF.empty()};
517 al::vector<ChannelDec> chancoeffs, chancoeffslf;
518 for(size_t i{0u};i < decoder.mChannels.size();++i)
520 const ALuint idx{GetChannelIdxByName(device->RealOut, decoder.mChannels[i])};
521 if(idx == INVALID_CHANNEL_INDEX)
523 ERR("Failed to find %s channel in device\n",
524 GetLabelFromChannel(decoder.mChannels[i]));
525 continue;
528 chancoeffs.resize(maxz(chancoeffs.size(), idx+1u), ChannelDec{});
529 al::span<float,MAX_AMBI_CHANNELS> coeffs{chancoeffs[idx]};
530 size_t start{0};
531 for(ALuint o{0};o <= decoder.mOrder;++o)
533 size_t count{o ? 2u : 1u};
534 do {
535 coeffs[start] = decoder.mCoeffs[i][start] * decoder.mOrderGain[o];
536 ++start;
537 } while(--count);
539 if(!dual_band)
540 continue;
542 chancoeffslf.resize(maxz(chancoeffslf.size(), idx+1u), ChannelDec{});
543 coeffs = chancoeffslf[idx];
544 start = 0;
545 for(ALuint o{0};o <= decoder.mOrder;++o)
547 size_t count{o ? 2u : 1u};
548 do {
549 coeffs[start] = decoder.mCoeffsLF[i][start] * decoder.mOrderGainLF[o];
550 ++start;
551 } while(--count);
555 /* For non-DevFmtAmbi3D, set the ambisonic order. */
556 device->mAmbiOrder = decoder.mOrder;
558 /* Built-in speaker decoders are always 2D. */
559 const size_t ambicount{Ambi2DChannelsFromOrder(decoder.mOrder)};
560 std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+ambicount,
561 std::begin(device->Dry.AmbiMap),
562 [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; }
564 AllocChannels(device, ambicount, device->channelsFromFmt());
566 std::unique_ptr<FrontStablizer> stablizer;
567 if(stablize)
569 /* Only enable the stablizer if the decoder does not output to the
570 * front-center channel.
572 const auto cidx = device->RealOut.ChannelIndex[FrontCenter];
573 bool hasfc{false};
574 if(cidx < chancoeffs.size())
576 for(const auto &coeff : chancoeffs[cidx])
577 hasfc |= coeff != 0.0f;
579 if(!hasfc && cidx < chancoeffslf.size())
581 for(const auto &coeff : chancoeffslf[cidx])
582 hasfc |= coeff != 0.0f;
584 if(!hasfc)
586 stablizer = CreateStablizer(device->channelsFromFmt(), device->Frequency);
587 TRACE("Front stablizer enabled\n");
591 TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
592 !dual_band ? "single" : "dual",
593 (decoder.mOrder > 2) ? "third" :
594 (decoder.mOrder > 1) ? "second" : "first",
595 "");
596 device->AmbiDecoder = BFormatDec::Create(ambicount, chancoeffs, chancoeffslf,
597 std::move(stablizer));
601 void InitCustomPanning(ALCdevice *device, const bool hqdec, const bool stablize,
602 const AmbDecConf *conf, const ALuint (&speakermap)[MAX_OUTPUT_CHANNELS])
604 if(!hqdec && conf->FreqBands != 1)
605 ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
606 conf->XOverFreq);
608 const ALuint order{(conf->ChanMask > AMBI_2ORDER_MASK) ? 3u :
609 (conf->ChanMask > AMBI_1ORDER_MASK) ? 2u : 1u};
610 device->mAmbiOrder = order;
612 size_t count;
613 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
615 count = AmbiChannelsFromOrder(order);
616 std::transform(AmbiIndex::FromACN.begin(), AmbiIndex::FromACN.begin()+count,
617 std::begin(device->Dry.AmbiMap),
618 [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; }
621 else
623 count = Ambi2DChannelsFromOrder(order);
624 std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+count,
625 std::begin(device->Dry.AmbiMap),
626 [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; }
629 AllocChannels(device, count, device->channelsFromFmt());
631 std::unique_ptr<FrontStablizer> stablizer;
632 if(stablize)
634 /* Only enable the stablizer if the decoder does not output to the
635 * front-center channel.
637 size_t cidx{0};
638 for(;cidx < conf->Speakers.size();++cidx)
640 if(speakermap[cidx] == FrontCenter)
641 break;
643 bool hasfc{false};
644 if(cidx < conf->LFMatrix.size())
646 for(const auto &coeff : conf->LFMatrix[cidx])
647 hasfc |= coeff != 0.0f;
649 if(!hasfc && cidx < conf->HFMatrix.size())
651 for(const auto &coeff : conf->HFMatrix[cidx])
652 hasfc |= coeff != 0.0f;
654 if(!hasfc)
656 stablizer = CreateStablizer(device->channelsFromFmt(), device->Frequency);
657 TRACE("Front stablizer enabled\n");
661 TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
662 (!hqdec || conf->FreqBands == 1) ? "single" : "dual",
663 (conf->ChanMask > AMBI_2ORDER_MASK) ? "third" :
664 (conf->ChanMask > AMBI_1ORDER_MASK) ? "second" : "first",
665 (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
667 device->AmbiDecoder = BFormatDec::Create(conf, hqdec, count, device->Frequency, speakermap,
668 std::move(stablizer));
670 auto accum_spkr_dist = std::bind(std::plus<float>{}, _1,
671 std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance), _2));
672 const float avg_dist{
673 std::accumulate(conf->Speakers.begin(), conf->Speakers.end(), 0.0f, accum_spkr_dist) /
674 static_cast<float>(conf->Speakers.size())};
675 InitNearFieldCtrl(device, avg_dist, order, !!(conf->ChanMask&AMBI_PERIPHONIC_MASK));
677 InitDistanceComp(device, conf, speakermap);
680 void InitHrtfPanning(ALCdevice *device)
682 constexpr float PI{al::MathDefs<float>::Pi()};
683 constexpr float PI_2{PI / 2.0f};
684 constexpr float PI_4{PI_2 / 2.0f};
685 constexpr float PI3_4{PI_4 * 3.0f};
686 static const float CornerElev{static_cast<float>(std::atan2(1.0, std::sqrt(2.0)))};
687 static const AngularPoint AmbiPoints1O[]{
688 { EvRadians{ CornerElev}, AzRadians{ -PI_4} },
689 { EvRadians{ CornerElev}, AzRadians{-PI3_4} },
690 { EvRadians{ CornerElev}, AzRadians{ PI_4} },
691 { EvRadians{ CornerElev}, AzRadians{ PI3_4} },
692 { EvRadians{-CornerElev}, AzRadians{ -PI_4} },
693 { EvRadians{-CornerElev}, AzRadians{-PI3_4} },
694 { EvRadians{-CornerElev}, AzRadians{ PI_4} },
695 { EvRadians{-CornerElev}, AzRadians{ PI3_4} },
696 }, AmbiPoints2O[]{
697 { EvRadians{ -CornerElev}, AzRadians{ -PI_4} },
698 { EvRadians{ -CornerElev}, AzRadians{ -PI3_4} },
699 { EvRadians{ CornerElev}, AzRadians{ -PI3_4} },
700 { EvRadians{ CornerElev}, AzRadians{ PI3_4} },
701 { EvRadians{ CornerElev}, AzRadians{ PI_4} },
702 { EvRadians{ -CornerElev}, AzRadians{ PI_4} },
703 { EvRadians{ -CornerElev}, AzRadians{ PI3_4} },
704 { EvRadians{ CornerElev}, AzRadians{ -PI_4} },
705 { EvRadians{-1.205932499e+00f}, AzRadians{ -PI_2} },
706 { EvRadians{ 1.205932499e+00f}, AzRadians{ PI_2} },
707 { EvRadians{-1.205932499e+00f}, AzRadians{ PI_2} },
708 { EvRadians{ 1.205932499e+00f}, AzRadians{ -PI_2} },
709 { EvRadians{ 0.0f}, AzRadians{-1.205932499e+00f} },
710 { EvRadians{ 0.0f}, AzRadians{-1.935660155e+00f} },
711 { EvRadians{ 0.0f}, AzRadians{ 1.205932499e+00f} },
712 { EvRadians{ 0.0f}, AzRadians{ 1.935660155e+00f} },
713 { EvRadians{-3.648638281e-01f}, AzRadians{ PI} },
714 { EvRadians{ 3.648638281e-01f}, AzRadians{ PI} },
715 { EvRadians{ 3.648638281e-01f}, AzRadians{ 0.0f} },
716 { EvRadians{-3.648638281e-01f}, AzRadians{ 0.0f} },
718 static const float AmbiMatrix1O[][MAX_AMBI_CHANNELS]{
719 { 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f },
720 { 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f },
721 { 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f },
722 { 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f },
723 { 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f },
724 { 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f },
725 { 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f },
726 { 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f },
727 }, AmbiMatrix2O[][MAX_AMBI_CHANNELS]{
728 { 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f },
729 { 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f },
730 { 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f },
731 { 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f },
732 { 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f },
733 { 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f },
734 { 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f },
735 { 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f },
736 { 5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f },
737 { 5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f },
738 { 5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f },
739 { 5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f },
740 { 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f },
741 { 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f },
742 { 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f },
743 { 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f },
744 { 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f },
745 { 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f },
746 { 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f },
747 { 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f },
749 static const float AmbiOrderHFGain1O[MAX_AMBI_ORDER+1]{
750 2.000000000e+00f, 1.154700538e+00f
751 }, AmbiOrderHFGain2O[MAX_AMBI_ORDER+1]{
752 2.357022604e+00f, 1.825741858e+00f, 9.428090416e-01f
755 static_assert(al::size(AmbiPoints1O) == al::size(AmbiMatrix1O), "First-Order Ambisonic HRTF mismatch");
756 static_assert(al::size(AmbiPoints2O) == al::size(AmbiMatrix2O), "Second-Order Ambisonic HRTF mismatch");
758 /* Don't bother with HOA when using full HRTF rendering. Nothing needs it,
759 * and it eases the CPU/memory load.
761 device->mRenderMode = RenderMode::Hrtf;
762 ALuint ambi_order{1};
763 if(auto modeopt = ConfigValueStr(device->DeviceName.c_str(), nullptr, "hrtf-mode"))
765 struct HrtfModeEntry {
766 char name[8];
767 RenderMode mode;
768 ALuint order;
770 static const HrtfModeEntry hrtf_modes[]{
771 { "full", RenderMode::Hrtf, 1 },
772 { "ambi1", RenderMode::Normal, 1 },
773 { "ambi2", RenderMode::Normal, 2 },
776 const char *mode{modeopt->c_str()};
777 if(al::strcasecmp(mode, "basic") == 0 || al::strcasecmp(mode, "ambi3") == 0)
779 ERR("HRTF mode \"%s\" deprecated, substituting \"%s\"\n", mode, "ambi2");
780 mode = "ambi2";
783 auto match_entry = [mode](const HrtfModeEntry &entry) -> bool
784 { return al::strcasecmp(mode, entry.name) == 0; };
785 auto iter = std::find_if(std::begin(hrtf_modes), std::end(hrtf_modes), match_entry);
786 if(iter == std::end(hrtf_modes))
787 ERR("Unexpected hrtf-mode: %s\n", mode);
788 else
790 device->mRenderMode = iter->mode;
791 ambi_order = iter->order;
794 TRACE("%u%s order %sHRTF rendering enabled, using \"%s\"\n", ambi_order,
795 (((ambi_order%100)/10) == 1) ? "th" :
796 ((ambi_order%10) == 1) ? "st" :
797 ((ambi_order%10) == 2) ? "nd" :
798 ((ambi_order%10) == 3) ? "rd" : "th",
799 (device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "",
800 device->HrtfName.c_str());
802 al::span<const AngularPoint> AmbiPoints{AmbiPoints1O};
803 const float (*AmbiMatrix)[MAX_AMBI_CHANNELS]{AmbiMatrix1O};
804 al::span<const float,MAX_AMBI_ORDER+1> AmbiOrderHFGain{AmbiOrderHFGain1O};
805 if(ambi_order >= 2)
807 AmbiPoints = AmbiPoints2O;
808 AmbiMatrix = AmbiMatrix2O;
809 AmbiOrderHFGain = AmbiOrderHFGain2O;
811 device->mAmbiOrder = ambi_order;
813 const size_t count{AmbiChannelsFromOrder(ambi_order)};
814 std::transform(AmbiIndex::FromACN.begin(), AmbiIndex::FromACN.begin()+count,
815 std::begin(device->Dry.AmbiMap),
816 [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; }
818 AllocChannels(device, count, device->channelsFromFmt());
820 HrtfStore *Hrtf{device->mHrtf.get()};
821 auto hrtfstate = DirectHrtfState::Create(count);
822 hrtfstate->build(Hrtf, AmbiPoints, AmbiMatrix, AmbiOrderHFGain);
823 device->mHrtfState = std::move(hrtfstate);
825 InitNearFieldCtrl(device, Hrtf->field[0].distance, ambi_order, true);
828 void InitUhjPanning(ALCdevice *device)
830 /* UHJ is always 2D first-order. */
831 constexpr size_t count{Ambi2DChannelsFromOrder(1)};
833 device->mAmbiOrder = 1;
835 auto acnmap_end = AmbiIndex::FromFuMa.begin() + count;
836 std::transform(AmbiIndex::FromFuMa.begin(), acnmap_end, std::begin(device->Dry.AmbiMap),
837 [](const uint8_t &acn) noexcept -> BFChannelConfig
838 { return BFChannelConfig{1.0f/AmbiScale::FromFuMa[acn], acn}; }
840 AllocChannels(device, count, device->channelsFromFmt());
843 } // namespace
845 void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq,
846 HrtfRequestMode hrtf_userreq)
848 /* Hold the HRTF the device last used, in case it's used again. */
849 HrtfStorePtr old_hrtf{std::move(device->mHrtf)};
851 device->mHrtfState = nullptr;
852 device->mHrtf = nullptr;
853 device->HrtfName.clear();
854 device->mRenderMode = RenderMode::Normal;
856 if(device->FmtChans != DevFmtStereo)
858 old_hrtf = nullptr;
859 if(hrtf_appreq == Hrtf_Enable)
860 device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
862 const char *layout{nullptr};
863 switch(device->FmtChans)
865 case DevFmtQuad: layout = "quad"; break;
866 case DevFmtX51: /* fall-through */
867 case DevFmtX51Rear: layout = "surround51"; break;
868 case DevFmtX61: layout = "surround61"; break;
869 case DevFmtX71: layout = "surround71"; break;
870 /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
871 case DevFmtMono:
872 case DevFmtStereo:
873 case DevFmtAmbi3D:
874 break;
877 const char *devname{device->DeviceName.c_str()};
878 ALuint speakermap[MAX_OUTPUT_CHANNELS];
879 AmbDecConf *pconf{nullptr};
880 AmbDecConf conf{};
881 if(layout)
883 if(auto decopt = ConfigValueStr(devname, "decoder", layout))
885 if(!conf.load(decopt->c_str()))
886 ERR("Failed to load layout file %s\n", decopt->c_str());
887 else if(conf.Speakers.size() > MAX_OUTPUT_CHANNELS)
888 ERR("Unsupported speaker count %zu (max %d)\n", conf.Speakers.size(),
889 MAX_OUTPUT_CHANNELS);
890 else if(conf.ChanMask > AMBI_3ORDER_MASK)
891 ERR("Unsupported channel mask 0x%04x (max 0x%x)\n", conf.ChanMask,
892 AMBI_3ORDER_MASK);
893 else if(MakeSpeakerMap(device, &conf, speakermap))
894 pconf = &conf;
898 /* Enable the stablizer only for formats that have front-left, front-
899 * right, and front-center outputs.
901 const bool stablize{device->RealOut.ChannelIndex[FrontCenter] != INVALID_CHANNEL_INDEX
902 && device->RealOut.ChannelIndex[FrontLeft] != INVALID_CHANNEL_INDEX
903 && device->RealOut.ChannelIndex[FrontRight] != INVALID_CHANNEL_INDEX
904 && GetConfigValueBool(devname, nullptr, "front-stablizer", 0) != 0};
905 const bool hqdec{GetConfigValueBool(devname, "decoder", "hq-mode", 1) != 0};
906 if(!pconf)
907 InitPanning(device, hqdec, stablize);
908 else
909 InitCustomPanning(device, hqdec, stablize, pconf, speakermap);
910 if(auto *ambidec{device->AmbiDecoder.get()})
912 device->PostProcess = ambidec->hasStablizer() ? &ALCdevice::ProcessAmbiDecStablized
913 : &ALCdevice::ProcessAmbiDec;
915 return;
918 bool headphones{device->IsHeadphones};
919 if(device->Type != DeviceType::Loopback)
921 if(auto modeopt = ConfigValueStr(device->DeviceName.c_str(), nullptr, "stereo-mode"))
923 const char *mode{modeopt->c_str()};
924 if(al::strcasecmp(mode, "headphones") == 0)
925 headphones = true;
926 else if(al::strcasecmp(mode, "speakers") == 0)
927 headphones = false;
928 else if(al::strcasecmp(mode, "auto") != 0)
929 ERR("Unexpected stereo-mode: %s\n", mode);
933 if(hrtf_userreq == Hrtf_Default)
935 bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
936 (hrtf_appreq == Hrtf_Enable);
937 if(!usehrtf) goto no_hrtf;
939 device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
940 if(headphones && hrtf_appreq != Hrtf_Disable)
941 device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
943 else
945 if(hrtf_userreq != Hrtf_Enable)
947 if(hrtf_appreq == Hrtf_Enable)
948 device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
949 goto no_hrtf;
951 device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
954 if(device->HrtfList.empty())
955 device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
957 if(hrtf_id >= 0 && static_cast<ALuint>(hrtf_id) < device->HrtfList.size())
959 const char *devname{device->DeviceName.c_str()};
960 const std::string &hrtfname = device->HrtfList[static_cast<ALuint>(hrtf_id)];
961 if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, devname, device->Frequency)})
963 device->mHrtf = std::move(hrtf);
964 device->HrtfName = hrtfname;
968 if(!device->mHrtf)
970 const char *devname{device->DeviceName.c_str()};
971 auto find_hrtf = [device,devname](const std::string &hrtfname) -> bool
973 HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, devname, device->Frequency)};
974 if(!hrtf) return false;
975 device->mHrtf = std::move(hrtf);
976 device->HrtfName = hrtfname;
977 return true;
979 std::find_if(device->HrtfList.cbegin(), device->HrtfList.cend(), find_hrtf);
982 if(device->mHrtf)
984 old_hrtf = nullptr;
986 InitHrtfPanning(device);
987 device->PostProcess = &ALCdevice::ProcessHrtf;
988 return;
990 device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
992 no_hrtf:
993 old_hrtf = nullptr;
995 device->mRenderMode = RenderMode::Pairwise;
997 if(device->Type != DeviceType::Loopback)
999 if(auto cflevopt = ConfigValueInt(device->DeviceName.c_str(), nullptr, "cf_level"))
1001 if(*cflevopt > 0 && *cflevopt <= 6)
1003 device->Bs2b = std::make_unique<bs2b>();
1004 bs2b_set_params(device->Bs2b.get(), *cflevopt,
1005 static_cast<int>(device->Frequency));
1006 TRACE("BS2B enabled\n");
1007 InitPanning(device);
1008 device->PostProcess = &ALCdevice::ProcessBs2b;
1009 return;
1014 if(auto encopt = ConfigValueStr(device->DeviceName.c_str(), nullptr, "stereo-encoding"))
1016 const char *mode{encopt->c_str()};
1017 if(al::strcasecmp(mode, "uhj") == 0)
1018 device->mRenderMode = RenderMode::Normal;
1019 else if(al::strcasecmp(mode, "panpot") != 0)
1020 ERR("Unexpected stereo-encoding: %s\n", mode);
1022 if(device->mRenderMode == RenderMode::Normal)
1024 device->Uhj_Encoder = std::make_unique<Uhj2Encoder>();
1025 TRACE("UHJ enabled\n");
1026 InitUhjPanning(device);
1027 device->PostProcess = &ALCdevice::ProcessUhj;
1028 return;
1031 TRACE("Stereo rendering\n");
1032 InitPanning(device);
1033 device->PostProcess = &ALCdevice::ProcessAmbiDec;
1037 void aluInitEffectPanning(ALeffectslot *slot, ALCdevice *device)
1039 const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
1040 slot->MixBuffer.resize(count);
1041 slot->MixBuffer.shrink_to_fit();
1043 auto acnmap_end = AmbiIndex::FromACN.begin() + count;
1044 auto iter = std::transform(AmbiIndex::FromACN.begin(), acnmap_end, slot->Wet.AmbiMap.begin(),
1045 [](const uint8_t &acn) noexcept -> BFChannelConfig
1046 { return BFChannelConfig{1.0f, acn}; }
1048 std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{});
1049 slot->Wet.Buffer = {slot->MixBuffer.data(), slot->MixBuffer.size()};
1053 std::array<float,MAX_AMBI_CHANNELS> CalcAmbiCoeffs(const float y, const float z, const float x,
1054 const float spread)
1056 std::array<float,MAX_AMBI_CHANNELS> coeffs;
1058 /* Zeroth-order */
1059 coeffs[0] = 1.0f; /* ACN 0 = 1 */
1060 /* First-order */
1061 coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
1062 coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
1063 coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
1064 /* Second-order */
1065 const float xx{x*x}, yy{y*y}, zz{z*z};
1066 coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
1067 coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
1068 coeffs[6] = 1.118033989f * (3.0f*zz - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
1069 coeffs[7] = 3.872983346f * x * z; /* ACN 7 = sqrt(15) * X * Z */
1070 coeffs[8] = 1.936491673f * (xx - yy); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
1071 /* Third-order */
1072 coeffs[9] = 2.091650066f * y * (3.0f*xx - yy); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
1073 coeffs[10] = 10.246950766f * z * x * y; /* ACN 10 = sqrt(105) * Z * X * Y */
1074 coeffs[11] = 1.620185175f * y * (5.0f*zz - 1.0f); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
1075 coeffs[12] = 1.322875656f * z * (5.0f*zz - 3.0f); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
1076 coeffs[13] = 1.620185175f * x * (5.0f*zz - 1.0f); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
1077 coeffs[14] = 5.123475383f * z * (xx - yy); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
1078 coeffs[15] = 2.091650066f * x * (xx - 3.0f*yy); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
1079 /* Fourth-order */
1080 /* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */
1081 /* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */
1082 /* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */
1083 /* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */
1084 /* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */
1085 /* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */
1086 /* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */
1087 /* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */
1088 /* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */
1090 if(spread > 0.0f)
1092 /* Implement the spread by using a spherical source that subtends the
1093 * angle spread. See:
1094 * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
1096 * When adjusted for N3D normalization instead of SN3D, these
1097 * calculations are:
1099 * ZH0 = -sqrt(pi) * (-1+ca);
1100 * ZH1 = 0.5*sqrt(pi) * sa*sa;
1101 * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
1102 * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
1103 * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
1104 * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
1106 * The gain of the source is compensated for size, so that the
1107 * loudness doesn't depend on the spread. Thus:
1109 * ZH0 = 1.0f;
1110 * ZH1 = 0.5f * (ca+1.0f);
1111 * ZH2 = 0.5f * (ca+1.0f)*ca;
1112 * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
1113 * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
1114 * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
1116 const float ca{std::cos(spread * 0.5f)};
1117 /* Increase the source volume by up to +3dB for a full spread. */
1118 const float scale{std::sqrt(1.0f + spread/al::MathDefs<float>::Tau())};
1120 const float ZH0_norm{scale};
1121 const float ZH1_norm{scale * 0.5f * (ca+1.f)};
1122 const float ZH2_norm{scale * 0.5f * (ca+1.f)*ca};
1123 const float ZH3_norm{scale * 0.125f * (ca+1.f)*(5.f*ca*ca-1.f)};
1125 /* Zeroth-order */
1126 coeffs[0] *= ZH0_norm;
1127 /* First-order */
1128 coeffs[1] *= ZH1_norm;
1129 coeffs[2] *= ZH1_norm;
1130 coeffs[3] *= ZH1_norm;
1131 /* Second-order */
1132 coeffs[4] *= ZH2_norm;
1133 coeffs[5] *= ZH2_norm;
1134 coeffs[6] *= ZH2_norm;
1135 coeffs[7] *= ZH2_norm;
1136 coeffs[8] *= ZH2_norm;
1137 /* Third-order */
1138 coeffs[9] *= ZH3_norm;
1139 coeffs[10] *= ZH3_norm;
1140 coeffs[11] *= ZH3_norm;
1141 coeffs[12] *= ZH3_norm;
1142 coeffs[13] *= ZH3_norm;
1143 coeffs[14] *= ZH3_norm;
1144 coeffs[15] *= ZH3_norm;
1147 return coeffs;
1150 void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain,
1151 const al::span<float,MAX_OUTPUT_CHANNELS> gains)
1153 auto ambimap = mix->AmbiMap.cbegin();
1155 auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), gains.begin(),
1156 [coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> float
1157 { return chanmap.Scale * coeffs[chanmap.Index] * ingain; }
1159 std::fill(iter, gains.end(), 0.0f);