Skip to content

Commit 12ed49a

Browse files
committed
new Point Source 2.5D WFS handling, old one becomes legacy
-old handling was Spors/Rabenstein, 2D to 2.5D -new one is Delft, 3D to 2.5D
1 parent a15b4f3 commit 12ed49a

File tree

2 files changed

+135
-6
lines changed

2 files changed

+135
-6
lines changed

doc/references.bib

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,29 @@ @article{Borish1984
7070
year = {1984},
7171
doi = {10.1121/1.390983}
7272
}
73+
@article{Firtha2017,
74+
author = {Gergely Firtha AND P{\'e}ter Fiala AND Frank Schultz AND
75+
Sascha Spors},
76+
title = {{Improved Referencing Schemes for 2.5D Wave Field Synthesis
77+
Driving Functions}},
78+
journal = {IEEE/ACM Trans. Audio Speech Language Process.},
79+
volume = {25},
80+
number = {5},
81+
pages = {1117-1127},
82+
year = {2017},
83+
doi = {10.1109/TASLP.2017.2689245}
84+
}
85+
@phdthesis{Start1997,
86+
author = {Evert W. Start},
87+
title = {{Direct Sound Enhancement by Wave Field Synthesis}},
88+
school = {Delft University of Technology},
89+
year = {1997}
90+
}
91+
@phdthesis{Schultz2016,
92+
author = {Frank Schultz},
93+
title = {{Sound Field Synthesis for Line Source Array Applications in
94+
Large-Scale Sound Reinforcement}},
95+
school = {University of Rostock},
96+
year = {2016},
97+
doi = {10.18453/rosdok_id00001765}
98+
}

sfs/mono/wfs.py

Lines changed: 109 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,102 @@ def _point(omega, x0, n0, xs, c=None):
155155

156156

157157
def point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None):
158+
r"""Driving function for 2.5-dimensional WFS of a virtual point source.
159+
160+
.. versionchanged:: 0.5
161+
see notes, old handling of `point_25d()` is now `point_25d_legacy()`
162+
163+
Parameters
164+
----------
165+
omega : float
166+
Angular frequency of point source.
167+
x0 : (N, 3) array_like
168+
Sequence of secondary source positions.
169+
n0 : (N, 3) array_like
170+
Sequence of normal vectors of secondary sources.
171+
xs : (3,) array_like
172+
Position of virtual point source.
173+
xref : (3,) array_like, optional
174+
Reference point xref or contour xref(x0) for amplitude correct
175+
synthesis.
176+
c : float, optional
177+
Speed of sound in m/s.
178+
omalias: float, optional
179+
Angular frequency where spatial aliasing becomes prominent.
180+
181+
Returns
182+
-------
183+
d : (N,) numpy.ndarray
184+
Complex weights of secondary sources.
185+
selection : (N,) numpy.ndarray
186+
Boolean array containing ``True`` or ``False`` depending on
187+
whether the corresponding secondary source is "active" or not.
188+
secondary_source_function : callable
189+
A function that can be used to create the sound field of a
190+
single secondary source. See `sfs.mono.synthesize()`.
191+
192+
Notes
193+
-----
194+
`point_25d()` derives 2.5D WFS from the 3D
195+
Neumann-Rayleigh integral (i.e. the TU Delft approach).
196+
The eq. (3.10), (3.11) in :cite:`Start1997`, equivalent to
197+
Eq. (2.137) in :cite:`Schultz2016`
198+
199+
.. math::
200+
201+
D(\x_0,\w) = \sqrt{8 \pi \, \i\wc}
202+
\sqrt{\frac{|\x_\text{ref}-\x_0| \cdot
203+
|\x_0-\x_\text{s}|}{|\x_\text{ref}-\x_0| + |\x_0-\x_\text{s}|}}
204+
\scalarprod{\frac{\x_0-\x_\text{s}}{|\x_0-\x_\text{s}|}}{\n_0}
205+
\frac{\e{-\i\wc |\x_0-\x_\text{s}|}}{4\pi\,|\x_0-\x_\text{s}|}
206+
207+
is implemented.
208+
The theoretical link of `point_25d()` and `point_25d_legacy()` was
209+
introduced as *unified WFS framework* in :cite:`Firtha2017`.
210+
211+
Examples
212+
--------
213+
.. plot::
214+
:context: close-figs
215+
216+
d, selection, secondary_source = sfs.mono.wfs.point_25d(
217+
omega, array.x, array.n, xs)
218+
normalize_gain = 4 * np.pi * np.linalg.norm(xs)
219+
plot(normalize_gain * d, selection, secondary_source)
220+
221+
"""
222+
x0 = util.asarray_of_rows(x0)
223+
n0 = util.asarray_of_rows(n0)
224+
xs = util.asarray_1d(xs)
225+
xref = util.asarray_1d(xref)
226+
k = util.wavenumber(omega, c)
227+
228+
ds = x0 - xs
229+
dr = xref - x0
230+
s = np.linalg.norm(ds, axis=1)
231+
r = np.linalg.norm(dr, axis=1)
232+
233+
d = (
234+
preeq_25d(omega, omalias, c) *
235+
np.sqrt(8 * np.pi) *
236+
np.sqrt((r * s) / (r + s)) *
237+
inner1d(n0, ds) / s *
238+
np.exp(-1j * k * s) / (4 * np.pi * s))
239+
selection = util.source_selection_point(n0, x0, xs)
240+
return d, selection, secondary_source_point(omega, c)
241+
242+
243+
point_3d = _point
244+
245+
246+
def point_25d_legacy(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None):
158247
r"""Driving function for 2.5-dimensional WFS for a virtual point source.
159248
249+
.. versionadded:: 0.5
250+
`point_25d()` was renamed to `point_25d_legacy()` (and a new
251+
function with the name `point_25d()` was introduced). See notes for
252+
further details.
253+
160254
Parameters
161255
----------
162256
omega : float
@@ -171,6 +265,8 @@ def point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None):
171265
Reference point for synthesized sound field.
172266
c : float, optional
173267
Speed of sound.
268+
omalias: float, optional
269+
Angular frequency where spatial aliasing becomes prominent.
174270
175271
Returns
176272
-------
@@ -185,21 +281,30 @@ def point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None):
185281
186282
Notes
187283
-----
284+
`point_25d_legacy()` derives 2.5D WFS from the 2D
285+
Neumann-Rayleigh integral (i.e. the approach by Rabenstein & Spors), cf.
286+
:cite:`Spors2008`.
287+
188288
.. math::
189289
190290
D(\x_0,\w) = \sqrt{\i\wc |\x_\text{ref}-\x_0|}
191291
\frac{\scalarprod{\x_0-\x_\text{s}}{\n_0}}
192292
{|\x_0-\x_\text{s}|^\frac{3}{2}}
193293
\e{-\i\wc |\x_0-\x_\text{s}|}
194294
295+
The theoretical link of `point_25d()` and `point_25d_legacy()` was
296+
introduced as *unified WFS framework* in :cite:`Firtha2017`.
297+
Also cf. Eq. (2.145)-(2.147) :cite:`Schultz2016`.
298+
195299
Examples
196300
--------
197301
.. plot::
198302
:context: close-figs
199303
200-
d, selection, secondary_source = sfs.mono.wfs.point_25d(
304+
d, selection, secondary_source = sfs.mono.wfs.point_25d_legacy(
201305
omega, array.x, array.n, xs)
202-
plot(d, selection, secondary_source)
306+
normalize_gain = np.linalg.norm(xs)
307+
plot(normalize_gain * d, selection, secondary_source)
203308
204309
"""
205310
x0 = util.asarray_of_rows(x0)
@@ -217,9 +322,6 @@ def point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None):
217322
return d, selection, secondary_source_point(omega, c)
218323

219324

220-
point_3d = _point
221-
222-
223325
def _plane(omega, x0, n0, n=[0, 1, 0], c=None):
224326
r"""Driving function for 2/3-dimensional WFS for a virtual plane wave.
225327
@@ -499,7 +601,8 @@ def preeq_25d(omega, omalias, c):
499601
500602
H(\w) = \begin{cases}
501603
\sqrt{\i \wc} & \text{for } \w \leq \w_\text{alias} \\
502-
\sqrt{\i \frac{\w_\text{alias}}{c}} & \text{for } \w > \w_\text{alias}
604+
\sqrt{\i \frac{\w_\text{alias}}{c}} &
605+
\text{for } \w > \w_\text{alias}
503606
\end{cases}
504607
505608
"""

0 commit comments

Comments
 (0)