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
35 #include "alAuxEffectSlot.h"
39 #include "bformatdec.h"
40 #include "filters/splitter.h"
41 #include "uhjfilter.h"
47 constexpr ALsizei FuMa2ACN
[MAX_AMBI_COEFFS
] = {
65 constexpr ALsizei ACN2ACN
[MAX_AMBI_COEFFS
] = {
66 0, 1, 2, 3, 4, 5, 6, 7,
67 8, 9, 10, 11, 12, 13, 14, 15
70 inline const char *GetLabelFromChannel(Channel channel
)
74 case FrontLeft
: return "front-left";
75 case FrontRight
: return "front-right";
76 case FrontCenter
: return "front-center";
77 case LFE
: return "lfe";
78 case BackLeft
: return "back-left";
79 case BackRight
: return "back-right";
80 case BackCenter
: return "back-center";
81 case SideLeft
: return "side-left";
82 case SideRight
: return "side-right";
84 case UpperFrontLeft
: return "upper-front-left";
85 case UpperFrontRight
: return "upper-front-right";
86 case UpperBackLeft
: return "upper-back-left";
87 case UpperBackRight
: return "upper-back-right";
88 case LowerFrontLeft
: return "lower-front-left";
89 case LowerFrontRight
: return "lower-front-right";
90 case LowerBackLeft
: return "lower-back-left";
91 case LowerBackRight
: return "lower-back-right";
93 case Aux0
: return "aux-0";
94 case Aux1
: return "aux-1";
95 case Aux2
: return "aux-2";
96 case Aux3
: return "aux-3";
97 case Aux4
: return "aux-4";
98 case Aux5
: return "aux-5";
99 case Aux6
: return "aux-6";
100 case Aux7
: return "aux-7";
101 case Aux8
: return "aux-8";
102 case Aux9
: return "aux-9";
103 case Aux10
: return "aux-10";
104 case Aux11
: return "aux-11";
105 case Aux12
: return "aux-12";
106 case Aux13
: return "aux-13";
107 case Aux14
: return "aux-14";
108 case Aux15
: return "aux-15";
110 case InvalidChannel
: break;
118 ChannelConfig Config
;
121 void SetChannelMap(const Channel (&devchans
)[MAX_OUTPUT_CHANNELS
], ChannelConfig
*ambicoeffs
,
122 const ChannelMap
*chanmap
, const size_t count
, ALsizei
*outcount
)
124 auto copy_coeffs
= [&devchans
,ambicoeffs
](ALsizei maxchans
, const ChannelMap
&channel
) -> ALsizei
126 const ALint idx
{GetChannelIndex(devchans
, channel
.ChanName
)};
129 ERR("Failed to find %s channel in device\n", GetLabelFromChannel(channel
.ChanName
));
133 std::copy(std::begin(channel
.Config
), std::end(channel
.Config
), ambicoeffs
[idx
]);
134 return maxi(maxchans
, idx
+1);
136 ALsizei maxcount
{std::accumulate(chanmap
, chanmap
+count
, ALsizei
{0}, copy_coeffs
)};
137 *outcount
= mini(maxcount
, MAX_OUTPUT_CHANNELS
);
140 bool MakeSpeakerMap(ALCdevice
*device
, const AmbDecConf
*conf
, ALsizei (&speakermap
)[MAX_OUTPUT_CHANNELS
])
142 auto map_spkr
= [device
](const AmbDecConf::SpeakerConf
&speaker
) -> ALsizei
144 /* NOTE: AmbDec does not define any standard speaker names, however
145 * for this to work we have to by able to find the output channel
146 * the speaker definition corresponds to. Therefore, OpenAL Soft
147 * requires these channel labels to be recognized:
158 * Additionally, surround51 will acknowledge back speakers for side
159 * channels, and surround51rear will acknowledge side speakers for
160 * back channels, to avoid issues with an ambdec expecting 5.1 to
161 * use the side channels when the device is configured for back,
165 if(speaker
.Name
== "LF")
167 else if(speaker
.Name
== "RF")
169 else if(speaker
.Name
== "CE")
171 else if(speaker
.Name
== "LS")
173 if(device
->FmtChans
== DevFmtX51Rear
)
178 else if(speaker
.Name
== "RS")
180 if(device
->FmtChans
== DevFmtX51Rear
)
185 else if(speaker
.Name
== "LB")
187 if(device
->FmtChans
== DevFmtX51
)
192 else if(speaker
.Name
== "RB")
194 if(device
->FmtChans
== DevFmtX51
)
199 else if(speaker
.Name
== "CB")
203 const char *name
{speaker
.Name
.c_str()};
207 if(sscanf(name
, "AUX%u%c", &n
, &c
) == 1 && n
< 16)
208 ch
= static_cast<Channel
>(Aux0
+n
);
211 ERR("AmbDec speaker label \"%s\" not recognized\n", name
);
215 const int chidx
{GetChannelIdxByName(&device
->RealOut
, ch
)};
217 ERR("Failed to lookup AmbDec speaker label %s\n", speaker
.Name
.c_str());
220 std::transform(conf
->Speakers
.begin(), conf
->Speakers
.end(), std::begin(speakermap
), map_spkr
);
221 /* Return success if no invalid entries are found. */
222 auto speakermap_end
= std::begin(speakermap
) + conf
->Speakers
.size();
223 return std::find(std::begin(speakermap
), speakermap_end
, -1) == speakermap_end
;
227 constexpr ChannelMap MonoCfg
[1] = {
228 { FrontCenter
, { 1.0f
} },
230 { FrontLeft
, { 5.00000000e-1f
, 2.88675135e-1f
, 0.0f
, 5.52305643e-2f
} },
231 { FrontRight
, { 5.00000000e-1f
, -2.88675135e-1f
, 0.0f
, 5.52305643e-2f
} },
233 { BackLeft
, { 3.53553391e-1f
, 2.04124145e-1f
, 0.0f
, -2.04124145e-1f
} },
234 { FrontLeft
, { 3.53553391e-1f
, 2.04124145e-1f
, 0.0f
, 2.04124145e-1f
} },
235 { FrontRight
, { 3.53553391e-1f
, -2.04124145e-1f
, 0.0f
, 2.04124145e-1f
} },
236 { BackRight
, { 3.53553391e-1f
, -2.04124145e-1f
, 0.0f
, -2.04124145e-1f
} },
238 { SideLeft
, { 3.33000782e-1f
, 1.89084803e-1f
, 0.0f
, -2.00042375e-1f
, -2.12307769e-2f
, 0.0f
, 0.0f
, 0.0f
, -1.14579885e-2f
} },
239 { FrontLeft
, { 1.88542860e-1f
, 1.27709292e-1f
, 0.0f
, 1.66295695e-1f
, 7.30571517e-2f
, 0.0f
, 0.0f
, 0.0f
, 2.10901184e-2f
} },
240 { FrontRight
, { 1.88542860e-1f
, -1.27709292e-1f
, 0.0f
, 1.66295695e-1f
, -7.30571517e-2f
, 0.0f
, 0.0f
, 0.0f
, 2.10901184e-2f
} },
241 { SideRight
, { 3.33000782e-1f
, -1.89084803e-1f
, 0.0f
, -2.00042375e-1f
, 2.12307769e-2f
, 0.0f
, 0.0f
, 0.0f
, -1.14579885e-2f
} },
243 { BackLeft
, { 3.33000782e-1f
, 1.89084803e-1f
, 0.0f
, -2.00042375e-1f
, -2.12307769e-2f
, 0.0f
, 0.0f
, 0.0f
, -1.14579885e-2f
} },
244 { FrontLeft
, { 1.88542860e-1f
, 1.27709292e-1f
, 0.0f
, 1.66295695e-1f
, 7.30571517e-2f
, 0.0f
, 0.0f
, 0.0f
, 2.10901184e-2f
} },
245 { FrontRight
, { 1.88542860e-1f
, -1.27709292e-1f
, 0.0f
, 1.66295695e-1f
, -7.30571517e-2f
, 0.0f
, 0.0f
, 0.0f
, 2.10901184e-2f
} },
246 { BackRight
, { 3.33000782e-1f
, -1.89084803e-1f
, 0.0f
, -2.00042375e-1f
, 2.12307769e-2f
, 0.0f
, 0.0f
, 0.0f
, -1.14579885e-2f
} },
248 { SideLeft
, { 2.04460341e-1f
, 2.17177926e-1f
, 0.0f
, -4.39996780e-2f
, -2.60790269e-2f
, 0.0f
, 0.0f
, 0.0f
, -6.87239792e-2f
} },
249 { FrontLeft
, { 1.58923161e-1f
, 9.21772680e-2f
, 0.0f
, 1.59658796e-1f
, 6.66278083e-2f
, 0.0f
, 0.0f
, 0.0f
, 3.84686854e-2f
} },
250 { FrontRight
, { 1.58923161e-1f
, -9.21772680e-2f
, 0.0f
, 1.59658796e-1f
, -6.66278083e-2f
, 0.0f
, 0.0f
, 0.0f
, 3.84686854e-2f
} },
251 { SideRight
, { 2.04460341e-1f
, -2.17177926e-1f
, 0.0f
, -4.39996780e-2f
, 2.60790269e-2f
, 0.0f
, 0.0f
, 0.0f
, -6.87239792e-2f
} },
252 { BackCenter
, { 2.50001688e-1f
, 0.00000000e+0f
, 0.0f
, -2.50000094e-1f
, 0.00000000e+0f
, 0.0f
, 0.0f
, 0.0f
, 6.05133395e-2f
} },
254 { BackLeft
, { 2.04124145e-1f
, 1.08880247e-1f
, 0.0f
, -1.88586120e-1f
, -1.29099444e-1f
, 0.0f
, 0.0f
, 0.0f
, 7.45355993e-2f
, 3.73460789e-2f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.00000000e+0f
} },
255 { SideLeft
, { 2.04124145e-1f
, 2.17760495e-1f
, 0.0f
, 0.00000000e+0f
, 0.00000000e+0f
, 0.0f
, 0.0f
, 0.0f
, -1.49071198e-1f
, -3.73460789e-2f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.00000000e+0f
} },
256 { FrontLeft
, { 2.04124145e-1f
, 1.08880247e-1f
, 0.0f
, 1.88586120e-1f
, 1.29099444e-1f
, 0.0f
, 0.0f
, 0.0f
, 7.45355993e-2f
, 3.73460789e-2f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.00000000e+0f
} },
257 { FrontRight
, { 2.04124145e-1f
, -1.08880247e-1f
, 0.0f
, 1.88586120e-1f
, -1.29099444e-1f
, 0.0f
, 0.0f
, 0.0f
, 7.45355993e-2f
, -3.73460789e-2f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.00000000e+0f
} },
258 { SideRight
, { 2.04124145e-1f
, -2.17760495e-1f
, 0.0f
, 0.00000000e+0f
, 0.00000000e+0f
, 0.0f
, 0.0f
, 0.0f
, -1.49071198e-1f
, 3.73460789e-2f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.00000000e+0f
} },
259 { BackRight
, { 2.04124145e-1f
, -1.08880247e-1f
, 0.0f
, -1.88586120e-1f
, 1.29099444e-1f
, 0.0f
, 0.0f
, 0.0f
, 7.45355993e-2f
, -3.73460789e-2f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.00000000e+0f
} },
262 void InitNearFieldCtrl(ALCdevice
*device
, ALfloat ctrl_dist
, ALsizei order
, const ALsizei
*RESTRICT chans_per_order
)
264 /* NFC is only used when AvgSpeakerDist is greater than 0, and can only be
265 * used when rendering to an ambisonic buffer.
267 const char *devname
{device
->DeviceName
.c_str()};
268 if(!GetConfigValueBool(devname
, "decoder", "nfc", 1) || !(ctrl_dist
> 0.0f
))
271 device
->AvgSpeakerDist
= minf(ctrl_dist
, 10.0f
);
272 TRACE("Using near-field reference distance: %.2f meters\n", device
->AvgSpeakerDist
);
274 auto iter
= std::copy(chans_per_order
, chans_per_order
+order
+1,
275 std::begin(device
->NumChannelsPerOrder
));
276 std::fill(iter
, std::end(device
->NumChannelsPerOrder
), 0);
279 void InitDistanceComp(ALCdevice
*device
, const AmbDecConf
*conf
, const ALsizei (&speakermap
)[MAX_OUTPUT_CHANNELS
])
281 using namespace std::placeholders
;
283 const ALfloat maxdist
{
284 std::accumulate(conf
->Speakers
.begin(), conf
->Speakers
.end(), float{0.0f
},
285 std::bind(maxf
, _1
, std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance
), _2
))
289 const char *devname
{device
->DeviceName
.c_str()};
290 if(!GetConfigValueBool(devname
, "decoder", "distance-comp", 1) || !(maxdist
> 0.0f
))
293 auto srate
= static_cast<ALfloat
>(device
->Frequency
);
295 for(size_t i
{0u};i
< conf
->Speakers
.size();i
++)
297 const AmbDecConf::SpeakerConf
&speaker
= conf
->Speakers
[i
];
298 const ALsizei chan
{speakermap
[i
]};
300 /* Distance compensation only delays in steps of the sample rate. This
301 * is a bit less accurate since the delay time falls to the nearest
302 * sample time, but it's far simpler as it doesn't have to deal with
303 * phase offsets. This means at 48khz, for instance, the distance delay
304 * will be in steps of about 7 millimeters.
307 std::floor((maxdist
- speaker
.Distance
)/SPEEDOFSOUNDMETRESPERSEC
*srate
+ 0.5f
)
309 if(delay
>= (ALfloat
)MAX_DELAY_LENGTH
)
310 ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %d)\n",
311 speaker
.Name
.c_str(), delay
, MAX_DELAY_LENGTH
);
313 device
->ChannelDelay
[chan
].Length
= static_cast<ALsizei
>(clampf(
314 delay
, 0.0f
, (ALfloat
)(MAX_DELAY_LENGTH
-1)
316 device
->ChannelDelay
[chan
].Gain
= speaker
.Distance
/ maxdist
;
317 TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan
,
318 speaker
.Name
.c_str(), device
->ChannelDelay
[chan
].Length
,
319 device
->ChannelDelay
[chan
].Gain
322 /* Round up to the next 4th sample, so each channel buffer starts
325 total
+= RoundUp(device
->ChannelDelay
[chan
].Length
, 4);
330 device
->ChannelDelay
.resize(total
);
331 device
->ChannelDelay
[0].Buffer
= device
->ChannelDelay
.data();
332 auto set_bufptr
= [](const DistanceComp::DistData
&last
, const DistanceComp::DistData
&cur
) -> DistanceComp::DistData
334 DistanceComp::DistData ret
{cur
};
335 ret
.Buffer
= last
.Buffer
+ RoundUp(last
.Length
, 4);
338 std::partial_sum(device
->ChannelDelay
.begin(), device
->ChannelDelay
.end(),
339 device
->ChannelDelay
.begin(), set_bufptr
);
343 auto GetAmbiScales(AmbDecScale scaletype
) noexcept
-> const float(&)[MAX_AMBI_COEFFS
]
345 if(scaletype
== AmbDecScale::FuMa
) return AmbiScale::FuMa2N3D
;
346 if(scaletype
== AmbDecScale::SN3D
) return AmbiScale::SN3D2N3D
;
347 return AmbiScale::N3D2N3D
;
350 auto GetAmbiScales(AmbiNorm scaletype
) noexcept
-> const float(&)[MAX_AMBI_COEFFS
]
352 if(scaletype
== AmbiNorm::FuMa
) return AmbiScale::FuMa2N3D
;
353 if(scaletype
== AmbiNorm::SN3D
) return AmbiScale::SN3D2N3D
;
354 return AmbiScale::N3D2N3D
;
357 auto GetAmbiLayout(AmbiLayout layouttype
) noexcept
-> const ALsizei(&)[MAX_AMBI_COEFFS
]
359 if(layouttype
== AmbiLayout::FuMa
) return FuMa2ACN
;
364 void InitPanning(ALCdevice
*device
)
366 const ChannelMap
*chanmap
{nullptr};
367 ALsizei coeffcount
{0};
370 switch(device
->FmtChans
)
373 count
= static_cast<ALsizei
>(COUNTOF(MonoCfg
));
379 count
= static_cast<ALsizei
>(COUNTOF(StereoCfg
));
385 count
= static_cast<ALsizei
>(COUNTOF(QuadCfg
));
391 count
= static_cast<ALsizei
>(COUNTOF(X51SideCfg
));
392 chanmap
= X51SideCfg
;
397 count
= static_cast<ALsizei
>(COUNTOF(X51RearCfg
));
398 chanmap
= X51RearCfg
;
403 count
= static_cast<ALsizei
>(COUNTOF(X61Cfg
));
409 count
= static_cast<ALsizei
>(COUNTOF(X71Cfg
));
418 if(device
->FmtChans
== DevFmtAmbi3D
)
420 const char *devname
{device
->DeviceName
.c_str()};
421 const ALsizei (&acnmap
)[MAX_AMBI_COEFFS
] = GetAmbiLayout(device
->mAmbiLayout
);
422 const ALfloat (&n3dscale
)[MAX_AMBI_COEFFS
] = GetAmbiScales(device
->mAmbiScale
);
424 count
= (device
->mAmbiOrder
== 3) ? 16 :
425 (device
->mAmbiOrder
== 2) ? 9 :
426 (device
->mAmbiOrder
== 1) ? 4 : 1;
427 auto acnmap_end
= std::begin(acnmap
) + count
;
428 std::transform(std::begin(acnmap
), acnmap_end
, std::begin(device
->Dry
.Ambi
.Map
),
429 [&n3dscale
](const ALsizei
&acn
) noexcept
-> BFChannelConfig
430 { return BFChannelConfig
{1.0f
/n3dscale
[acn
], acn
}; }
432 device
->Dry
.CoeffCount
= 0;
433 device
->Dry
.NumChannels
= count
;
435 if(device
->mAmbiOrder
< 2)
437 device
->FOAOut
.Ambi
= device
->Dry
.Ambi
;
438 device
->FOAOut
.CoeffCount
= device
->Dry
.CoeffCount
;
439 device
->FOAOut
.NumChannels
= 0;
443 /* FOA output is always ACN+N3D for higher-order ambisonic output.
444 * The upsampler expects this and will convert it for output.
446 device
->FOAOut
.Ambi
= AmbiConfig
{};
447 acnmap_end
= std::begin(ACN2ACN
) + 4;
448 std::transform(std::begin(ACN2ACN
), acnmap_end
, std::begin(device
->FOAOut
.Ambi
.Map
),
449 [](const ALsizei
&acn
) noexcept
{ return BFChannelConfig
{1.0f
, acn
}; }
451 device
->FOAOut
.CoeffCount
= 0;
452 device
->FOAOut
.NumChannels
= 4;
454 ALfloat w_scale
{1.0f
}, xyz_scale
{1.0f
};
455 if(device
->mAmbiOrder
>= 3)
457 w_scale
= W_SCALE_3H3P
;
458 xyz_scale
= XYZ_SCALE_3H3P
;
462 w_scale
= W_SCALE_2H2P
;
463 xyz_scale
= XYZ_SCALE_2H2P
;
465 device
->AmbiUp
->reset(device
, w_scale
, xyz_scale
);
468 ALfloat nfc_delay
{0.0f
};
469 if(ConfigValueFloat(devname
, "decoder", "nfc-ref-delay", &nfc_delay
) && nfc_delay
> 0.0f
)
471 static constexpr ALsizei chans_per_order
[MAX_AMBI_ORDER
+1]{ 1, 3, 5, 7 };
472 nfc_delay
= clampf(nfc_delay
, 0.001f
, 1000.0f
);
473 InitNearFieldCtrl(device
, nfc_delay
* SPEEDOFSOUNDMETRESPERSEC
,
474 device
->mAmbiOrder
, chans_per_order
);
479 SetChannelMap(device
->RealOut
.ChannelName
, device
->Dry
.Ambi
.Coeffs
,
480 chanmap
, count
, &device
->Dry
.NumChannels
);
481 device
->Dry
.CoeffCount
= coeffcount
;
483 const ALfloat w_scale
{(device
->Dry
.CoeffCount
> 9) ? W_SCALE_3H0P
:
484 (device
->Dry
.CoeffCount
> 4) ? W_SCALE_2H0P
: 1.0f
};
485 const ALfloat xyz_scale
{(device
->Dry
.CoeffCount
> 9) ? XYZ_SCALE_3H0P
:
486 (device
->Dry
.CoeffCount
> 4) ? XYZ_SCALE_2H0P
: 1.0f
};
488 device
->FOAOut
.Ambi
= AmbiConfig
{};
489 for(ALsizei i
{0};i
< device
->Dry
.NumChannels
;i
++)
491 device
->FOAOut
.Ambi
.Coeffs
[i
][0] = device
->Dry
.Ambi
.Coeffs
[i
][0] * w_scale
;
492 for(ALsizei j
{1};j
< 4;j
++)
493 device
->FOAOut
.Ambi
.Coeffs
[i
][j
] = device
->Dry
.Ambi
.Coeffs
[i
][j
] * xyz_scale
;
495 device
->FOAOut
.CoeffCount
= 4;
496 device
->FOAOut
.NumChannels
= 0;
498 device
->RealOut
.NumChannels
= 0;
501 void InitCustomPanning(ALCdevice
*device
, const AmbDecConf
*conf
, const ALsizei (&speakermap
)[MAX_OUTPUT_CHANNELS
])
503 if(conf
->FreqBands
!= 1)
504 ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
507 ALfloat w_scale
{1.0f
}, xyz_scale
{1.0f
};
508 if((conf
->ChanMask
&AMBI_PERIPHONIC_MASK
))
510 if(conf
->ChanMask
> AMBI_2ORDER_MASK
)
512 w_scale
= W_SCALE_3H3P
;
513 xyz_scale
= XYZ_SCALE_3H3P
;
515 else if(conf
->ChanMask
> AMBI_1ORDER_MASK
)
517 w_scale
= W_SCALE_2H2P
;
518 xyz_scale
= XYZ_SCALE_2H2P
;
523 if(conf
->ChanMask
> AMBI_2ORDER_MASK
)
525 w_scale
= W_SCALE_3H0P
;
526 xyz_scale
= XYZ_SCALE_3H0P
;
528 else if(conf
->ChanMask
> AMBI_1ORDER_MASK
)
530 w_scale
= W_SCALE_2H0P
;
531 xyz_scale
= XYZ_SCALE_2H0P
;
535 const ALfloat (&coeff_scale
)[MAX_AMBI_COEFFS
] = GetAmbiScales(conf
->CoeffScale
);
536 ChannelMap chanmap
[MAX_OUTPUT_CHANNELS
]{};
537 for(size_t i
{0u};i
< conf
->Speakers
.size();i
++)
539 chanmap
[i
].ChanName
= device
->RealOut
.ChannelName
[speakermap
[i
]];
540 std::fill(std::begin(chanmap
[i
].Config
), std::end(chanmap
[i
].Config
), 0.0f
);
542 for(ALsizei j
{0},k
{0};j
< MAX_AMBI_COEFFS
;j
++)
544 if(!(conf
->ChanMask
&(1<<j
)))
546 chanmap
[i
].Config
[j
] = conf
->HFMatrix
[i
][k
++] / coeff_scale
[j
] *
547 ((j
>= 9) ? conf
->HFOrderGain
[3] :
548 (j
>= 4) ? conf
->HFOrderGain
[2] :
549 (j
>= 1) ? conf
->HFOrderGain
[1] : conf
->HFOrderGain
[0]);
553 SetChannelMap(device
->RealOut
.ChannelName
, device
->Dry
.Ambi
.Coeffs
, chanmap
,
554 conf
->Speakers
.size(), &device
->Dry
.NumChannels
);
555 device
->Dry
.CoeffCount
= (conf
->ChanMask
> AMBI_2ORDER_MASK
) ? 16 :
556 (conf
->ChanMask
> AMBI_1ORDER_MASK
) ? 9 : 4;
558 device
->FOAOut
.Ambi
= AmbiConfig
{};
559 for(ALsizei i
{0};i
< device
->Dry
.NumChannels
;i
++)
561 device
->FOAOut
.Ambi
.Coeffs
[i
][0] = device
->Dry
.Ambi
.Coeffs
[i
][0] * w_scale
;
562 for(ALsizei j
{1};j
< 4;j
++)
563 device
->FOAOut
.Ambi
.Coeffs
[i
][j
] = device
->Dry
.Ambi
.Coeffs
[i
][j
] * xyz_scale
;
565 device
->FOAOut
.CoeffCount
= 4;
566 device
->FOAOut
.NumChannels
= 0;
568 device
->RealOut
.NumChannels
= 0;
570 InitDistanceComp(device
, conf
, speakermap
);
573 void InitHQPanning(ALCdevice
*device
, const AmbDecConf
*conf
, const ALsizei (&speakermap
)[MAX_OUTPUT_CHANNELS
])
575 static constexpr ALsizei chans_per_order2d
[MAX_AMBI_ORDER
+1] = { 1, 2, 2, 2 };
576 static constexpr ALsizei chans_per_order3d
[MAX_AMBI_ORDER
+1] = { 1, 3, 5, 7 };
579 if((conf
->ChanMask
&AMBI_PERIPHONIC_MASK
))
581 static constexpr int map
[MAX_AMBI_COEFFS
] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
582 count
= (conf
->ChanMask
> AMBI_2ORDER_MASK
) ? 16 :
583 (conf
->ChanMask
> AMBI_1ORDER_MASK
) ? 9 : 4;
584 std::transform(std::begin(map
), std::begin(map
)+count
, std::begin(device
->Dry
.Ambi
.Map
),
585 [](const ALsizei
&index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
590 static constexpr int map
[MAX_AMBI2D_COEFFS
] = { 0, 1, 3, 4, 8, 9, 15 };
591 count
= (conf
->ChanMask
> AMBI_2ORDER_MASK
) ? 7 :
592 (conf
->ChanMask
> AMBI_1ORDER_MASK
) ? 5 : 3;
593 std::transform(std::begin(map
), std::begin(map
)+count
, std::begin(device
->Dry
.Ambi
.Map
),
594 [](const ALsizei
&index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
597 device
->Dry
.CoeffCount
= 0;
598 device
->Dry
.NumChannels
= count
;
600 TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
601 (conf
->FreqBands
== 1) ? "single" : "dual",
602 (conf
->ChanMask
> AMBI_2ORDER_MASK
) ? "third" :
603 (conf
->ChanMask
> AMBI_1ORDER_MASK
) ? "second" : "first",
604 (conf
->ChanMask
&AMBI_PERIPHONIC_MASK
) ? " periphonic" : ""
606 device
->AmbiDecoder
->reset(conf
, count
, device
->Frequency
, speakermap
);
608 if(conf
->ChanMask
<= AMBI_1ORDER_MASK
)
610 device
->FOAOut
.Ambi
= device
->Dry
.Ambi
;
611 device
->FOAOut
.CoeffCount
= device
->Dry
.CoeffCount
;
612 device
->FOAOut
.NumChannels
= 0;
616 device
->FOAOut
.Ambi
= AmbiConfig
{};
617 if((conf
->ChanMask
&AMBI_PERIPHONIC_MASK
))
619 static constexpr int map
[4] = { 0, 1, 2, 3 };
621 std::transform(std::begin(map
), std::begin(map
)+count
, std::begin(device
->FOAOut
.Ambi
.Map
),
622 [](const ALsizei
&index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
627 static constexpr int map
[3] = { 0, 1, 3 };
629 std::transform(std::begin(map
), std::begin(map
)+count
, std::begin(device
->FOAOut
.Ambi
.Map
),
630 [](const ALsizei
&index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
633 device
->FOAOut
.CoeffCount
= 0;
634 device
->FOAOut
.NumChannels
= count
;
637 device
->RealOut
.NumChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
639 using namespace std::placeholders
;
640 auto accum_spkr_dist
= std::bind(
641 std::plus
<float>{}, _1
, std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance
), _2
)
643 const ALfloat avg_dist
{
644 std::accumulate(conf
->Speakers
.begin(), conf
->Speakers
.end(), float{0.0f
},
645 accum_spkr_dist
) / static_cast<ALfloat
>(conf
->Speakers
.size())
647 InitNearFieldCtrl(device
, avg_dist
,
648 (conf
->ChanMask
> AMBI_2ORDER_MASK
) ? 3 :
649 (conf
->ChanMask
> AMBI_1ORDER_MASK
) ? 2 : 1,
650 (conf
->ChanMask
&AMBI_PERIPHONIC_MASK
) ? chans_per_order3d
: chans_per_order2d
653 InitDistanceComp(device
, conf
, speakermap
);
656 void InitHrtfPanning(ALCdevice
*device
)
658 /* NOTE: azimuth goes clockwise. */
659 static constexpr AngularPoint AmbiPoints
[] = {
660 { DEG2RAD( 90.0f
), DEG2RAD( 0.0f
) },
661 { DEG2RAD( 35.2643897f
), DEG2RAD( 45.0f
) },
662 { DEG2RAD( 35.2643897f
), DEG2RAD( 135.0f
) },
663 { DEG2RAD( 35.2643897f
), DEG2RAD(-135.0f
) },
664 { DEG2RAD( 35.2643897f
), DEG2RAD( -45.0f
) },
665 { DEG2RAD( 0.0f
), DEG2RAD( 0.0f
) },
666 { DEG2RAD( 0.0f
), DEG2RAD( 45.0f
) },
667 { DEG2RAD( 0.0f
), DEG2RAD( 90.0f
) },
668 { DEG2RAD( 0.0f
), DEG2RAD( 135.0f
) },
669 { DEG2RAD( 0.0f
), DEG2RAD( 180.0f
) },
670 { DEG2RAD( 0.0f
), DEG2RAD(-135.0f
) },
671 { DEG2RAD( 0.0f
), DEG2RAD( -90.0f
) },
672 { DEG2RAD( 0.0f
), DEG2RAD( -45.0f
) },
673 { DEG2RAD(-35.2643897f
), DEG2RAD( 45.0f
) },
674 { DEG2RAD(-35.2643897f
), DEG2RAD( 135.0f
) },
675 { DEG2RAD(-35.2643897f
), DEG2RAD(-135.0f
) },
676 { DEG2RAD(-35.2643897f
), DEG2RAD( -45.0f
) },
677 { DEG2RAD(-90.0f
), DEG2RAD( 0.0f
) },
679 static constexpr ALfloat AmbiMatrixFOA
[][MAX_AMBI_COEFFS
] = {
680 { 5.55555556e-02f
, 0.00000000e+00f
, 1.23717915e-01f
, 0.00000000e+00f
},
681 { 5.55555556e-02f
, -5.00000000e-02f
, 7.14285715e-02f
, 5.00000000e-02f
},
682 { 5.55555556e-02f
, -5.00000000e-02f
, 7.14285715e-02f
, -5.00000000e-02f
},
683 { 5.55555556e-02f
, 5.00000000e-02f
, 7.14285715e-02f
, -5.00000000e-02f
},
684 { 5.55555556e-02f
, 5.00000000e-02f
, 7.14285715e-02f
, 5.00000000e-02f
},
685 { 5.55555556e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 8.66025404e-02f
},
686 { 5.55555556e-02f
, -6.12372435e-02f
, 0.00000000e+00f
, 6.12372435e-02f
},
687 { 5.55555556e-02f
, -8.66025404e-02f
, 0.00000000e+00f
, 0.00000000e+00f
},
688 { 5.55555556e-02f
, -6.12372435e-02f
, 0.00000000e+00f
, -6.12372435e-02f
},
689 { 5.55555556e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, -8.66025404e-02f
},
690 { 5.55555556e-02f
, 6.12372435e-02f
, 0.00000000e+00f
, -6.12372435e-02f
},
691 { 5.55555556e-02f
, 8.66025404e-02f
, 0.00000000e+00f
, 0.00000000e+00f
},
692 { 5.55555556e-02f
, 6.12372435e-02f
, 0.00000000e+00f
, 6.12372435e-02f
},
693 { 5.55555556e-02f
, -5.00000000e-02f
, -7.14285715e-02f
, 5.00000000e-02f
},
694 { 5.55555556e-02f
, -5.00000000e-02f
, -7.14285715e-02f
, -5.00000000e-02f
},
695 { 5.55555556e-02f
, 5.00000000e-02f
, -7.14285715e-02f
, -5.00000000e-02f
},
696 { 5.55555556e-02f
, 5.00000000e-02f
, -7.14285715e-02f
, 5.00000000e-02f
},
697 { 5.55555556e-02f
, 0.00000000e+00f
, -1.23717915e-01f
, 0.00000000e+00f
},
698 }, AmbiMatrixHOA
[][MAX_AMBI_COEFFS
] = {
699 { 5.55555556e-02f
, 0.00000000e+00f
, 1.23717915e-01f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
},
700 { 5.55555556e-02f
, -5.00000000e-02f
, 7.14285715e-02f
, 5.00000000e-02f
, -4.55645099e-02f
, 0.00000000e+00f
},
701 { 5.55555556e-02f
, -5.00000000e-02f
, 7.14285715e-02f
, -5.00000000e-02f
, 4.55645099e-02f
, 0.00000000e+00f
},
702 { 5.55555556e-02f
, 5.00000000e-02f
, 7.14285715e-02f
, -5.00000000e-02f
, -4.55645099e-02f
, 0.00000000e+00f
},
703 { 5.55555556e-02f
, 5.00000000e-02f
, 7.14285715e-02f
, 5.00000000e-02f
, 4.55645099e-02f
, 0.00000000e+00f
},
704 { 5.55555556e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 8.66025404e-02f
, 0.00000000e+00f
, 1.29099445e-01f
},
705 { 5.55555556e-02f
, -6.12372435e-02f
, 0.00000000e+00f
, 6.12372435e-02f
, -6.83467648e-02f
, 0.00000000e+00f
},
706 { 5.55555556e-02f
, -8.66025404e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, -1.29099445e-01f
},
707 { 5.55555556e-02f
, -6.12372435e-02f
, 0.00000000e+00f
, -6.12372435e-02f
, 6.83467648e-02f
, 0.00000000e+00f
},
708 { 5.55555556e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, -8.66025404e-02f
, 0.00000000e+00f
, 1.29099445e-01f
},
709 { 5.55555556e-02f
, 6.12372435e-02f
, 0.00000000e+00f
, -6.12372435e-02f
, -6.83467648e-02f
, 0.00000000e+00f
},
710 { 5.55555556e-02f
, 8.66025404e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, -1.29099445e-01f
},
711 { 5.55555556e-02f
, 6.12372435e-02f
, 0.00000000e+00f
, 6.12372435e-02f
, 6.83467648e-02f
, 0.00000000e+00f
},
712 { 5.55555556e-02f
, -5.00000000e-02f
, -7.14285715e-02f
, 5.00000000e-02f
, -4.55645099e-02f
, 0.00000000e+00f
},
713 { 5.55555556e-02f
, -5.00000000e-02f
, -7.14285715e-02f
, -5.00000000e-02f
, 4.55645099e-02f
, 0.00000000e+00f
},
714 { 5.55555556e-02f
, 5.00000000e-02f
, -7.14285715e-02f
, -5.00000000e-02f
, -4.55645099e-02f
, 0.00000000e+00f
},
715 { 5.55555556e-02f
, 5.00000000e-02f
, -7.14285715e-02f
, 5.00000000e-02f
, 4.55645099e-02f
, 0.00000000e+00f
},
716 { 5.55555556e-02f
, 0.00000000e+00f
, -1.23717915e-01f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
},
718 static constexpr ALfloat AmbiOrderHFGainFOA
[MAX_AMBI_ORDER
+1] = {
719 3.00000000e+00f
, 1.73205081e+00f
720 }, AmbiOrderHFGainHOA
[MAX_AMBI_ORDER
+1] = {
721 2.40192231e+00f
, 1.86052102e+00f
, 9.60768923e-01f
723 static constexpr ALsizei IndexMap
[6] = { 0, 1, 2, 3, 4, 8 };
724 static constexpr ALsizei ChansPerOrder
[MAX_AMBI_ORDER
+1] = { 1, 3, 2, 0 };
725 const ALfloat (*RESTRICT AmbiMatrix
)[MAX_AMBI_COEFFS
] = AmbiMatrixFOA
;
726 const ALfloat
*RESTRICT AmbiOrderHFGain
= AmbiOrderHFGainFOA
;
729 static_assert(COUNTOF(AmbiPoints
) == COUNTOF(AmbiMatrixFOA
), "FOA Ambisonic HRTF mismatch");
730 static_assert(COUNTOF(AmbiPoints
) == COUNTOF(AmbiMatrixHOA
), "HOA Ambisonic HRTF mismatch");
734 AmbiMatrix
= AmbiMatrixHOA
;
735 AmbiOrderHFGain
= AmbiOrderHFGainHOA
;
736 count
= static_cast<ALsizei
>(COUNTOF(IndexMap
));
739 device
->mHrtfState
.reset(
740 new (al_calloc(16, FAM_SIZE(DirectHrtfState
, Chan
, count
))) DirectHrtfState
{});
742 std::transform(std::begin(IndexMap
), std::begin(IndexMap
)+count
, std::begin(device
->Dry
.Ambi
.Map
),
743 [](const ALsizei
&index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
745 device
->Dry
.CoeffCount
= 0;
746 device
->Dry
.NumChannels
= count
;
750 device
->FOAOut
.Ambi
= AmbiConfig
{};
751 std::transform(std::begin(IndexMap
), std::begin(IndexMap
)+4, std::begin(device
->FOAOut
.Ambi
.Map
),
752 [](const ALsizei
&index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
754 device
->FOAOut
.CoeffCount
= 0;
755 device
->FOAOut
.NumChannels
= 4;
757 device
->AmbiUp
->reset(device
, AmbiOrderHFGainFOA
[0] / AmbiOrderHFGain
[0],
758 AmbiOrderHFGainFOA
[1] / AmbiOrderHFGain
[1]);
762 device
->FOAOut
.Ambi
= device
->Dry
.Ambi
;
763 device
->FOAOut
.CoeffCount
= device
->Dry
.CoeffCount
;
764 device
->FOAOut
.NumChannels
= 0;
767 device
->RealOut
.NumChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
769 BuildBFormatHrtf(device
->HrtfHandle
,
770 device
->mHrtfState
.get(), device
->Dry
.NumChannels
, AmbiPoints
, AmbiMatrix
,
771 static_cast<ALsizei
>(COUNTOF(AmbiPoints
)), AmbiOrderHFGain
774 InitNearFieldCtrl(device
, device
->HrtfHandle
->distance
, device
->AmbiUp
? 2 : 1,
778 void InitUhjPanning(ALCdevice
*device
)
780 static constexpr ALsizei count
{3};
782 std::transform(std::begin(FuMa2ACN
), std::begin(FuMa2ACN
)+count
, std::begin(device
->Dry
.Ambi
.Map
),
783 [](const ALsizei
&acn
) noexcept
-> BFChannelConfig
784 { return BFChannelConfig
{1.0f
/AmbiScale::FuMa2N3D
[acn
], acn
}; }
786 device
->Dry
.CoeffCount
= 0;
787 device
->Dry
.NumChannels
= count
;
789 device
->FOAOut
.Ambi
= device
->Dry
.Ambi
;
790 device
->FOAOut
.CoeffCount
= device
->Dry
.CoeffCount
;
791 device
->FOAOut
.NumChannels
= 0;
793 device
->RealOut
.NumChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
799 void CalcAmbiCoeffs(const ALfloat y
, const ALfloat z
, const ALfloat x
, const ALfloat spread
,
800 ALfloat (&coeffs
)[MAX_AMBI_COEFFS
])
803 coeffs
[0] = 1.0f
; /* ACN 0 = 1 */
805 coeffs
[1] = SQRTF_3
* y
; /* ACN 1 = sqrt(3) * Y */
806 coeffs
[2] = SQRTF_3
* z
; /* ACN 2 = sqrt(3) * Z */
807 coeffs
[3] = SQRTF_3
* x
; /* ACN 3 = sqrt(3) * X */
809 coeffs
[4] = 3.872983346f
* x
* y
; /* ACN 4 = sqrt(15) * X * Y */
810 coeffs
[5] = 3.872983346f
* y
* z
; /* ACN 5 = sqrt(15) * Y * Z */
811 coeffs
[6] = 1.118033989f
* (3.0f
*z
*z
- 1.0f
); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
812 coeffs
[7] = 3.872983346f
* x
* z
; /* ACN 7 = sqrt(15) * X * Z */
813 coeffs
[8] = 1.936491673f
* (x
*x
- y
*y
); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
815 coeffs
[9] = 2.091650066f
* y
* (3.0f
*x
*x
- y
*y
); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
816 coeffs
[10] = 10.246950766f
* z
* x
* y
; /* ACN 10 = sqrt(105) * Z * X * Y */
817 coeffs
[11] = 1.620185175f
* y
* (5.0f
*z
*z
- 1.0f
); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
818 coeffs
[12] = 1.322875656f
* z
* (5.0f
*z
*z
- 3.0f
); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
819 coeffs
[13] = 1.620185175f
* x
* (5.0f
*z
*z
- 1.0f
); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
820 coeffs
[14] = 5.123475383f
* z
* (x
*x
- y
*y
); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
821 coeffs
[15] = 2.091650066f
* x
* (x
*x
- 3.0f
*y
*y
); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
823 /* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */
824 /* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */
825 /* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */
826 /* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */
827 /* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */
828 /* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */
829 /* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */
830 /* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */
831 /* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */
835 /* Implement the spread by using a spherical source that subtends the
837 * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
839 * When adjusted for N3D normalization instead of SN3D, these
842 * ZH0 = -sqrt(pi) * (-1+ca);
843 * ZH1 = 0.5*sqrt(pi) * sa*sa;
844 * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
845 * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
846 * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
847 * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
849 * The gain of the source is compensated for size, so that the
850 * loudness doesn't depend on the spread. Thus:
853 * ZH1 = 0.5f * (ca+1.0f);
854 * ZH2 = 0.5f * (ca+1.0f)*ca;
855 * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
856 * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
857 * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
859 ALfloat ca
= std::cos(spread
* 0.5f
);
860 /* Increase the source volume by up to +3dB for a full spread. */
861 ALfloat scale
= std::sqrt(1.0f
+ spread
/F_TAU
);
863 ALfloat ZH0_norm
= scale
;
864 ALfloat ZH1_norm
= 0.5f
* (ca
+1.f
) * scale
;
865 ALfloat ZH2_norm
= 0.5f
* (ca
+1.f
)*ca
* scale
;
866 ALfloat ZH3_norm
= 0.125f
* (ca
+1.f
)*(5.f
*ca
*ca
-1.f
) * scale
;
869 coeffs
[0] *= ZH0_norm
;
871 coeffs
[1] *= ZH1_norm
;
872 coeffs
[2] *= ZH1_norm
;
873 coeffs
[3] *= ZH1_norm
;
875 coeffs
[4] *= ZH2_norm
;
876 coeffs
[5] *= ZH2_norm
;
877 coeffs
[6] *= ZH2_norm
;
878 coeffs
[7] *= ZH2_norm
;
879 coeffs
[8] *= ZH2_norm
;
881 coeffs
[9] *= ZH3_norm
;
882 coeffs
[10] *= ZH3_norm
;
883 coeffs
[11] *= ZH3_norm
;
884 coeffs
[12] *= ZH3_norm
;
885 coeffs
[13] *= ZH3_norm
;
886 coeffs
[14] *= ZH3_norm
;
887 coeffs
[15] *= ZH3_norm
;
892 void ComputePanningGainsMC(const ChannelConfig
*chancoeffs
, ALsizei numchans
, ALsizei numcoeffs
, const ALfloat
*RESTRICT coeffs
, ALfloat ingain
, ALfloat (&gains
)[MAX_OUTPUT_CHANNELS
])
894 ASSUME(numchans
> 0);
895 auto iter
= std::transform(chancoeffs
, chancoeffs
+numchans
, std::begin(gains
),
896 [numcoeffs
,coeffs
,ingain
](const ChannelConfig
&chancoeffs
) -> ALfloat
898 ASSUME(numcoeffs
> 0);
899 float gain
{std::inner_product(std::begin(chancoeffs
), std::begin(chancoeffs
)+numcoeffs
,
900 coeffs
, float{0.0f
})};
901 return clampf(gain
, 0.0f
, 1.0f
) * ingain
;
904 std::fill(iter
, std::end(gains
), 0.0f
);
907 void ComputePanningGainsBF(const BFChannelConfig
*chanmap
, ALsizei numchans
, const ALfloat
*RESTRICT coeffs
, ALfloat ingain
, ALfloat (&gains
)[MAX_OUTPUT_CHANNELS
])
909 ASSUME(numchans
> 0);
910 auto iter
= std::transform(chanmap
, chanmap
+numchans
, std::begin(gains
),
911 [coeffs
,ingain
](const BFChannelConfig
&chanmap
) noexcept
-> ALfloat
913 ASSUME(chanmap
.Index
>= 0);
914 return chanmap
.Scale
* coeffs
[chanmap
.Index
] * ingain
;
917 std::fill(iter
, std::end(gains
), 0.0f
);
921 void aluInitRenderer(ALCdevice
*device
, ALint hrtf_id
, HrtfRequestMode hrtf_appreq
, HrtfRequestMode hrtf_userreq
)
923 /* Hold the HRTF the device last used, in case it's used again. */
924 Hrtf
*old_hrtf
{device
->HrtfHandle
};
926 device
->mHrtfState
= nullptr;
927 device
->HrtfHandle
= nullptr;
928 device
->HrtfName
.clear();
929 device
->Render_Mode
= NormalRender
;
931 device
->Dry
.Ambi
= AmbiConfig
{};
932 device
->Dry
.CoeffCount
= 0;
933 device
->Dry
.NumChannels
= 0;
934 std::fill(std::begin(device
->NumChannelsPerOrder
), std::end(device
->NumChannelsPerOrder
), 0);
936 device
->AvgSpeakerDist
= 0.0f
;
937 device
->ChannelDelay
.clear();
939 device
->Stablizer
= nullptr;
941 if(device
->FmtChans
!= DevFmtStereo
)
944 Hrtf_DecRef(old_hrtf
);
946 if(hrtf_appreq
== Hrtf_Enable
)
947 device
->HrtfStatus
= ALC_HRTF_UNSUPPORTED_FORMAT_SOFT
;
949 const char *layout
{nullptr};
950 switch(device
->FmtChans
)
952 case DevFmtQuad
: layout
= "quad"; break;
953 case DevFmtX51
: /* fall-through */
954 case DevFmtX51Rear
: layout
= "surround51"; break;
955 case DevFmtX61
: layout
= "surround61"; break;
956 case DevFmtX71
: layout
= "surround71"; break;
957 /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
964 const char *devname
{device
->DeviceName
.c_str()};
965 ALsizei speakermap
[MAX_OUTPUT_CHANNELS
];
966 AmbDecConf
*pconf
{nullptr};
971 if(ConfigValueStr(devname
, "decoder", layout
, &fname
))
973 if(!conf
.load(fname
))
974 ERR("Failed to load layout file %s\n", fname
);
975 else if(conf
.Speakers
.size() > MAX_OUTPUT_CHANNELS
)
976 ERR("Unsupported speaker count " SZFMT
" (max %d)\n", conf
.Speakers
.size(),
977 MAX_OUTPUT_CHANNELS
);
978 else if(conf
.ChanMask
> AMBI_3ORDER_MASK
)
979 ERR("Unsupported channel mask 0x%04x (max 0x%x)\n", conf
.ChanMask
,
981 else if(MakeSpeakerMap(device
, &conf
, speakermap
))
986 if(pconf
&& GetConfigValueBool(devname
, "decoder", "hq-mode", 0))
988 device
->AmbiUp
= nullptr;
989 if(!device
->AmbiDecoder
)
990 device
->AmbiDecoder
.reset(new BFormatDec
{});
994 device
->AmbiDecoder
= nullptr;
995 if(device
->FmtChans
!= DevFmtAmbi3D
|| device
->mAmbiOrder
< 2)
996 device
->AmbiUp
= nullptr;
997 else if(!device
->AmbiUp
)
998 device
->AmbiUp
.reset(new AmbiUpsampler
{});
1002 InitPanning(device
);
1003 else if(device
->AmbiDecoder
)
1004 InitHQPanning(device
, pconf
, speakermap
);
1006 InitCustomPanning(device
, pconf
, speakermap
);
1008 /* Enable the stablizer only for formats that have front-left, front-
1009 * right, and front-center outputs.
1011 switch(device
->FmtChans
)
1017 if(GetConfigValueBool(devname
, nullptr, "front-stablizer", 0))
1019 /* Initialize band-splitting filters for the front-left and
1020 * front-right channels, with a crossover at 5khz (could be
1023 ALfloat scale
= (ALfloat
)(5000.0 / device
->Frequency
);
1024 std::unique_ptr
<FrontStablizer
> stablizer
{new FrontStablizer
{}};
1026 stablizer
->LFilter
.init(scale
);
1027 stablizer
->RFilter
= stablizer
->LFilter
;
1029 /* Initialize all-pass filters for all other channels. */
1030 stablizer
->APFilter
[0].init(scale
);
1031 std::fill(std::begin(stablizer
->APFilter
)+1, std::end(stablizer
->APFilter
),
1032 stablizer
->APFilter
[0]);
1034 device
->Stablizer
= std::move(stablizer
);
1043 TRACE("Front stablizer %s\n", device
->Stablizer
? "enabled" : "disabled");
1048 device
->AmbiDecoder
= nullptr;
1050 bool headphones
{device
->IsHeadphones
!= AL_FALSE
};
1051 if(device
->Type
!= Loopback
)
1054 if(ConfigValueStr(device
->DeviceName
.c_str(), nullptr, "stereo-mode", &mode
))
1056 if(strcasecmp(mode
, "headphones") == 0)
1058 else if(strcasecmp(mode
, "speakers") == 0)
1060 else if(strcasecmp(mode
, "auto") != 0)
1061 ERR("Unexpected stereo-mode: %s\n", mode
);
1065 if(hrtf_userreq
== Hrtf_Default
)
1067 bool usehrtf
= (headphones
&& hrtf_appreq
!= Hrtf_Disable
) ||
1068 (hrtf_appreq
== Hrtf_Enable
);
1069 if(!usehrtf
) goto no_hrtf
;
1071 device
->HrtfStatus
= ALC_HRTF_ENABLED_SOFT
;
1072 if(headphones
&& hrtf_appreq
!= Hrtf_Disable
)
1073 device
->HrtfStatus
= ALC_HRTF_HEADPHONES_DETECTED_SOFT
;
1077 if(hrtf_userreq
!= Hrtf_Enable
)
1079 if(hrtf_appreq
== Hrtf_Enable
)
1080 device
->HrtfStatus
= ALC_HRTF_DENIED_SOFT
;
1083 device
->HrtfStatus
= ALC_HRTF_REQUIRED_SOFT
;
1086 if(device
->HrtfList
.empty())
1087 device
->HrtfList
= EnumerateHrtf(device
->DeviceName
.c_str());
1089 if(hrtf_id
>= 0 && (size_t)hrtf_id
< device
->HrtfList
.size())
1091 const EnumeratedHrtf
&entry
= device
->HrtfList
[hrtf_id
];
1092 Hrtf
*hrtf
{GetLoadedHrtf(entry
.hrtf
)};
1093 if(hrtf
&& hrtf
->sampleRate
== device
->Frequency
)
1095 device
->HrtfHandle
= hrtf
;
1096 device
->HrtfName
= entry
.name
;
1102 if(!device
->HrtfHandle
)
1104 auto find_hrtf
= [device
](const EnumeratedHrtf
&entry
) -> bool
1106 Hrtf
*hrtf
{GetLoadedHrtf(entry
.hrtf
)};
1107 if(!hrtf
) return false;
1108 if(hrtf
->sampleRate
!= device
->Frequency
)
1113 device
->HrtfHandle
= hrtf
;
1114 device
->HrtfName
= entry
.name
;
1117 std::find_if(device
->HrtfList
.cbegin(), device
->HrtfList
.cend(), find_hrtf
);
1120 if(device
->HrtfHandle
)
1123 Hrtf_DecRef(old_hrtf
);
1126 device
->Render_Mode
= HrtfRender
;
1128 if(ConfigValueStr(device
->DeviceName
.c_str(), nullptr, "hrtf-mode", &mode
))
1130 if(strcasecmp(mode
, "full") == 0)
1131 device
->Render_Mode
= HrtfRender
;
1132 else if(strcasecmp(mode
, "basic") == 0)
1133 device
->Render_Mode
= NormalRender
;
1135 ERR("Unexpected hrtf-mode: %s\n", mode
);
1138 if(device
->Render_Mode
== HrtfRender
)
1140 /* Don't bother with HOA when using full HRTF rendering. Nothing
1141 * needs it, and it eases the CPU/memory load.
1143 device
->AmbiUp
= nullptr;
1148 device
->AmbiUp
.reset(new AmbiUpsampler
{});
1151 TRACE("%s HRTF rendering enabled, using \"%s\"\n",
1152 ((device
->Render_Mode
== HrtfRender
) ? "Full" : "Basic"), device
->HrtfName
.c_str()
1154 InitHrtfPanning(device
);
1157 device
->HrtfStatus
= ALC_HRTF_UNSUPPORTED_FORMAT_SOFT
;
1161 Hrtf_DecRef(old_hrtf
);
1163 TRACE("HRTF disabled\n");
1165 device
->Render_Mode
= StereoPair
;
1167 device
->AmbiUp
= nullptr;
1169 int bs2blevel
{((headphones
&& hrtf_appreq
!= Hrtf_Disable
) ||
1170 (hrtf_appreq
== Hrtf_Enable
)) ? 5 : 0};
1171 if(device
->Type
!= Loopback
)
1172 ConfigValueInt(device
->DeviceName
.c_str(), nullptr, "cf_level", &bs2blevel
);
1173 if(bs2blevel
> 0 && bs2blevel
<= 6)
1175 device
->Bs2b
.reset(new bs2b
{});
1176 bs2b_set_params(device
->Bs2b
.get(), bs2blevel
, device
->Frequency
);
1177 TRACE("BS2B enabled\n");
1178 InitPanning(device
);
1182 TRACE("BS2B disabled\n");
1185 if(ConfigValueStr(device
->DeviceName
.c_str(), nullptr, "stereo-encoding", &mode
))
1187 if(strcasecmp(mode
, "uhj") == 0)
1188 device
->Render_Mode
= NormalRender
;
1189 else if(strcasecmp(mode
, "panpot") != 0)
1190 ERR("Unexpected stereo-encoding: %s\n", mode
);
1192 if(device
->Render_Mode
== NormalRender
)
1194 device
->Uhj_Encoder
.reset(new Uhj2Encoder
{});
1195 TRACE("UHJ enabled\n");
1196 InitUhjPanning(device
);
1200 TRACE("UHJ disabled\n");
1201 InitPanning(device
);
1205 void aluInitEffectPanning(ALeffectslot
*slot
)
1207 const size_t count
{countof(slot
->ChanMap
)};
1208 std::transform(std::begin(ACN2ACN
), std::begin(ACN2ACN
)+count
, std::begin(slot
->ChanMap
),
1209 [](const ALsizei
&acn
) noexcept
{ return BFChannelConfig
{1.0f
, acn
}; }
1211 slot
->NumChannels
= static_cast<ALsizei
>(count
);