10 /* Near-field control filters are the basis for handling the near-field effect.
11 * The near-field effect is a bass-boost present in the directional components
12 * of a recorded signal, created as a result of the wavefront curvature (itself
13 * a function of sound distance). Proper reproduction dictates this be
14 * compensated for using a bass-cut given the playback speaker distance, to
15 * avoid excessive bass in the playback.
17 * For real-time rendered audio, emulating the near-field effect based on the
18 * sound source's distance, and subsequently compensating for it at output
19 * based on the speaker distances, can create a more realistic perception of
20 * sound distance beyond a simple 1/r attenuation.
22 * These filters do just that. Each one applies a low-shelf filter, created as
23 * the combination of a bass-boost for a given sound source distance (near-
24 * field emulation) along with a bass-cut for a given control/speaker distance
25 * (near-field compensation).
27 * Note that it is necessary to apply a cut along with the boost, since the
28 * boost alone is unstable in higher-order ambisonics as it causes an infinite
29 * DC gain (even first-order ambisonics requires there to be no DC offset for
30 * the boost to work). Consequently, ambisonics requires a control parameter to
31 * be used to avoid an unstable boost-only filter. NFC-HOA defines this control
32 * as a reference delay, calculated with:
34 * reference_delay = control_distance / speed_of_sound
36 * This means w0 (for input) or w1 (for output) should be set to:
38 * wN = 1 / (reference_delay * sample_rate)
40 * when dealing with NFC-HOA content. For FOA input content, which does not
41 * specify a reference_delay variable, w0 should be set to 0 to apply only
42 * near-field compensation for output. It's important that w1 be a finite,
43 * positive, non-0 value or else the bass-boost will become unstable again.
44 * Also, w0 should not be too large compared to w1, to avoid excessively loud
48 static const float B
[4][3] = {
52 { 3.6778f
, 6.4595f
, 2.3222f
},
53 /*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/
56 static void NfcFilterCreate1(struct NfcFilter1
*nfc
, const float w0
, const float w1
)
61 nfc
->base_gain
= 1.0f
;
64 /* Calculate bass-boost coefficients. */
70 nfc
->b1
= 2.0f
* b_00
/ g_0
;
72 /* Calculate bass-cut coefficients. */
77 nfc
->base_gain
/= g_0
;
79 nfc
->a1
= 2.0f
* b_00
/ g_0
;
82 static void NfcFilterAdjust1(struct NfcFilter1
*nfc
, const float w0
)
91 nfc
->gain
= nfc
->base_gain
* g_0
;
92 nfc
->b1
= 2.0f
* b_00
/ g_0
;
96 static void NfcFilterCreate2(struct NfcFilter2
*nfc
, const float w0
, const float w1
)
98 float b_10
, b_11
, g_1
;
101 nfc
->base_gain
= 1.0f
;
104 /* Calculate bass-boost coefficients. */
107 b_11
= B
[2][1] * r
* r
;
108 g_1
= 1.0f
+ b_10
+ b_11
;
111 nfc
->b1
= (2.0f
*b_10
+ 4.0f
*b_11
) / g_1
;
112 nfc
->b2
= 4.0f
* b_11
/ g_1
;
114 /* Calculate bass-cut coefficients. */
117 b_11
= B
[2][1] * r
* r
;
118 g_1
= 1.0f
+ b_10
+ b_11
;
120 nfc
->base_gain
/= g_1
;
122 nfc
->a1
= (2.0f
*b_10
+ 4.0f
*b_11
) / g_1
;
123 nfc
->a2
= 4.0f
* b_11
/ g_1
;
126 static void NfcFilterAdjust2(struct NfcFilter2
*nfc
, const float w0
)
128 float b_10
, b_11
, g_1
;
133 b_11
= B
[2][1] * r
* r
;
134 g_1
= 1.0f
+ b_10
+ b_11
;
136 nfc
->gain
= nfc
->base_gain
* g_1
;
137 nfc
->b1
= (2.0f
*b_10
+ 4.0f
*b_11
) / g_1
;
138 nfc
->b2
= 4.0f
* b_11
/ g_1
;
142 static void NfcFilterCreate3(struct NfcFilter3
*nfc
, const float w0
, const float w1
)
144 float b_10
, b_11
, g_1
;
148 nfc
->base_gain
= 1.0f
;
151 /* Calculate bass-boost coefficients. */
154 b_11
= B
[3][1] * r
* r
;
155 g_1
= 1.0f
+ b_10
+ b_11
;
158 nfc
->b1
= (2.0f
*b_10
+ 4.0f
*b_11
) / g_1
;
159 nfc
->b2
= 4.0f
* b_11
/ g_1
;
165 nfc
->b3
= 2.0f
* b_00
/ g_0
;
167 /* Calculate bass-cut coefficients. */
170 b_11
= B
[3][1] * r
* r
;
171 g_1
= 1.0f
+ b_10
+ b_11
;
173 nfc
->base_gain
/= g_1
;
175 nfc
->a1
= (2.0f
*b_10
+ 4.0f
*b_11
) / g_1
;
176 nfc
->a2
= 4.0f
* b_11
/ g_1
;
181 nfc
->base_gain
/= g_0
;
183 nfc
->a3
= 2.0f
* b_00
/ g_0
;
186 static void NfcFilterAdjust3(struct NfcFilter3
*nfc
, const float w0
)
188 float b_10
, b_11
, g_1
;
194 b_11
= B
[3][1] * r
* r
;
195 g_1
= 1.0f
+ b_10
+ b_11
;
197 nfc
->gain
= nfc
->base_gain
* g_1
;
198 nfc
->b1
= (2.0f
*b_10
+ 4.0f
*b_11
) / g_1
;
199 nfc
->b2
= 4.0f
* b_11
/ g_1
;
205 nfc
->b3
= 2.0f
* b_00
/ g_0
;
209 void NfcFilterCreate(NfcFilter
*nfc
, const float w0
, const float w1
)
211 memset(nfc
, 0, sizeof(*nfc
));
212 NfcFilterCreate1(&nfc
->first
, w0
, w1
);
213 NfcFilterCreate2(&nfc
->second
, w0
, w1
);
214 NfcFilterCreate3(&nfc
->third
, w0
, w1
);
217 void NfcFilterAdjust(NfcFilter
*nfc
, const float w0
)
219 NfcFilterAdjust1(&nfc
->first
, w0
);
220 NfcFilterAdjust2(&nfc
->second
, w0
);
221 NfcFilterAdjust3(&nfc
->third
, w0
);
225 void NfcFilterProcess1(NfcFilter
*nfc
, float *restrict dst
, const float *restrict src
, const int count
)
227 const float gain
= nfc
->first
.gain
;
228 const float b1
= nfc
->first
.b1
;
229 const float a1
= nfc
->first
.a1
;
230 float z1
= nfc
->first
.z
[0];
235 for(i
= 0;i
< count
;i
++)
237 float y
= src
[i
]*gain
- a1
*z1
;
238 float out
= y
+ b1
*z1
;
243 nfc
->first
.z
[0] = z1
;
246 void NfcFilterProcess2(NfcFilter
*nfc
, float *restrict dst
, const float *restrict src
, const int count
)
248 const float gain
= nfc
->second
.gain
;
249 const float b1
= nfc
->second
.b1
;
250 const float b2
= nfc
->second
.b2
;
251 const float a1
= nfc
->second
.a1
;
252 const float a2
= nfc
->second
.a2
;
253 float z1
= nfc
->second
.z
[0];
254 float z2
= nfc
->second
.z
[1];
259 for(i
= 0;i
< count
;i
++)
261 float y
= src
[i
]*gain
- a1
*z1
- a2
*z2
;
262 float out
= y
+ b1
*z1
+ b2
*z2
;
268 nfc
->second
.z
[0] = z1
;
269 nfc
->second
.z
[1] = z2
;
272 void NfcFilterProcess3(NfcFilter
*nfc
, float *restrict dst
, const float *restrict src
, const int count
)
274 const float gain
= nfc
->third
.gain
;
275 const float b1
= nfc
->third
.b1
;
276 const float b2
= nfc
->third
.b2
;
277 const float b3
= nfc
->third
.b3
;
278 const float a1
= nfc
->third
.a1
;
279 const float a2
= nfc
->third
.a2
;
280 const float a3
= nfc
->third
.a3
;
281 float z1
= nfc
->third
.z
[0];
282 float z2
= nfc
->third
.z
[1];
283 float z3
= nfc
->third
.z
[2];
288 for(i
= 0;i
< count
;i
++)
290 float y
= src
[i
]*gain
- a1
*z1
- a2
*z2
;
291 float out
= y
+ b1
*z1
+ b2
*z2
;
301 nfc
->third
.z
[0] = z1
;
302 nfc
->third
.z
[1] = z2
;
303 nfc
->third
.z
[2] = z3
;
306 #if 0 /* Original methods the above are derived from. */
307 static void NfcFilterCreate(NfcFilter
*nfc
, const ALsizei order
, const float src_dist
, const float ctl_dist
, const float rate
)
309 static const float B
[4][5] = {
313 { 3.6778f
, 6.4595f
, 2.3222f
},
314 { 4.2076f
, 11.4877f
, 5.7924f
, 9.1401f
}
316 float w0
= SPEEDOFSOUNDMETRESPERSEC
/ (src_dist
* rate
);
317 float w1
= SPEEDOFSOUNDMETRESPERSEC
/ (ctl_dist
* rate
);
322 nfc
->coeffs
[0] = 1.0f
;
324 /* NOTE: Slight adjustment from the literature to raise the center
325 * frequency a bit (0.5 -> 1.0).
328 for(i
= 0; i
< (order
-1);i
+= 2)
330 float b_10
= B
[order
][i
] * r
;
331 float b_11
= B
[order
][i
+1] * r
* r
;
332 float g_1
= 1.0f
+ b_10
+ b_11
;
335 nfc
->b
[i
+ 1] = b_11
;
336 nfc
->coeffs
[0] *= g_1
;
337 nfc
->coeffs
[i
+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
338 nfc
->coeffs
[i
+2] = (4.0f
* b_11
) / g_1
;
342 float b_00
= B
[order
][i
] * r
;
343 float g_0
= 1.0f
+ b_00
;
346 nfc
->coeffs
[0] *= g_0
;
347 nfc
->coeffs
[i
+1] = (2.0f
* b_00
) / g_0
;
351 for(i
= 0;i
< (order
-1);i
+= 2)
353 float b_10
= B
[order
][i
] * r
;
354 float b_11
= B
[order
][i
+1] * r
* r
;
355 float g_1
= 1.0f
+ b_10
+ b_11
;
358 nfc
->coeffs
[0] /= g_1
;
359 nfc
->coeffs
[order
+i
+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
360 nfc
->coeffs
[order
+i
+2] = (4.0f
* b_11
) / g_1
;
364 float b_00
= B
[order
][i
] * r
;
365 float g_0
= 1.0f
+ b_00
;
368 nfc
->coeffs
[0] /= g_0
;
369 nfc
->coeffs
[order
+i
+1] = (2.0f
* b_00
) / g_0
;
372 for(i
= 0; i
< MAX_AMBI_ORDER
; i
++)
373 nfc
->history
[i
] = 0.0f
;
376 static void NfcFilterAdjust(NfcFilter
*nfc
, const float distance
)
380 nfc
->coeffs
[0] = nfc
->g
;
382 for(i
= 0;i
< (nfc
->order
-1);i
+= 2)
384 float b_10
= nfc
->b
[i
] / distance
;
385 float b_11
= nfc
->b
[i
+1] / (distance
* distance
);
386 float g_1
= 1.0f
+ b_10
+ b_11
;
388 nfc
->coeffs
[0] *= g_1
;
389 nfc
->coeffs
[i
+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
390 nfc
->coeffs
[i
+2] = (4.0f
* b_11
) / g_1
;
394 float b_00
= nfc
->b
[i
] / distance
;
395 float g_0
= 1.0f
+ b_00
;
397 nfc
->coeffs
[0] *= g_0
;
398 nfc
->coeffs
[i
+1] = (2.0f
* b_00
) / g_0
;
402 static float NfcFilterProcess(const float in
, NfcFilter
*nfc
)
405 float out
= in
* nfc
->coeffs
[0];
407 for(i
= 0;i
< (nfc
->order
-1);i
+= 2)
409 float y
= out
- (nfc
->coeffs
[nfc
->order
+i
+1] * nfc
->history
[i
]) -
410 (nfc
->coeffs
[nfc
->order
+i
+2] * nfc
->history
[i
+1]) + 1.0e-30f
;
411 out
= y
+ (nfc
->coeffs
[i
+1]*nfc
->history
[i
]) + (nfc
->coeffs
[i
+2]*nfc
->history
[i
+1]);
413 nfc
->history
[i
+1] += nfc
->history
[i
];
414 nfc
->history
[i
] += y
;
418 float y
= out
- (nfc
->coeffs
[nfc
->order
+i
+1] * nfc
->history
[i
]) + 1.0e-30f
;
420 out
= y
+ (nfc
->coeffs
[i
+1] * nfc
->history
[i
]);
421 nfc
->history
[i
] += y
;