9 /* Near-field control filters are the basis for handling the near-field effect.
10 * The near-field effect is a bass-boost present in the directional components
11 * of a recorded signal, created as a result of the wavefront curvature (itself
12 * a function of sound distance). Proper reproduction dictates this be
13 * compensated for using a bass-cut given the playback speaker distance, to
14 * avoid excessive bass in the playback.
16 * For real-time rendered audio, emulating the near-field effect based on the
17 * sound source's distance, and subsequently compensating for it at output
18 * based on the speaker distances, can create a more realistic perception of
19 * sound distance beyond a simple 1/r attenuation.
21 * These filters do just that. Each one applies a low-shelf filter, created as
22 * the combination of a bass-boost for a given sound source distance (near-
23 * field emulation) along with a bass-cut for a given control/speaker distance
24 * (near-field compensation).
26 * Note that it is necessary to apply a cut along with the boost, since the
27 * boost alone is unstable in higher-order ambisonics as it causes an infinite
28 * DC gain (even first-order ambisonics requires there to be no DC offset for
29 * the boost to work). Consequently, ambisonics requires a control parameter to
30 * be used to avoid an unstable boost-only filter. NFC-HOA defines this control
31 * as a reference delay, calculated with:
33 * reference_delay = control_distance / speed_of_sound
35 * This means w0 (for input) or w1 (for output) should be set to:
37 * wN = 1 / (reference_delay * sample_rate)
39 * when dealing with NFC-HOA content. For FOA input content, which does not
40 * specify a reference_delay variable, w0 should be set to 0 to apply only
41 * near-field compensation for output. It's important that w1 be a finite,
42 * positive, non-0 value or else the bass-boost will become unstable again.
43 * Also, w0 should not be too large compared to w1, to avoid excessively loud
47 static const float B
[4][3] = {
51 { 3.6778f
, 6.4595f
, 2.3222f
},
52 /*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/
55 static void NfcFilterCreate1(struct NfcFilter1
*nfc
, const float w0
, const float w1
)
61 nfc
->coeffs
[0] = 1.0f
;
63 /* Calculate bass-boost coefficients. */
68 nfc
->coeffs
[0] *= g_0
;
69 nfc
->coeffs
[1] = (2.0f
* b_00
) / g_0
;
71 /* Calculate bass-cut coefficients. */
77 nfc
->coeffs
[0] /= g_0
;
78 nfc
->coeffs
[1+1] = (2.0f
* b_00
) / g_0
;
81 static void NfcFilterAdjust1(struct NfcFilter1
*nfc
, const float w0
)
90 nfc
->coeffs
[0] = nfc
->g
* g_0
;
91 nfc
->coeffs
[1] = (2.0f
* b_00
) / g_0
;
95 static void NfcFilterCreate2(struct NfcFilter2
*nfc
, const float w0
, const float w1
)
97 float b_10
, b_11
, g_1
;
101 nfc
->coeffs
[0] = 1.0f
;
103 /* Calculate bass-boost coefficients. */
106 b_11
= B
[2][1] * r
* r
;
107 g_1
= 1.0f
+ b_10
+ b_11
;
109 nfc
->coeffs
[0] *= g_1
;
110 nfc
->coeffs
[1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
111 nfc
->coeffs
[2] = (4.0f
* b_11
) / g_1
;
113 /* Calculate bass-cut coefficients. */
116 b_11
= B
[2][1] * r
* r
;
117 g_1
= 1.0f
+ b_10
+ b_11
;
120 nfc
->coeffs
[0] /= g_1
;
121 nfc
->coeffs
[2+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
122 nfc
->coeffs
[2+2] = (4.0f
* b_11
) / g_1
;
125 static void NfcFilterAdjust2(struct NfcFilter2
*nfc
, const float w0
)
127 float b_10
, b_11
, g_1
;
132 b_11
= B
[2][1] * r
* r
;
133 g_1
= 1.0f
+ b_10
+ b_11
;
135 nfc
->coeffs
[0] = nfc
->g
* g_1
;
136 nfc
->coeffs
[1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
137 nfc
->coeffs
[2] = (4.0f
* b_11
) / g_1
;
141 static void NfcFilterCreate3(struct NfcFilter3
*nfc
, const float w0
, const float w1
)
143 float b_10
, b_11
, g_1
;
148 nfc
->coeffs
[0] = 1.0f
;
150 /* Calculate bass-boost coefficients. */
153 b_11
= B
[3][1] * r
* r
;
154 g_1
= 1.0f
+ b_10
+ b_11
;
156 nfc
->coeffs
[0] *= g_1
;
157 nfc
->coeffs
[1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
158 nfc
->coeffs
[2] = (4.0f
* b_11
) / g_1
;
163 nfc
->coeffs
[0] *= g_0
;
164 nfc
->coeffs
[2+1] = (2.0f
* b_00
) / g_0
;
166 /* Calculate bass-cut coefficients. */
169 b_11
= B
[3][1] * r
* r
;
170 g_1
= 1.0f
+ b_10
+ b_11
;
173 nfc
->coeffs
[0] /= g_1
;
174 nfc
->coeffs
[3+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
175 nfc
->coeffs
[3+2] = (4.0f
* b_11
) / g_1
;
181 nfc
->coeffs
[0] /= g_0
;
182 nfc
->coeffs
[3+2+1] = (2.0f
* b_00
) / g_0
;
185 static void NfcFilterAdjust3(struct NfcFilter3
*nfc
, const float w0
)
187 float b_10
, b_11
, g_1
;
193 b_11
= B
[3][1] * r
* r
;
194 g_1
= 1.0f
+ b_10
+ b_11
;
196 nfc
->coeffs
[0] = nfc
->g
* g_1
;
197 nfc
->coeffs
[1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
198 nfc
->coeffs
[2] = (4.0f
* b_11
) / g_1
;
203 nfc
->coeffs
[0] *= g_0
;
204 nfc
->coeffs
[2+1] = (2.0f
* b_00
) / g_0
;
208 void NfcFilterCreate(NfcFilter
*nfc
, const float w0
, const float w1
)
210 memset(nfc
, 0, sizeof(*nfc
));
211 NfcFilterCreate1(&nfc
->first
, w0
, w1
);
212 NfcFilterCreate2(&nfc
->second
, w0
, w1
);
213 NfcFilterCreate3(&nfc
->third
, w0
, w1
);
216 void NfcFilterAdjust(NfcFilter
*nfc
, const float w0
)
218 NfcFilterAdjust1(&nfc
->first
, w0
);
219 NfcFilterAdjust2(&nfc
->second
, w0
);
220 NfcFilterAdjust3(&nfc
->third
, w0
);
224 void NfcFilterUpdate1(NfcFilter
*nfc
, ALfloat
*restrict dst
, const float *restrict src
, const int count
)
226 const float b0
= nfc
->first
.coeffs
[0];
227 const float a0
= nfc
->first
.coeffs
[1];
228 const float a1
= nfc
->first
.coeffs
[2];
229 float z1
= nfc
->first
.history
[0];
232 for(i
= 0;i
< count
;i
++)
234 float out
= src
[i
] * b0
;
243 nfc
->first
.history
[0] = z1
;
246 void NfcFilterUpdate2(NfcFilter
*nfc
, ALfloat
*restrict dst
, const float *restrict src
, const int count
)
248 const float b0
= nfc
->second
.coeffs
[0];
249 const float a00
= nfc
->second
.coeffs
[1];
250 const float a01
= nfc
->second
.coeffs
[2];
251 const float a10
= nfc
->second
.coeffs
[3];
252 const float a11
= nfc
->second
.coeffs
[4];
253 float z1
= nfc
->second
.history
[0];
254 float z2
= nfc
->second
.history
[1];
257 for(i
= 0;i
< count
;i
++)
259 float out
= src
[i
] * b0
;
262 y
= out
- (a10
*z1
) - (a11
*z2
);
263 out
= y
+ (a00
*z1
) + (a01
*z2
);
269 nfc
->second
.history
[0] = z1
;
270 nfc
->second
.history
[1] = z2
;
273 void NfcFilterUpdate3(NfcFilter
*nfc
, ALfloat
*restrict dst
, const float *restrict src
, const int count
)
275 const float b0
= nfc
->third
.coeffs
[0];
276 const float a00
= nfc
->third
.coeffs
[1];
277 const float a01
= nfc
->third
.coeffs
[2];
278 const float a02
= nfc
->third
.coeffs
[3];
279 const float a10
= nfc
->third
.coeffs
[4];
280 const float a11
= nfc
->third
.coeffs
[5];
281 const float a12
= nfc
->third
.coeffs
[6];
282 float z1
= nfc
->third
.history
[0];
283 float z2
= nfc
->third
.history
[1];
284 float z3
= nfc
->third
.history
[2];
287 for(i
= 0;i
< count
;i
++)
289 float out
= src
[i
] * b0
;
292 y
= out
- (a10
*z1
) - (a11
*z2
);
293 out
= y
+ (a00
*z1
) + (a01
*z2
);
303 nfc
->third
.history
[0] = z1
;
304 nfc
->third
.history
[1] = z2
;
305 nfc
->third
.history
[2] = z3
;
308 #if 0 /* Original methods the above are derived from. */
309 static void NfcFilterCreate(NfcFilter
*nfc
, const ALsizei order
, const float src_dist
, const float ctl_dist
, const float rate
)
311 static const float B
[4][5] = {
315 { 3.6778f
, 6.4595f
, 2.3222f
},
316 { 4.2076f
, 11.4877f
, 5.7924f
, 9.1401f
}
318 float w0
= SPEEDOFSOUNDMETRESPERSEC
/ (src_dist
* rate
);
319 float w1
= SPEEDOFSOUNDMETRESPERSEC
/ (ctl_dist
* rate
);
324 nfc
->coeffs
[0] = 1.0f
;
326 /* NOTE: Slight adjustment from the literature to raise the center
327 * frequency a bit (0.5 -> 1.0).
330 for(i
= 0; i
< (order
-1);i
+= 2)
332 float b_10
= B
[order
][i
] * r
;
333 float b_11
= B
[order
][i
+1] * r
* r
;
334 float g_1
= 1.0f
+ b_10
+ b_11
;
337 nfc
->b
[i
+ 1] = b_11
;
338 nfc
->coeffs
[0] *= g_1
;
339 nfc
->coeffs
[i
+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
340 nfc
->coeffs
[i
+2] = (4.0f
* b_11
) / g_1
;
344 float b_00
= B
[order
][i
] * r
;
345 float g_0
= 1.0f
+ b_00
;
348 nfc
->coeffs
[0] *= g_0
;
349 nfc
->coeffs
[i
+1] = (2.0f
* b_00
) / g_0
;
353 for(i
= 0;i
< (order
-1);i
+= 2)
355 float b_10
= B
[order
][i
] * r
;
356 float b_11
= B
[order
][i
+1] * r
* r
;
357 float g_1
= 1.0f
+ b_10
+ b_11
;
360 nfc
->coeffs
[0] /= g_1
;
361 nfc
->coeffs
[order
+i
+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
362 nfc
->coeffs
[order
+i
+2] = (4.0f
* b_11
) / g_1
;
366 float b_00
= B
[order
][i
] * r
;
367 float g_0
= 1.0f
+ b_00
;
370 nfc
->coeffs
[0] /= g_0
;
371 nfc
->coeffs
[order
+i
+1] = (2.0f
* b_00
) / g_0
;
374 for(i
= 0; i
< MAX_AMBI_ORDER
; i
++)
375 nfc
->history
[i
] = 0.0f
;
378 static void NfcFilterAdjust(NfcFilter
*nfc
, const float distance
)
382 nfc
->coeffs
[0] = nfc
->g
;
384 for(i
= 0;i
< (nfc
->order
-1);i
+= 2)
386 float b_10
= nfc
->b
[i
] / distance
;
387 float b_11
= nfc
->b
[i
+1] / (distance
* distance
);
388 float g_1
= 1.0f
+ b_10
+ b_11
;
390 nfc
->coeffs
[0] *= g_1
;
391 nfc
->coeffs
[i
+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
392 nfc
->coeffs
[i
+2] = (4.0f
* b_11
) / g_1
;
396 float b_00
= nfc
->b
[i
] / distance
;
397 float g_0
= 1.0f
+ b_00
;
399 nfc
->coeffs
[0] *= g_0
;
400 nfc
->coeffs
[i
+1] = (2.0f
* b_00
) / g_0
;
404 static float NfcFilterUpdate(const float in
, NfcFilter
*nfc
)
407 float out
= in
* nfc
->coeffs
[0];
409 for(i
= 0;i
< (nfc
->order
-1);i
+= 2)
411 float y
= out
- (nfc
->coeffs
[nfc
->order
+i
+1] * nfc
->history
[i
]) -
412 (nfc
->coeffs
[nfc
->order
+i
+2] * nfc
->history
[i
+1]) + 1.0e-30f
;
413 out
= y
+ (nfc
->coeffs
[i
+1]*nfc
->history
[i
]) + (nfc
->coeffs
[i
+2]*nfc
->history
[i
+1]);
415 nfc
->history
[i
+1] += nfc
->history
[i
];
416 nfc
->history
[i
] += y
;
420 float y
= out
- (nfc
->coeffs
[nfc
->order
+i
+1] * nfc
->history
[i
]) + 1.0e-30f
;
422 out
= y
+ (nfc
->coeffs
[i
+1] * nfc
->history
[i
]);
423 nfc
->history
[i
] += y
;