11 #include "bformatdec.h"
13 #include "filters/splitter.h"
20 /* NOTE: These are scale factors as applied to Ambisonics content. Decoder
21 * coefficients should be divided by these values to get proper N3D scalings.
23 const ALfloat N3D2N3DScale
[MAX_AMBI_COEFFS
] = {
24 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
,
25 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
27 const ALfloat SN3D2N3DScale
[MAX_AMBI_COEFFS
] = {
28 1.000000000f
, /* ACN 0 (W), sqrt(1) */
29 1.732050808f
, /* ACN 1 (Y), sqrt(3) */
30 1.732050808f
, /* ACN 2 (Z), sqrt(3) */
31 1.732050808f
, /* ACN 3 (X), sqrt(3) */
32 2.236067978f
, /* ACN 4 (V), sqrt(5) */
33 2.236067978f
, /* ACN 5 (T), sqrt(5) */
34 2.236067978f
, /* ACN 6 (R), sqrt(5) */
35 2.236067978f
, /* ACN 7 (S), sqrt(5) */
36 2.236067978f
, /* ACN 8 (U), sqrt(5) */
37 2.645751311f
, /* ACN 9 (Q), sqrt(7) */
38 2.645751311f
, /* ACN 10 (O), sqrt(7) */
39 2.645751311f
, /* ACN 11 (M), sqrt(7) */
40 2.645751311f
, /* ACN 12 (K), sqrt(7) */
41 2.645751311f
, /* ACN 13 (L), sqrt(7) */
42 2.645751311f
, /* ACN 14 (N), sqrt(7) */
43 2.645751311f
, /* ACN 15 (P), sqrt(7) */
45 const ALfloat FuMa2N3DScale
[MAX_AMBI_COEFFS
] = {
46 1.414213562f
, /* ACN 0 (W), sqrt(2) */
47 1.732050808f
, /* ACN 1 (Y), sqrt(3) */
48 1.732050808f
, /* ACN 2 (Z), sqrt(3) */
49 1.732050808f
, /* ACN 3 (X), sqrt(3) */
50 1.936491673f
, /* ACN 4 (V), sqrt(15)/2 */
51 1.936491673f
, /* ACN 5 (T), sqrt(15)/2 */
52 2.236067978f
, /* ACN 6 (R), sqrt(5) */
53 1.936491673f
, /* ACN 7 (S), sqrt(15)/2 */
54 1.936491673f
, /* ACN 8 (U), sqrt(15)/2 */
55 2.091650066f
, /* ACN 9 (Q), sqrt(35/8) */
56 1.972026594f
, /* ACN 10 (O), sqrt(35)/3 */
57 2.231093404f
, /* ACN 11 (M), sqrt(224/45) */
58 2.645751311f
, /* ACN 12 (K), sqrt(7) */
59 2.231093404f
, /* ACN 13 (L), sqrt(224/45) */
60 1.972026594f
, /* ACN 14 (N), sqrt(35)/3 */
61 2.091650066f
, /* ACN 15 (P), sqrt(35/8) */
69 static_assert(BFormatDec::sNumBands
== 2, "Unexpected BFormatDec::sNumBands");
70 static_assert(AmbiUpsampler::sNumBands
== 2, "Unexpected AmbiUpsampler::sNumBands");
72 /* These points are in AL coordinates! */
73 constexpr ALfloat Ambi3DPoints
[8][3] = {
74 { -0.577350269f
, 0.577350269f
, -0.577350269f
},
75 { 0.577350269f
, 0.577350269f
, -0.577350269f
},
76 { -0.577350269f
, 0.577350269f
, 0.577350269f
},
77 { 0.577350269f
, 0.577350269f
, 0.577350269f
},
78 { -0.577350269f
, -0.577350269f
, -0.577350269f
},
79 { 0.577350269f
, -0.577350269f
, -0.577350269f
},
80 { -0.577350269f
, -0.577350269f
, 0.577350269f
},
81 { 0.577350269f
, -0.577350269f
, 0.577350269f
},
83 constexpr ALfloat Ambi3DDecoder
[8][MAX_AMBI_COEFFS
] = {
84 { 0.125f
, 0.125f
, 0.125f
, 0.125f
},
85 { 0.125f
, -0.125f
, 0.125f
, 0.125f
},
86 { 0.125f
, 0.125f
, 0.125f
, -0.125f
},
87 { 0.125f
, -0.125f
, 0.125f
, -0.125f
},
88 { 0.125f
, 0.125f
, -0.125f
, 0.125f
},
89 { 0.125f
, -0.125f
, -0.125f
, 0.125f
},
90 { 0.125f
, 0.125f
, -0.125f
, -0.125f
},
91 { 0.125f
, -0.125f
, -0.125f
, -0.125f
},
93 constexpr ALfloat Ambi3DDecoderHFScale
[MAX_AMBI_COEFFS
] = {
95 1.15470054f
, 1.15470054f
, 1.15470054f
99 #define INVALID_UPSAMPLE_INDEX INT_MAX
100 ALsizei
GetACNIndex(const BFChannelConfig
*chans
, ALsizei numchans
, ALsizei acn
)
103 for(i
= 0;i
< numchans
;i
++)
105 if(chans
[i
].Index
== acn
)
108 return INVALID_UPSAMPLE_INDEX
;
110 #define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a))
115 void BFormatDec::reset(const AmbDecConf
*conf
, ALsizei chancount
, ALuint srate
, const ALsizei (&chanmap
)[MAX_OUTPUT_CHANNELS
])
117 static constexpr ALsizei map2DTo3D
[MAX_AMBI2D_COEFFS
] = {
120 const ALfloat
*coeff_scale
= N3D2N3DScale
;
123 mSamplesHF
= nullptr;
124 mSamplesLF
= nullptr;
126 mNumChannels
= chancount
;
127 mSamples
.resize(mNumChannels
* 2);
128 mSamplesHF
= mSamples
.data();
129 mSamplesLF
= mSamplesHF
+ mNumChannels
;
131 mEnabled
= std::accumulate(std::begin(chanmap
), std::begin(chanmap
)+conf
->NumSpeakers
, 0u,
132 [](ALuint mask
, const ALsizei
&chan
) noexcept
-> ALuint
133 { return mask
| (1 << chan
); }
136 if(conf
->CoeffScale
== AmbDecScale::SN3D
)
137 coeff_scale
= SN3D2N3DScale
;
138 else if(conf
->CoeffScale
== AmbDecScale::FuMa
)
139 coeff_scale
= FuMa2N3DScale
;
141 mUpSampler
[0].XOver
.init(400.0f
/ (float)srate
);
142 std::fill(std::begin(mUpSampler
[0].Gains
), std::end(mUpSampler
[0].Gains
), 0.0f
);
143 std::fill(std::begin(mUpSampler
)+1, std::end(mUpSampler
), mUpSampler
[0]);
145 const bool periphonic
{(conf
->ChanMask
&AMBI_PERIPHONIC_MASK
) != 0};
148 mUpSampler
[0].Gains
[HF_BAND
] = (conf
->ChanMask
> 0x1ff) ? W_SCALE_3H3P
:
149 (conf
->ChanMask
> 0xf) ? W_SCALE_2H2P
: 1.0f
;
150 mUpSampler
[0].Gains
[LF_BAND
] = 1.0f
;
151 for(ALsizei i
{1};i
< 4;i
++)
153 mUpSampler
[i
].Gains
[HF_BAND
] = (conf
->ChanMask
> 0x1ff) ? XYZ_SCALE_3H3P
:
154 (conf
->ChanMask
> 0xf) ? XYZ_SCALE_2H2P
: 1.0f
;
155 mUpSampler
[i
].Gains
[LF_BAND
] = 1.0f
;
160 mUpSampler
[0].Gains
[HF_BAND
] = (conf
->ChanMask
> 0x1ff) ? W_SCALE_3H0P
:
161 (conf
->ChanMask
> 0xf) ? W_SCALE_2H0P
: 1.0f
;
162 mUpSampler
[0].Gains
[LF_BAND
] = 1.0f
;
163 for(ALsizei i
{1};i
< 3;i
++)
165 mUpSampler
[i
].Gains
[HF_BAND
] = (conf
->ChanMask
> 0x1ff) ? XYZ_SCALE_3H0P
:
166 (conf
->ChanMask
> 0xf) ? XYZ_SCALE_2H0P
: 1.0f
;
167 mUpSampler
[i
].Gains
[LF_BAND
] = 1.0f
;
169 mUpSampler
[3].Gains
[HF_BAND
] = 0.0f
;
170 mUpSampler
[3].Gains
[LF_BAND
] = 0.0f
;
173 memset(&mMatrix
, 0, sizeof(mMatrix
));
174 if(conf
->FreqBands
== 1)
176 mDualBand
= AL_FALSE
;
177 for(ALsizei i
{0};i
< conf
->NumSpeakers
;i
++)
179 ALsizei chan
= chanmap
[i
];
185 for(j
= 0,k
= 0;j
< MAX_AMBI2D_COEFFS
;j
++)
187 ALsizei l
= map2DTo3D
[j
];
188 if(j
== 0) gain
= conf
->HFOrderGain
[0];
189 else if(j
== 1) gain
= conf
->HFOrderGain
[1];
190 else if(j
== 3) gain
= conf
->HFOrderGain
[2];
191 else if(j
== 5) gain
= conf
->HFOrderGain
[3];
192 if((conf
->ChanMask
&(1<<l
)))
193 mMatrix
.Single
[chan
][j
] = conf
->HFMatrix
[i
][k
++] / coeff_scale
[l
] * gain
;
198 for(j
= 0,k
= 0;j
< MAX_AMBI_COEFFS
;j
++)
200 if(j
== 0) gain
= conf
->HFOrderGain
[0];
201 else if(j
== 1) gain
= conf
->HFOrderGain
[1];
202 else if(j
== 4) gain
= conf
->HFOrderGain
[2];
203 else if(j
== 9) gain
= conf
->HFOrderGain
[3];
204 if((conf
->ChanMask
&(1<<j
)))
205 mMatrix
.Single
[chan
][j
] = conf
->HFMatrix
[i
][k
++] / coeff_scale
[j
] * gain
;
214 mXOver
[0].init(conf
->XOverFreq
/ (float)srate
);
215 std::fill(std::begin(mXOver
)+1, std::end(mXOver
), mXOver
[0]);
217 float ratio
{std::pow(10.0f
, conf
->XOverRatio
/ 40.0f
)};
218 for(ALsizei i
{0};i
< conf
->NumSpeakers
;i
++)
220 ALsizei chan
= chanmap
[i
];
225 for(ALsizei j
{0},k
{0};j
< MAX_AMBI2D_COEFFS
;j
++)
227 ALsizei l
= map2DTo3D
[j
];
228 if(j
== 0) gain
= conf
->HFOrderGain
[0] * ratio
;
229 else if(j
== 1) gain
= conf
->HFOrderGain
[1] * ratio
;
230 else if(j
== 3) gain
= conf
->HFOrderGain
[2] * ratio
;
231 else if(j
== 5) gain
= conf
->HFOrderGain
[3] * ratio
;
232 if((conf
->ChanMask
&(1<<l
)))
233 mMatrix
.Dual
[chan
][HF_BAND
][j
] = conf
->HFMatrix
[i
][k
++] / coeff_scale
[l
] *
236 for(ALsizei j
{0},k
{0};j
< MAX_AMBI2D_COEFFS
;j
++)
238 ALsizei l
= map2DTo3D
[j
];
239 if(j
== 0) gain
= conf
->LFOrderGain
[0] / ratio
;
240 else if(j
== 1) gain
= conf
->LFOrderGain
[1] / ratio
;
241 else if(j
== 3) gain
= conf
->LFOrderGain
[2] / ratio
;
242 else if(j
== 5) gain
= conf
->LFOrderGain
[3] / ratio
;
243 if((conf
->ChanMask
&(1<<l
)))
244 mMatrix
.Dual
[chan
][LF_BAND
][j
] = conf
->LFMatrix
[i
][k
++] / coeff_scale
[l
] *
250 for(ALsizei j
{0},k
{0};j
< MAX_AMBI_COEFFS
;j
++)
252 if(j
== 0) gain
= conf
->HFOrderGain
[0] * ratio
;
253 else if(j
== 1) gain
= conf
->HFOrderGain
[1] * ratio
;
254 else if(j
== 4) gain
= conf
->HFOrderGain
[2] * ratio
;
255 else if(j
== 9) gain
= conf
->HFOrderGain
[3] * ratio
;
256 if((conf
->ChanMask
&(1<<j
)))
257 mMatrix
.Dual
[chan
][HF_BAND
][j
] = conf
->HFMatrix
[i
][k
++] / coeff_scale
[j
] *
260 for(ALsizei j
{0},k
{0};j
< MAX_AMBI_COEFFS
;j
++)
262 if(j
== 0) gain
= conf
->LFOrderGain
[0] / ratio
;
263 else if(j
== 1) gain
= conf
->LFOrderGain
[1] / ratio
;
264 else if(j
== 4) gain
= conf
->LFOrderGain
[2] / ratio
;
265 else if(j
== 9) gain
= conf
->LFOrderGain
[3] / ratio
;
266 if((conf
->ChanMask
&(1<<j
)))
267 mMatrix
.Dual
[chan
][LF_BAND
][j
] = conf
->LFMatrix
[i
][k
++] / coeff_scale
[j
] *
275 void BFormatDec::process(ALfloat (*RESTRICT OutBuffer
)[BUFFERSIZE
], const ALsizei OutChannels
, const ALfloat (*RESTRICT InSamples
)[BUFFERSIZE
], const ALsizei SamplesToDo
)
277 ASSUME(OutChannels
> 0);
278 ASSUME(SamplesToDo
> 0);
283 for(i
= 0;i
< mNumChannels
;i
++)
284 mXOver
[i
].process(mSamplesHF
[i
].data(), mSamplesLF
[i
].data(), InSamples
[i
],
287 for(chan
= 0;chan
< OutChannels
;chan
++)
289 if(UNLIKELY(!(mEnabled
&(1<<chan
))))
292 std::fill(std::begin(mChannelMix
), std::begin(mChannelMix
)+SamplesToDo
, 0.0f
);
293 MixRowSamples(mChannelMix
, mMatrix
.Dual
[chan
][HF_BAND
],
294 &reinterpret_cast<ALfloat(&)[BUFFERSIZE
]>(mSamplesHF
[0]),
295 mNumChannels
, 0, SamplesToDo
297 MixRowSamples(mChannelMix
, mMatrix
.Dual
[chan
][LF_BAND
],
298 &reinterpret_cast<ALfloat(&)[BUFFERSIZE
]>(mSamplesLF
[0]),
299 mNumChannels
, 0, SamplesToDo
302 std::transform(std::begin(mChannelMix
), std::begin(mChannelMix
)+SamplesToDo
,
303 OutBuffer
[chan
], OutBuffer
[chan
], std::plus
<float>());
308 for(chan
= 0;chan
< OutChannels
;chan
++)
310 if(UNLIKELY(!(mEnabled
&(1<<chan
))))
313 std::fill(std::begin(mChannelMix
), std::begin(mChannelMix
)+SamplesToDo
, 0.0f
);
314 MixRowSamples(mChannelMix
, mMatrix
.Single
[chan
], InSamples
,
315 mNumChannels
, 0, SamplesToDo
);
317 std::transform(std::begin(mChannelMix
), std::begin(mChannelMix
)+SamplesToDo
,
318 OutBuffer
[chan
], OutBuffer
[chan
], std::plus
<float>());
323 void BFormatDec::upSample(ALfloat (*RESTRICT OutBuffer
)[BUFFERSIZE
], const ALfloat (*RESTRICT InSamples
)[BUFFERSIZE
], const ALsizei InChannels
, const ALsizei SamplesToDo
)
325 ASSUME(InChannels
> 0);
326 ASSUME(SamplesToDo
> 0);
328 /* This up-sampler leverages the differences observed in dual-band second-
329 * and third-order decoder matrices compared to first-order. For the same
330 * output channel configuration, the low-frequency matrix has identical
331 * coefficients in the shared input channels, while the high-frequency
332 * matrix has extra scalars applied to the W channel and X/Y/Z channels.
333 * Mixing the first-order content into the higher-order stream with the
334 * appropriate counter-scales applied to the HF response results in the
335 * subsequent higher-order decode generating the same response as a first-
338 for(ALsizei i
{0};i
< InChannels
;i
++)
340 /* First, split the first-order components into low and high frequency
343 mUpSampler
[i
].XOver
.process(mSamples
[HF_BAND
].data(), mSamples
[LF_BAND
].data(),
344 InSamples
[i
], SamplesToDo
);
346 /* Now write each band to the output. */
347 MixRowSamples(OutBuffer
[i
], mUpSampler
[i
].Gains
,
348 &reinterpret_cast<ALfloat(&)[BUFFERSIZE
]>(mSamples
[0]),
349 sNumBands
, 0, SamplesToDo
);
354 void AmbiUpsampler::reset(const ALCdevice
*device
, const ALfloat w_scale
, const ALfloat xyz_scale
)
356 using namespace std::placeholders
;
358 mXOver
[0].init(400.0f
/ (float)device
->Frequency
);
359 std::fill(std::begin(mXOver
)+1, std::end(mXOver
), mXOver
[0]);
361 memset(mGains
, 0, sizeof(mGains
));
362 if(device
->Dry
.CoeffCount
> 0)
364 ALfloat encgains
[8][MAX_OUTPUT_CHANNELS
];
365 for(size_t k
{0u};k
< COUNTOF(Ambi3DPoints
);k
++)
367 ALfloat coeffs
[MAX_AMBI_COEFFS
] = { 0.0f
};
368 CalcDirectionCoeffs(Ambi3DPoints
[k
], 0.0f
, coeffs
);
369 ComputePanGains(&device
->Dry
, coeffs
, 1.0f
, encgains
[k
]);
372 /* Combine the matrices that do the in->virt and virt->out conversions
373 * so we get a single in->out conversion. NOTE: the Encoder matrix
374 * (encgains) and output are transposed, so the input channels line up
375 * with the rows and the output channels line up with the columns.
377 for(ALsizei i
{0};i
< 4;i
++)
379 for(ALsizei j
{0};j
< device
->Dry
.NumChannels
;j
++)
382 for(size_t k
{0u};k
< COUNTOF(Ambi3DDecoder
);k
++)
383 gain
+= (ALdouble
)Ambi3DDecoder
[k
][i
] * encgains
[k
][j
];
384 mGains
[i
][j
][HF_BAND
] = (ALfloat
)(gain
* Ambi3DDecoderHFScale
[i
]);
385 mGains
[i
][j
][LF_BAND
] = (ALfloat
)gain
;
391 for(ALsizei i
{0};i
< 4;i
++)
393 ALsizei index
= GetChannelForACN(device
->Dry
, i
);
394 if(index
!= INVALID_UPSAMPLE_INDEX
)
396 ALfloat scale
= device
->Dry
.Ambi
.Map
[index
].Scale
;
397 mGains
[i
][index
][HF_BAND
] = scale
* ((i
==0) ? w_scale
: xyz_scale
);
398 mGains
[i
][index
][LF_BAND
] = scale
;
404 void AmbiUpsampler::process(ALfloat (*RESTRICT OutBuffer
)[BUFFERSIZE
], const ALsizei OutChannels
, const ALfloat (*RESTRICT InSamples
)[BUFFERSIZE
], const ALsizei SamplesToDo
)
406 ASSUME(OutChannels
> 0);
407 ASSUME(SamplesToDo
> 0);
409 for(ALsizei i
{0};i
< 4;i
++)
411 mXOver
[i
].process(mSamples
[HF_BAND
], mSamples
[LF_BAND
], InSamples
[i
], SamplesToDo
);
413 for(ALsizei j
{0};j
< OutChannels
;j
++)
414 MixRowSamples(OutBuffer
[j
], mGains
[i
][j
], mSamples
, sNumBands
, 0, SamplesToDo
);