@@ -189,7 +189,7 @@ def radial_filter(orders, freqs, array_configuration, amp_maxdB=40):
189189 return limiting_factor / extrapolation_coeffs
190190
191191
192- def spherical_head_filter (max_order , full_order , kr ):
192+ def spherical_head_filter (max_order , full_order , kr , is_tapering = False ):
193193 """Generate coloration compensation filter of specified maximum SH order.
194194
195195 Parameters
@@ -200,6 +200,8 @@ def spherical_head_filter(max_order, full_order, kr):
200200 Full order necessary to expand sound field in entire modal range
201201 kr : array_like
202202 Vector of corresponding wave numbers
203+ is_tapering : bool, optional
204+ If set, spherical head filter will be adapted applying a Hann window, according to [2]
203205
204206 Returns
205207 -------
@@ -208,33 +210,43 @@ def spherical_head_filter(max_order, full_order, kr):
208210
209211 References
210212 ----------
211- .. [1] Ben-Hur, Z., Brinkmann, F., Sheaffer, J., et al. (2017). Spectral equalization in binaural signals
212- represented by order-truncated spherical harmonics. The Journal of the Acoustical Society of America.
213+ .. [1] Ben-Hur, Z., Brinkmann, F., Sheaffer, J., et al. (2017). "Spectral equalization in binaural signals
214+ represented by order-truncated spherical harmonics. The Journal of the Acoustical Society of America".
215+
216+ .. [2] Hold, Christoph, Hannes Gamper, Ville Pulkki, Nikunj Raghuvanshi, and Ivan J. Tashev (2019). “Improving
217+ Binaural Ambisonics Decoding by Spherical Harmonics Domain Tapering and Coloration Compensation.”
213218 """
214219
215- def pressure_on_sphere (max_order , kr ):
220+ # noinspection PyShadowingNames
221+ def pressure_on_sphere (max_order , kr , taper_weights = None ):
216222 """
217223 Calculate the diffuse field pressure frequency response of a spherical scatterer, up to the specified SH order.
224+ If tapering weights are specified, pressure on the sphere function will be adapted.
218225 """
226+ if taper_weights is None :
227+ taper_weights = _np .ones (max_order + 1 ) # no weighting
228+
219229 p = _np .zeros_like (kr )
220230 for order in range (max_order + 1 ):
221231 # Calculate mode strength b_n(kr) for an incident plane wave on sphere according to [1, Eq.(9)]
222232 b_n = 4 * _np .pi * 1j ** order * (spherical_jn (order , kr ) -
223233 (spherical_jn (order , kr , True ) /
224234 dsphankel2 (order , kr )) * sphankel2 (order , kr ))
225- p += (2 * order + 1 ) * _np .abs (b_n ) ** 2
235+ p += (2 * order + 1 ) * _np .abs (b_n ) ** 2 * taper_weights [ order ]
226236
227237 # according to [1, Eq.(11)]
228238 return 1 / (4 * _np .pi ) * _np .sqrt (p )
229239
230240 # according to [1, Eq.(12)].
231- G_SHF = pressure_on_sphere (full_order , kr ) / pressure_on_sphere (max_order , kr )
241+ taper_weights = tapering_window (max_order ) if is_tapering else None
242+ G_SHF = pressure_on_sphere (full_order , kr ) / pressure_on_sphere (max_order , kr , taper_weights )
243+
232244 G_SHF [G_SHF == 0 ] = 1e-12 # catch zeros
233245 G_SHF [_np .isnan (G_SHF )] = 1 # catch NaNs
234246 return G_SHF
235247
236248
237- def spherical_head_filter_spec (max_order , NFFT , fs , radius , amp_maxdB = None ):
249+ def spherical_head_filter_spec (max_order , NFFT , fs , radius , amp_maxdB = None , is_tapering = False ):
238250 """Generate NFFT/2 + 1 coloration compensation filter of specified maximum SH order for frequencies 0:fs/2, wraps
239251 spherical_head_filter().
240252
@@ -250,21 +262,23 @@ def spherical_head_filter_spec(max_order, NFFT, fs, radius, amp_maxdB=None):
250262 Array radius
251263 amp_maxdB : int, optional
252264 Maximum modal amplification limit in dB [Default: None]
265+ is_tapering : bool, optional
266+ If true spherical head filter will be adapted for SH tapering. [Default: False]
253267
254268 Returns
255269 -------
256270 G_SHF : array_like
257271 Vector of frequency domain filter of shape [NFFT / 2 + 1]
258272 """
259273 # frequency support vector & corresponding wave numbers k
260- freqs = _np .linspace (0 , fs / 2 , NFFT // 2 + 1 )
274+ freqs = _np .linspace (0 , fs / 2 , int ( NFFT / 2 + 1 ) )
261275 kr_SHF = kr (freqs , radius )
262276
263277 # calculate SH order necessary to expand sound field in entire modal range
264278 order_full = int (_np .ceil (kr_SHF [- 1 ]))
265279
266280 # calculate filter
267- G_SHF = spherical_head_filter (max_order , order_full , kr_SHF )
281+ G_SHF = spherical_head_filter (max_order , order_full , kr_SHF , is_tapering = is_tapering )
268282
269283 # filter limiting
270284 if amp_maxdB :
@@ -430,3 +444,34 @@ def spherical_noise(gridData=None, order_max=8, spherical_harmonic_bases=None):
430444 order_max = _np .int (_np .sqrt (spherical_harmonic_bases .shape [1 ]) - 1 )
431445 return _np .inner (spherical_harmonic_bases ,
432446 _np .random .randn ((order_max + 1 ) ** 2 ) + 1j * _np .random .randn ((order_max + 1 ) ** 2 ))
447+
448+
449+ def tapering_window (max_order ):
450+ """Design tapering window with cosine slope for orders greater than 3.
451+
452+ Parameters
453+ ----------
454+ max_order : int
455+ Maximum SH order
456+
457+ Returns
458+ -------
459+ hann_window_half : array_like
460+ Tapering window with cosine slope for orders greater than 3. Ones in case of maximum SH order being smaller than
461+ 3.
462+
463+ References
464+ ----------
465+ .. [1] Hold, Christoph, Hannes Gamper, Ville Pulkki, Nikunj Raghuvanshi, and Ivan J. Tashev (2019). “Improving
466+ Binaural Ambisonics Decoding by Spherical Harmonics Domain Tapering and Coloration Compensation.”
467+ """
468+ weights = _np .ones (max_order + 1 )
469+
470+ if max_order >= 3 :
471+ hann_window = _np .hanning (2 * ((max_order + 1 ) // 2 ) + 1 )
472+ weights [- ((max_order - 1 ) // 2 ):] = hann_window [- ((max_order + 1 ) // 2 ):- 1 ]
473+ else :
474+ import sys
475+ print ('[WARNING] SH maximum order is smaller than 3. No tapering will be used.' , file = sys .stderr )
476+
477+ return weights
0 commit comments