9 #include "bformatdec.h"
11 #include "filters/splitter.h"
18 /* NOTE: These are scale factors as applied to Ambisonics content. Decoder
19 * coefficients should be divided by these values to get proper N3D scalings.
21 const ALfloat N3D2N3DScale
[MAX_AMBI_COEFFS
] = {
22 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
,
23 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
25 const ALfloat SN3D2N3DScale
[MAX_AMBI_COEFFS
] = {
26 1.000000000f
, /* ACN 0 (W), sqrt(1) */
27 1.732050808f
, /* ACN 1 (Y), sqrt(3) */
28 1.732050808f
, /* ACN 2 (Z), sqrt(3) */
29 1.732050808f
, /* ACN 3 (X), sqrt(3) */
30 2.236067978f
, /* ACN 4 (V), sqrt(5) */
31 2.236067978f
, /* ACN 5 (T), sqrt(5) */
32 2.236067978f
, /* ACN 6 (R), sqrt(5) */
33 2.236067978f
, /* ACN 7 (S), sqrt(5) */
34 2.236067978f
, /* ACN 8 (U), sqrt(5) */
35 2.645751311f
, /* ACN 9 (Q), sqrt(7) */
36 2.645751311f
, /* ACN 10 (O), sqrt(7) */
37 2.645751311f
, /* ACN 11 (M), sqrt(7) */
38 2.645751311f
, /* ACN 12 (K), sqrt(7) */
39 2.645751311f
, /* ACN 13 (L), sqrt(7) */
40 2.645751311f
, /* ACN 14 (N), sqrt(7) */
41 2.645751311f
, /* ACN 15 (P), sqrt(7) */
43 const ALfloat FuMa2N3DScale
[MAX_AMBI_COEFFS
] = {
44 1.414213562f
, /* ACN 0 (W), sqrt(2) */
45 1.732050808f
, /* ACN 1 (Y), sqrt(3) */
46 1.732050808f
, /* ACN 2 (Z), sqrt(3) */
47 1.732050808f
, /* ACN 3 (X), sqrt(3) */
48 1.936491673f
, /* ACN 4 (V), sqrt(15)/2 */
49 1.936491673f
, /* ACN 5 (T), sqrt(15)/2 */
50 2.236067978f
, /* ACN 6 (R), sqrt(5) */
51 1.936491673f
, /* ACN 7 (S), sqrt(15)/2 */
52 1.936491673f
, /* ACN 8 (U), sqrt(15)/2 */
53 2.091650066f
, /* ACN 9 (Q), sqrt(35/8) */
54 1.972026594f
, /* ACN 10 (O), sqrt(35)/3 */
55 2.231093404f
, /* ACN 11 (M), sqrt(224/45) */
56 2.645751311f
, /* ACN 12 (K), sqrt(7) */
57 2.231093404f
, /* ACN 13 (L), sqrt(224/45) */
58 1.972026594f
, /* ACN 14 (N), sqrt(35)/3 */
59 2.091650066f
, /* ACN 15 (P), sqrt(35/8) */
67 static_assert(BFormatDec::sNumBands
== 2, "Unexpected BFormatDec::sNumBands");
68 static_assert(AmbiUpsampler::sNumBands
== 2, "Unexpected AmbiUpsampler::sNumBands");
70 /* These points are in AL coordinates! */
71 constexpr ALfloat Ambi3DPoints
[8][3] = {
72 { -0.577350269f
, 0.577350269f
, -0.577350269f
},
73 { 0.577350269f
, 0.577350269f
, -0.577350269f
},
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
},
81 constexpr ALfloat Ambi3DDecoder
[8][MAX_AMBI_COEFFS
] = {
82 { 0.125f
, 0.125f
, 0.125f
, 0.125f
},
83 { 0.125f
, -0.125f
, 0.125f
, 0.125f
},
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
},
91 constexpr ALfloat Ambi3DDecoderHFScale
[MAX_AMBI_COEFFS
] = {
93 1.15470054f
, 1.15470054f
, 1.15470054f
97 #define INVALID_UPSAMPLE_INDEX INT_MAX
98 ALsizei
GetACNIndex(const BFChannelConfig
*chans
, ALsizei numchans
, ALsizei acn
)
101 for(i
= 0;i
< numchans
;i
++)
103 if(chans
[i
].Index
== acn
)
106 return INVALID_UPSAMPLE_INDEX
;
108 #define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a))
113 void BFormatDec::reset(const AmbDecConf
*conf
, ALsizei chancount
, ALuint srate
, const ALsizei (&chanmap
)[MAX_OUTPUT_CHANNELS
])
115 static constexpr ALsizei map2DTo3D
[MAX_AMBI2D_COEFFS
] = {
118 const ALfloat
*coeff_scale
= N3D2N3DScale
;
121 mSamplesHF
= nullptr;
122 mSamplesLF
= nullptr;
124 mNumChannels
= chancount
;
125 mSamples
.resize(mNumChannels
* 2);
126 mSamplesHF
= mSamples
.data();
127 mSamplesLF
= mSamplesHF
+ mNumChannels
;
129 mEnabled
= std::accumulate(std::begin(chanmap
), std::begin(chanmap
)+conf
->NumSpeakers
, 0u,
130 [](ALuint mask
, const ALsizei
&chan
) noexcept
-> ALuint
131 { return mask
| (1 << chan
); }
134 if(conf
->CoeffScale
== AmbDecScale::SN3D
)
135 coeff_scale
= SN3D2N3DScale
;
136 else if(conf
->CoeffScale
== AmbDecScale::FuMa
)
137 coeff_scale
= FuMa2N3DScale
;
139 float ratio
{400.0f
/ (float)srate
};
140 for(auto &chan
: mUpSampler
)
142 chan
.XOver
.init(ratio
);
144 std::fill(std::begin(chan
.Gains
), std::end(chan
.Gains
), 0.0f
);
147 const bool periphonic
{(conf
->ChanMask
&AMBI_PERIPHONIC_MASK
) != 0};
150 mUpSampler
[0].Gains
[HF_BAND
] = (conf
->ChanMask
> 0x1ff) ? W_SCALE_3H3P
:
151 (conf
->ChanMask
> 0xf) ? W_SCALE_2H2P
: 1.0f
;
152 mUpSampler
[0].Gains
[LF_BAND
] = 1.0f
;
153 for(ALsizei i
{1};i
< 4;i
++)
155 mUpSampler
[i
].Gains
[HF_BAND
] = (conf
->ChanMask
> 0x1ff) ? XYZ_SCALE_3H3P
:
156 (conf
->ChanMask
> 0xf) ? XYZ_SCALE_2H2P
: 1.0f
;
157 mUpSampler
[i
].Gains
[LF_BAND
] = 1.0f
;
162 mUpSampler
[0].Gains
[HF_BAND
] = (conf
->ChanMask
> 0x1ff) ? W_SCALE_3H0P
:
163 (conf
->ChanMask
> 0xf) ? W_SCALE_2H0P
: 1.0f
;
164 mUpSampler
[0].Gains
[LF_BAND
] = 1.0f
;
165 for(ALsizei i
{1};i
< 3;i
++)
167 mUpSampler
[i
].Gains
[HF_BAND
] = (conf
->ChanMask
> 0x1ff) ? XYZ_SCALE_3H0P
:
168 (conf
->ChanMask
> 0xf) ? XYZ_SCALE_2H0P
: 1.0f
;
169 mUpSampler
[i
].Gains
[LF_BAND
] = 1.0f
;
171 mUpSampler
[3].Gains
[HF_BAND
] = 0.0f
;
172 mUpSampler
[3].Gains
[LF_BAND
] = 0.0f
;
175 memset(&mMatrix
, 0, sizeof(mMatrix
));
176 if(conf
->FreqBands
== 1)
178 mDualBand
= AL_FALSE
;
179 for(ALsizei i
{0};i
< conf
->NumSpeakers
;i
++)
181 ALsizei chan
= chanmap
[i
];
187 for(j
= 0,k
= 0;j
< MAX_AMBI2D_COEFFS
;j
++)
189 ALsizei l
= map2DTo3D
[j
];
190 if(j
== 0) gain
= conf
->HFOrderGain
[0];
191 else if(j
== 1) gain
= conf
->HFOrderGain
[1];
192 else if(j
== 3) gain
= conf
->HFOrderGain
[2];
193 else if(j
== 5) gain
= conf
->HFOrderGain
[3];
194 if((conf
->ChanMask
&(1<<l
)))
195 mMatrix
.Single
[chan
][j
] = conf
->HFMatrix
[i
][k
++] / coeff_scale
[l
] * gain
;
200 for(j
= 0,k
= 0;j
< MAX_AMBI_COEFFS
;j
++)
202 if(j
== 0) gain
= conf
->HFOrderGain
[0];
203 else if(j
== 1) gain
= conf
->HFOrderGain
[1];
204 else if(j
== 4) gain
= conf
->HFOrderGain
[2];
205 else if(j
== 9) gain
= conf
->HFOrderGain
[3];
206 if((conf
->ChanMask
&(1<<j
)))
207 mMatrix
.Single
[chan
][j
] = conf
->HFMatrix
[i
][k
++] / coeff_scale
[j
] * gain
;
214 using namespace std::placeholders
;
217 ratio
= conf
->XOverFreq
/ (ALfloat
)srate
;
218 std::for_each(std::begin(mXOver
), std::end(mXOver
),
219 std::bind(std::mem_fn(&BandSplitter::init
), _1
, ratio
));
221 ratio
= powf(10.0f
, conf
->XOverRatio
/ 40.0f
);
222 for(ALsizei i
{0};i
< conf
->NumSpeakers
;i
++)
224 ALsizei chan
= chanmap
[i
];
230 for(j
= 0,k
= 0;j
< MAX_AMBI2D_COEFFS
;j
++)
232 ALsizei l
= map2DTo3D
[j
];
233 if(j
== 0) gain
= conf
->HFOrderGain
[0] * ratio
;
234 else if(j
== 1) gain
= conf
->HFOrderGain
[1] * ratio
;
235 else if(j
== 3) gain
= conf
->HFOrderGain
[2] * ratio
;
236 else if(j
== 5) gain
= conf
->HFOrderGain
[3] * ratio
;
237 if((conf
->ChanMask
&(1<<l
)))
238 mMatrix
.Dual
[chan
][HF_BAND
][j
] = conf
->HFMatrix
[i
][k
++] / coeff_scale
[l
] *
241 for(j
= 0,k
= 0;j
< MAX_AMBI2D_COEFFS
;j
++)
243 ALsizei l
= map2DTo3D
[j
];
244 if(j
== 0) gain
= conf
->LFOrderGain
[0] / ratio
;
245 else if(j
== 1) gain
= conf
->LFOrderGain
[1] / ratio
;
246 else if(j
== 3) gain
= conf
->LFOrderGain
[2] / ratio
;
247 else if(j
== 5) gain
= conf
->LFOrderGain
[3] / ratio
;
248 if((conf
->ChanMask
&(1<<l
)))
249 mMatrix
.Dual
[chan
][LF_BAND
][j
] = conf
->LFMatrix
[i
][k
++] / coeff_scale
[l
] *
255 for(j
= 0,k
= 0;j
< MAX_AMBI_COEFFS
;j
++)
257 if(j
== 0) gain
= conf
->HFOrderGain
[0] * ratio
;
258 else if(j
== 1) gain
= conf
->HFOrderGain
[1] * ratio
;
259 else if(j
== 4) gain
= conf
->HFOrderGain
[2] * ratio
;
260 else if(j
== 9) gain
= conf
->HFOrderGain
[3] * ratio
;
261 if((conf
->ChanMask
&(1<<j
)))
262 mMatrix
.Dual
[chan
][HF_BAND
][j
] = conf
->HFMatrix
[i
][k
++] / coeff_scale
[j
] *
265 for(j
= 0,k
= 0;j
< MAX_AMBI_COEFFS
;j
++)
267 if(j
== 0) gain
= conf
->LFOrderGain
[0] / ratio
;
268 else if(j
== 1) gain
= conf
->LFOrderGain
[1] / ratio
;
269 else if(j
== 4) gain
= conf
->LFOrderGain
[2] / ratio
;
270 else if(j
== 9) gain
= conf
->LFOrderGain
[3] / ratio
;
271 if((conf
->ChanMask
&(1<<j
)))
272 mMatrix
.Dual
[chan
][LF_BAND
][j
] = conf
->LFMatrix
[i
][k
++] / coeff_scale
[j
] *
280 void BFormatDec::process(ALfloat (*RESTRICT OutBuffer
)[BUFFERSIZE
], const ALsizei OutChannels
, const ALfloat (*RESTRICT InSamples
)[BUFFERSIZE
], const ALsizei SamplesToDo
)
282 ASSUME(OutChannels
> 0);
283 ASSUME(SamplesToDo
> 0);
288 for(i
= 0;i
< mNumChannels
;i
++)
289 mXOver
[i
].process(mSamplesHF
[i
].data(), mSamplesLF
[i
].data(), InSamples
[i
],
292 for(chan
= 0;chan
< OutChannels
;chan
++)
294 if(UNLIKELY(!(mEnabled
&(1<<chan
))))
297 std::fill(std::begin(mChannelMix
), std::begin(mChannelMix
)+SamplesToDo
, 0.0f
);
298 MixRowSamples(mChannelMix
, mMatrix
.Dual
[chan
][HF_BAND
],
299 &reinterpret_cast<ALfloat(&)[BUFFERSIZE
]>(mSamplesHF
[0]),
300 mNumChannels
, 0, SamplesToDo
302 MixRowSamples(mChannelMix
, mMatrix
.Dual
[chan
][LF_BAND
],
303 &reinterpret_cast<ALfloat(&)[BUFFERSIZE
]>(mSamplesLF
[0]),
304 mNumChannels
, 0, SamplesToDo
307 std::transform(std::begin(mChannelMix
), std::begin(mChannelMix
)+SamplesToDo
,
308 OutBuffer
[chan
], OutBuffer
[chan
], std::plus
<float>());
313 for(chan
= 0;chan
< OutChannels
;chan
++)
315 if(UNLIKELY(!(mEnabled
&(1<<chan
))))
318 std::fill(std::begin(mChannelMix
), std::begin(mChannelMix
)+SamplesToDo
, 0.0f
);
319 MixRowSamples(mChannelMix
, mMatrix
.Single
[chan
], InSamples
,
320 mNumChannels
, 0, SamplesToDo
);
322 std::transform(std::begin(mChannelMix
), std::begin(mChannelMix
)+SamplesToDo
,
323 OutBuffer
[chan
], OutBuffer
[chan
], std::plus
<float>());
328 void BFormatDec::upSample(ALfloat (*RESTRICT OutBuffer
)[BUFFERSIZE
], const ALfloat (*RESTRICT InSamples
)[BUFFERSIZE
], const ALsizei InChannels
, const ALsizei SamplesToDo
)
330 ASSUME(InChannels
> 0);
331 ASSUME(SamplesToDo
> 0);
333 /* This up-sampler leverages the differences observed in dual-band second-
334 * and third-order decoder matrices compared to first-order. For the same
335 * output channel configuration, the low-frequency matrix has identical
336 * coefficients in the shared input channels, while the high-frequency
337 * matrix has extra scalars applied to the W channel and X/Y/Z channels.
338 * Mixing the first-order content into the higher-order stream with the
339 * appropriate counter-scales applied to the HF response results in the
340 * subsequent higher-order decode generating the same response as a first-
343 for(ALsizei i
{0};i
< InChannels
;i
++)
345 /* First, split the first-order components into low and high frequency
348 mUpSampler
[i
].XOver
.process(mSamples
[HF_BAND
].data(), mSamples
[LF_BAND
].data(),
349 InSamples
[i
], SamplesToDo
);
351 /* Now write each band to the output. */
352 MixRowSamples(OutBuffer
[i
], mUpSampler
[i
].Gains
,
353 &reinterpret_cast<ALfloat(&)[BUFFERSIZE
]>(mSamples
[0]),
354 sNumBands
, 0, SamplesToDo
);
359 void AmbiUpsampler::reset(const ALCdevice
*device
, const ALfloat w_scale
, const ALfloat xyz_scale
)
361 using namespace std::placeholders
;
363 float ratio
{400.0f
/ (float)device
->Frequency
};
364 std::for_each(std::begin(mXOver
), std::end(mXOver
),
365 std::bind(std::mem_fn(&BandSplitter::init
), _1
, ratio
));
367 memset(mGains
, 0, sizeof(mGains
));
368 if(device
->Dry
.CoeffCount
> 0)
370 ALfloat encgains
[8][MAX_OUTPUT_CHANNELS
];
371 for(size_t k
{0u};k
< COUNTOF(Ambi3DPoints
);k
++)
373 ALfloat coeffs
[MAX_AMBI_COEFFS
] = { 0.0f
};
374 CalcDirectionCoeffs(Ambi3DPoints
[k
], 0.0f
, coeffs
);
375 ComputePanGains(&device
->Dry
, coeffs
, 1.0f
, encgains
[k
]);
378 /* Combine the matrices that do the in->virt and virt->out conversions
379 * so we get a single in->out conversion. NOTE: the Encoder matrix
380 * (encgains) and output are transposed, so the input channels line up
381 * with the rows and the output channels line up with the columns.
383 for(ALsizei i
{0};i
< 4;i
++)
385 for(ALsizei j
{0};j
< device
->Dry
.NumChannels
;j
++)
388 for(size_t k
{0u};k
< COUNTOF(Ambi3DDecoder
);k
++)
389 gain
+= (ALdouble
)Ambi3DDecoder
[k
][i
] * encgains
[k
][j
];
390 mGains
[i
][j
][HF_BAND
] = (ALfloat
)(gain
* Ambi3DDecoderHFScale
[i
]);
391 mGains
[i
][j
][LF_BAND
] = (ALfloat
)gain
;
397 for(ALsizei i
{0};i
< 4;i
++)
399 ALsizei index
= GetChannelForACN(device
->Dry
, i
);
400 if(index
!= INVALID_UPSAMPLE_INDEX
)
402 ALfloat scale
= device
->Dry
.Ambi
.Map
[index
].Scale
;
403 mGains
[i
][index
][HF_BAND
] = scale
* ((i
==0) ? w_scale
: xyz_scale
);
404 mGains
[i
][index
][LF_BAND
] = scale
;
410 void AmbiUpsampler::process(ALfloat (*RESTRICT OutBuffer
)[BUFFERSIZE
], const ALsizei OutChannels
, const ALfloat (*RESTRICT InSamples
)[BUFFERSIZE
], const ALsizei SamplesToDo
)
412 ASSUME(OutChannels
> 0);
413 ASSUME(SamplesToDo
> 0);
415 for(ALsizei i
{0};i
< 4;i
++)
417 mXOver
[i
].process(mSamples
[HF_BAND
], mSamples
[LF_BAND
], InSamples
[i
], SamplesToDo
);
419 for(ALsizei j
{0};j
< OutChannels
;j
++)
420 MixRowSamples(OutBuffer
[j
], mGains
[i
][j
], mSamples
, sNumBands
, 0, SamplesToDo
);