Skip to content

Commit 458c58f

Browse files
committed
Implement Spherical Harmonics coefficients tapering;
Adaption of associated Spherical Head Filter; Raise of version number
1 parent 41d77f5 commit 458c58f

File tree

3 files changed

+59
-10
lines changed

3 files changed

+59
-10
lines changed

README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ Render a spherical microphone array measurement for binaural reproduction.
122122
Version history
123123
---------------
124124

125+
*2019-07-11 V0.8*
126+
* Implement Spherical Harmonics coefficients tapering
127+
* Adaption of associated Spherical Head Filter
128+
125129
*2019-06-17 V0.7*
126130
* Implement Bandwidth Extension for Microphone Arrays (BEMA)
127131
* Edit read_miro_struct, named tuple ArraySignal and miro_to_struct.m to load center measurements

sound_field_analysis/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""Version information."""
2-
__version__ = '0.7.1'
2+
__version__ = '0.8'

sound_field_analysis/gen.py

100755100644
Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)