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 void NfcFilterCreate1(NfcFilter
*nfc
, const float w0
, const float w1
)
60 memset(nfc
, 0, sizeof(*nfc
));
63 nfc
->coeffs
[0] = 1.0f
;
65 /* Calculate bass-boost coefficients. */
70 nfc
->coeffs
[0] *= g_0
;
71 nfc
->coeffs
[1] = (2.0f
* b_00
) / g_0
;
73 /* Calculate bass-cut coefficients. */
79 nfc
->coeffs
[0] /= g_0
;
80 nfc
->coeffs
[1+1] = (2.0f
* b_00
) / g_0
;
83 void NfcFilterAdjust1(NfcFilter
*nfc
, const float w0
)
92 nfc
->coeffs
[0] = nfc
->g
* g_0
;
93 nfc
->coeffs
[1] = (2.0f
* b_00
) / g_0
;
96 void NfcFilterUpdate1(NfcFilter
*nfc
, ALfloat
*restrict dst
, const float *restrict src
, const int count
)
98 const float b0
= nfc
->coeffs
[0];
99 const float a0
= nfc
->coeffs
[1];
100 const float a1
= nfc
->coeffs
[2];
101 float z1
= nfc
->history
[0];
104 for(i
= 0;i
< count
;i
++)
106 float out
= src
[i
] * b0
;
115 nfc
->history
[0] = z1
;
119 void NfcFilterCreate2(NfcFilter
*nfc
, const float w0
, const float w1
)
121 float b_10
, b_11
, g_1
;
124 memset(nfc
, 0, sizeof(*nfc
));
127 nfc
->coeffs
[0] = 1.0f
;
129 /* Calculate bass-boost coefficients. */
132 b_11
= B
[2][1] * r
* r
;
133 g_1
= 1.0f
+ b_10
+ b_11
;
135 nfc
->coeffs
[0] *= 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
;
139 /* Calculate bass-cut coefficients. */
142 b_11
= B
[2][1] * r
* r
;
143 g_1
= 1.0f
+ b_10
+ b_11
;
146 nfc
->coeffs
[0] /= g_1
;
147 nfc
->coeffs
[2+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
148 nfc
->coeffs
[2+2] = (4.0f
* b_11
) / g_1
;
151 void NfcFilterAdjust2(NfcFilter
*nfc
, const float w0
)
153 float b_10
, b_11
, g_1
;
158 b_11
= B
[2][1] * r
* r
;
159 g_1
= 1.0f
+ b_10
+ b_11
;
161 nfc
->coeffs
[0] = nfc
->g
* g_1
;
162 nfc
->coeffs
[1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
163 nfc
->coeffs
[2] = (4.0f
* b_11
) / g_1
;
166 void NfcFilterUpdate2(NfcFilter
*nfc
, ALfloat
*restrict dst
, const float *restrict src
, const int count
)
168 const float b0
= nfc
->coeffs
[0];
169 const float a00
= nfc
->coeffs
[1];
170 const float a01
= nfc
->coeffs
[2];
171 const float a10
= nfc
->coeffs
[3];
172 const float a11
= nfc
->coeffs
[4];
173 float z1
= nfc
->history
[0];
174 float z2
= nfc
->history
[1];
177 for(i
= 0;i
< count
;i
++)
179 float out
= src
[i
] * b0
;
182 y
= out
- (a10
*z1
) - (a11
*z2
);
183 out
= y
+ (a00
*z1
) + (a01
*z2
);
189 nfc
->history
[0] = z1
;
190 nfc
->history
[1] = z2
;
194 void NfcFilterCreate3(NfcFilter
*nfc
, const float w0
, const float w1
)
196 float b_10
, b_11
, g_1
;
200 memset(nfc
, 0, sizeof(*nfc
));
203 nfc
->coeffs
[0] = 1.0f
;
205 /* Calculate bass-boost coefficients. */
208 b_11
= B
[3][1] * r
* r
;
209 g_1
= 1.0f
+ b_10
+ b_11
;
211 nfc
->coeffs
[0] *= g_1
;
212 nfc
->coeffs
[1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
213 nfc
->coeffs
[2] = (4.0f
* b_11
) / g_1
;
218 nfc
->coeffs
[0] *= g_0
;
219 nfc
->coeffs
[2+1] = (2.0f
* b_00
) / g_0
;
221 /* Calculate bass-cut coefficients. */
224 b_11
= B
[3][1] * r
* r
;
225 g_1
= 1.0f
+ b_10
+ b_11
;
228 nfc
->coeffs
[0] /= g_1
;
229 nfc
->coeffs
[3+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
230 nfc
->coeffs
[3+2] = (4.0f
* b_11
) / g_1
;
236 nfc
->coeffs
[0] /= g_0
;
237 nfc
->coeffs
[3+2+1] = (2.0f
* b_00
) / g_0
;
240 void NfcFilterAdjust3(NfcFilter
*nfc
, const float w0
)
242 float b_10
, b_11
, g_1
;
248 b_11
= B
[3][1] * r
* r
;
249 g_1
= 1.0f
+ b_10
+ b_11
;
251 nfc
->coeffs
[0] = nfc
->g
* g_1
;
252 nfc
->coeffs
[1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
253 nfc
->coeffs
[2] = (4.0f
* b_11
) / g_1
;
258 nfc
->coeffs
[0] *= g_0
;
259 nfc
->coeffs
[2+1] = (2.0f
* b_00
) / g_0
;
262 void NfcFilterUpdate3(NfcFilter
*nfc
, ALfloat
*restrict dst
, const float *restrict src
, const int count
)
264 const float b0
= nfc
->coeffs
[0];
265 const float a00
= nfc
->coeffs
[1];
266 const float a01
= nfc
->coeffs
[2];
267 const float a02
= nfc
->coeffs
[3];
268 const float a10
= nfc
->coeffs
[4];
269 const float a11
= nfc
->coeffs
[5];
270 const float a12
= nfc
->coeffs
[6];
271 float z1
= nfc
->history
[0];
272 float z2
= nfc
->history
[1];
273 float z3
= nfc
->history
[2];
276 for(i
= 0;i
< count
;i
++)
278 float out
= src
[i
] * b0
;
281 y
= out
- (a10
*z1
) - (a11
*z2
);
282 out
= y
+ (a00
*z1
) + (a01
*z2
);
292 nfc
->history
[0] = z1
;
293 nfc
->history
[1] = z2
;
294 nfc
->history
[2] = z3
;
298 #if 0 /* Original methods the above are derived from. */
299 static void NfcFilterCreate(NfcFilter
*nfc
, const ALsizei order
, const float src_dist
, const float ctl_dist
, const float rate
)
301 static const float B
[4][5] = {
305 { 3.6778f
, 6.4595f
, 2.3222f
},
306 { 4.2076f
, 11.4877f
, 5.7924f
, 9.1401f
}
308 float w0
= SPEEDOFSOUNDMETRESPERSEC
/ (src_dist
* rate
);
309 float w1
= SPEEDOFSOUNDMETRESPERSEC
/ (ctl_dist
* rate
);
314 nfc
->coeffs
[0] = 1.0f
;
316 /* NOTE: Slight adjustment from the literature to raise the center
317 * frequency a bit (0.5 -> 1.0).
320 for(i
= 0; i
< (order
-1);i
+= 2)
322 float b_10
= B
[order
][i
] * r
;
323 float b_11
= B
[order
][i
+1] * r
* r
;
324 float g_1
= 1.0f
+ b_10
+ b_11
;
327 nfc
->b
[i
+ 1] = b_11
;
328 nfc
->coeffs
[0] *= g_1
;
329 nfc
->coeffs
[i
+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
330 nfc
->coeffs
[i
+2] = (4.0f
* b_11
) / g_1
;
334 float b_00
= B
[order
][i
] * r
;
335 float g_0
= 1.0f
+ b_00
;
338 nfc
->coeffs
[0] *= g_0
;
339 nfc
->coeffs
[i
+1] = (2.0f
* b_00
) / g_0
;
343 for(i
= 0;i
< (order
-1);i
+= 2)
345 float b_10
= B
[order
][i
] * r
;
346 float b_11
= B
[order
][i
+1] * r
* r
;
347 float g_1
= 1.0f
+ b_10
+ b_11
;
350 nfc
->coeffs
[0] /= g_1
;
351 nfc
->coeffs
[order
+i
+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
352 nfc
->coeffs
[order
+i
+2] = (4.0f
* b_11
) / g_1
;
356 float b_00
= B
[order
][i
] * r
;
357 float g_0
= 1.0f
+ b_00
;
360 nfc
->coeffs
[0] /= g_0
;
361 nfc
->coeffs
[order
+i
+1] = (2.0f
* b_00
) / g_0
;
364 for(i
= 0; i
< MAX_AMBI_ORDER
; i
++)
365 nfc
->history
[i
] = 0.0f
;
368 static void NfcFilterAdjust(NfcFilter
*nfc
, const float distance
)
372 nfc
->coeffs
[0] = nfc
->g
;
374 for(i
= 0;i
< (nfc
->order
-1);i
+= 2)
376 float b_10
= nfc
->b
[i
] / distance
;
377 float b_11
= nfc
->b
[i
+1] / (distance
* distance
);
378 float g_1
= 1.0f
+ b_10
+ b_11
;
380 nfc
->coeffs
[0] *= g_1
;
381 nfc
->coeffs
[i
+1] = ((2.0f
* b_10
) + (4.0f
* b_11
)) / g_1
;
382 nfc
->coeffs
[i
+2] = (4.0f
* b_11
) / g_1
;
386 float b_00
= nfc
->b
[i
] / distance
;
387 float g_0
= 1.0f
+ b_00
;
389 nfc
->coeffs
[0] *= g_0
;
390 nfc
->coeffs
[i
+1] = (2.0f
* b_00
) / g_0
;
394 static float NfcFilterUpdate(const float in
, NfcFilter
*nfc
)
397 float out
= in
* nfc
->coeffs
[0];
399 for(i
= 0;i
< (nfc
->order
-1);i
+= 2)
401 float y
= out
- (nfc
->coeffs
[nfc
->order
+i
+1] * nfc
->history
[i
]) -
402 (nfc
->coeffs
[nfc
->order
+i
+2] * nfc
->history
[i
+1]) + 1.0e-30f
;
403 out
= y
+ (nfc
->coeffs
[i
+1]*nfc
->history
[i
]) + (nfc
->coeffs
[i
+2]*nfc
->history
[i
+1]);
405 nfc
->history
[i
+1] += nfc
->history
[i
];
406 nfc
->history
[i
] += y
;
410 float y
= out
- (nfc
->coeffs
[nfc
->order
+i
+1] * nfc
->history
[i
]) + 1.0e-30f
;
412 out
= y
+ (nfc
->coeffs
[i
+1] * nfc
->history
[i
]);
413 nfc
->history
[i
] += y
;