Sound Field Synthesis Toolbox for Python¶
Python implementation of the Sound Field Synthesis Toolbox.
 Documentation:
 http://sfs.rtfd.org/
 Code:
 http://github.com/sfstoolbox/sfspython/
 Python Package Index:
 http://pypi.python.org/pypi/sfs/
Requirements¶
Obviously, you’ll need Python. We normally use Python 3.x, but it should also work with Python 2.x. NumPy and SciPy are needed for the calcuations. If you also want to plot the resulting sound fields, you’ll need matplotlib.
Instead of installing all of them separately, you should probably get a Python distribution that already includes everything, e.g. Anaconda.
Installation¶
Once you have installed the abovementioned dependencies, you can use pip to download and install the latest release with a single command:
pip install sfs user
If you want to install it systemwide for all users (assuming you have the
necessary rights), you can just drop the user
option.
To uninstall, use:
pip uninstall sfs
If you want to keep up with the latest and greatest development version you should get it from Github:
git clone https://github.com/sfstoolbox/sfspython.git
cd sfspython
python setup.py develop user
How to Get Started¶
Various examples are located in the directory examples/
 sound_field_synthesis.py:
Illustrates the general usage of the toolbox
 horizontal_plane_arrays.py:
Computes the sound fields for various techniques, virtual sources and loudspeaker array configurations
 soundfigures.py:
Illustrates the synthesis of sound figures with Wave Field Synthesis
API Documentation¶
Loudspeaker Arrays¶
Compute positions of various secondary source distributions.
import sfs
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = 8, 4 # inch
plt.rcParams['axes.grid'] = True

sfs.array.
linear
(N, spacing, center=[0, 0, 0], n0=[1, 0, 0])[source]¶ Linear secondary source distribution.
Parameters:  N (int) – Number of loudspeakers.
 spacing (float) – Distance (in metres) between loudspeakers.
 center ((3,) array_like, optional) – Coordinates of array center.
 n0 ((3,) array_like, optional) – Normal vector of array.
Returns:  positions ((N, 3) numpy.ndarray) – Positions of secondary sources
 directions ((N, 3) numpy.ndarray) – Orientations (normal vectors) of secondary sources
 weights ((N,) numpy.ndarray) – Weights of secondary sources
Example
x0, n0, a0 = sfs.array.linear(16, 0.2, n0=[0, 1, 0]) sfs.plot.loudspeaker_2d(x0, n0, a0) plt.axis('equal')

sfs.array.
linear_nested
(N, dx1, dx2, center=[0, 0, 0], n0=[1, 0, 0])[source]¶ Nested linear secondary source distribution.
Example
x0, n0, a0 = sfs.array.linear_nested(16, 0.15, 0.2, n0=[0, 1, 0]) sfs.plot.loudspeaker_2d(x0, n0, a0) plt.axis('equal')

sfs.array.
linear_random
(N, dy1, dy2, center=[0, 0, 0], n0=[1, 0, 0])[source]¶ Randomly sampled linear array.
Example
x0, n0, a0 = sfs.array.linear_random(12, 0.15, 0.4, n0=[0, 1, 0]) sfs.plot.loudspeaker_2d(x0, n0, a0) plt.axis('equal')

sfs.array.
circular
(N, R, center=[0, 0, 0])[source]¶ Circular secondary source distribution parallel to the xyplane.
Example
x0, n0, a0 = sfs.array.circular(16, 1) sfs.plot.loudspeaker_2d(x0, n0, a0, size=0.2, show_numbers=True) plt.axis('equal')

sfs.array.
rectangular
(Nx, dx, Ny, dy, center=[0, 0, 0], n0=None)[source]¶ Rectangular secondary source distribution.
Example
x0, n0, a0 = sfs.array.rectangular(8, 0.2, 4, 0.2) sfs.plot.loudspeaker_2d(x0, n0, a0, show_numbers=True) plt.axis('equal')

sfs.array.
rounded_edge
(Nxy, Nr, dx, center=[0, 0, 0], n0=None)[source]¶ Array along the xyaxis with rounded edge at the origin.
Parameters:  Nxy (int) – Number of secondary sources along x and yaxis.
 Nr (int) – Number of secondary sources in rounded edge. Radius of edge is adjusted to equdistant sampling along entire array.
 center ((3,) array_like, optional) – Position of edge.
 n0 ((3,) array_like, optional) – Normal vector of array. Default orientation is along xyaxis.
Returns:  positions ((N, 3) numpy.ndarray) – Positions of secondary sources
 directions ((N, 3) numpy.ndarray) – Orientations (normal vectors) of secondary sources
 weights ((N,) numpy.ndarray) – Integration weights of secondary sources
Example
x0, n0, a0 = sfs.array.rounded_edge(8, 5, 0.2) sfs.plot.loudspeaker_2d(x0, n0, a0) plt.axis('equal')

sfs.array.
planar
(Ny, dy, Nz, dz, center=[0, 0, 0], n0=None)[source]¶ Planar secondary source distribtion.

sfs.array.
cube
(Nx, dx, Ny, dy, Nz, dz, center=[0, 0, 0], n0=None)[source]¶ Cube shaped secondary source distribtion.

sfs.array.
sphere_load
(fname, radius, center=[0, 0, 0])[source]¶ Spherical secondary source distribution loaded from datafile.
ASCII Format (see MATLAB SFS Toolbox) with 4 numbers (3 position, 1 weight) per secondary source located on the unit circle.

sfs.array.
load
(fname, center=[0, 0, 0], n0=None)[source]¶ Load secondary source positions from datafile.
Comma Seperated Values (CSV) format with 7 values (3 positions, 3 directions, 1 weight) per secondary source.
Tapering¶
Weights (tapering) for the driving function.
Monochromatic Sources¶
Compute the sound field generated by a sound source.
import sfs
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = 8, 4 # inch
x0 = 1.5, 1, 0
f = 500 # Hz
omega = 2 * np.pi * f
grid = sfs.util.xyz_grid([2, 3], [1, 2], 0, spacing=0.02)

sfs.mono.source.
point
(omega, x0, n0, grid, c=None)[source]¶ Point source.
1 e^(j w/c xx0) G(xx0, w) =   4pi xx0
Examples
p_point = sfs.mono.source.point(omega, x0, None, grid) sfs.plot.soundfield(p_point, grid) plt.title("Point Source at {} m".format(x0))
Normalization ... multiply by \(4\pi\) ...
p_point *= 4 * np.pi sfs.plot.soundfield(p_point, grid) plt.title("Point Source at {} m (normalized)".format(x0))

sfs.mono.source.
point_modal
(omega, x0, n0, grid, L, N=None, deltan=0, c=None)[source]¶ Point source in a rectangular room using a modal room model.
Parameters:  omega (float) – Frequency of source.
 x0 ((3,) array_like) – Position of source.
 n0 ((3,) array_like) – Normal vector (direction) of source (only required for compatibility).
 grid (triple of numpy.ndarray) – The grid that is used for the sound field calculations.
 L ((3,) array_like) – Dimensionons of the rectangular room.
 N ((3,) array_like or int, optional) – Combination of modal orders in the threespatial dimensions to calculate the sound field for or maximum order for all dimensions. If not given, the maximum modal order is approximately determined and the sound field is computed up to this maximum order.
 deltan (float, optional) – Absorption coefficient of the walls.
 c (float, optional) – Speed of sound.
Returns: numpy.ndarray – Sound pressure at positions given by grid.

sfs.mono.source.
line
(omega, x0, n0, grid, c=None)[source]¶ Line source parallel to the zaxis.
Note: third component of x0 is ignored.
(2) G(xx0, w) = j/4 H0 (w/c xx0)
Examples
p_line = sfs.mono.source.line(omega, x0, None, grid) sfs.plot.soundfield(p_line, grid) plt.title("Line Source at {} m".format(x0[:2]))
Normalization ...
p_line *= np.exp(1j*7*np.pi/4) / np.sqrt(1/(8*np.pi*omega/sfs.defs.c)) sfs.plot.soundfield(p_line, grid) plt.title("Line Source at {} m (normalized)".format(x0[:2]))

sfs.mono.source.
line_dipole
(omega, x0, n0, grid, c=None)[source]¶ Line source with dipole characteristics parallel to the zaxis.
Note: third component of x0 is ignored.
(2) G(xx0, w) = jk/4 H1 (w/c xx0) cos(phi)

sfs.mono.source.
plane
(omega, x0, n0, grid, c=None)[source]¶ Plane wave.
G(x, w) = e^(i w/c n x)
Example
direction = 45 # degree n0 = sfs.util.direction_vector(np.radians(direction)) p_plane = sfs.mono.source.plane(omega, x0, n0, grid) sfs.plot.soundfield(p_plane, grid); plt.title("Plane wave with direction {} degree".format(direction))
Monochromatic Driving Functions¶
Compute driving functions for various systems.

sfs.mono.drivingfunction.
wfs_2d_line
(omega, x0, n0, xs, c=None)[source]¶ Line source by 2dimensional WFS.
D(x0,k) = j k (x0xs) n0 / x0xs * H1(k x0xs)

sfs.mono.drivingfunction.
wfs_2d_point
(omega, x0, n0, xs, c=None)¶ Point source by two or threedimensional WFS.
(x0xs) n0 D(x0,k) = j k  e^(j k x0xs) x0xs^(3/2)

sfs.mono.drivingfunction.
wfs_25d_point
(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None)[source]¶ Point source by 2.5dimensional WFS.
____________ (x0xs) n0 D(x0,k) = \j k xrefx0  e^(j k x0xs) x0xs^(3/2)

sfs.mono.drivingfunction.
wfs_3d_point
(omega, x0, n0, xs, c=None)¶ Point source by two or threedimensional WFS.
(x0xs) n0 D(x0,k) = j k  e^(j k x0xs) x0xs^(3/2)

sfs.mono.drivingfunction.
wfs_2d_plane
(omega, x0, n0, n=[0, 1, 0], c=None)¶ Plane wave by two or threedimensional WFS.
Eq.(17) from [Spors et al, 2008]:
D(x0,k) = j k n n0 e^(j k n x0)

sfs.mono.drivingfunction.
wfs_25d_plane
(omega, x0, n0, n=[0, 1, 0], xref=[0, 0, 0], c=None, omalias=None)[source]¶ Plane wave by 2.5dimensional WFS.
____________ D_2.5D(x0,w) = \j k xrefx0 n n0 e^(j k n x0)

sfs.mono.drivingfunction.
wfs_3d_plane
(omega, x0, n0, n=[0, 1, 0], c=None)¶ Plane wave by two or threedimensional WFS.
Eq.(17) from [Spors et al, 2008]:
D(x0,k) = j k n n0 e^(j k n x0)

sfs.mono.drivingfunction.
delay_3d_plane
(omega, x0, n0, n=[0, 1, 0], c=None)[source]¶ Plane wave by simple delay of secondary sources.

sfs.mono.drivingfunction.
source_selection_plane
(n0, n)[source]¶ Secondary source selection for a plane wave.
Eq.(13) from [Spors et al, 2008]

sfs.mono.drivingfunction.
source_selection_point
(n0, x0, xs)[source]¶ Secondary source selection for a point source.
Eq.(15) from [Spors et al, 2008]

sfs.mono.drivingfunction.
nfchoa_2d_plane
(omega, x0, r0, n=[0, 1, 0], c=None)[source]¶ Point source by 2.5dimensional WFS.

sfs.mono.drivingfunction.
nfchoa_25d_point
(omega, x0, r0, xs, c=None)[source]¶ Point source by 2.5dimensional WFS.
__ (2) 1 \ hm (w/c r) D(phi0,w) =  /__  e^(i m (phi0phi)) 2pi r0 m=N..N (2) hm (w/c r0)

sfs.mono.drivingfunction.
nfchoa_25d_plane
(omega, x0, r0, n=[0, 1, 0], c=None)[source]¶ Plane wave by 2.5dimensional WFS.
__ 2i \ i^m D_25D(phi0,w) =  /__  e^(i m (phi0phi_pw) ) r0 m=N..N (2) w/c hm (w/c r0)

sfs.mono.drivingfunction.
sdm_2d_line
(omega, x0, n0, xs, c=None)[source]¶ Line source by twodimensional SDM.
The secondary sources have to be located on the xaxis (y0=0). Derived from [Spors 2009, 126th AES Convention], Eq.(9), Eq.(4):
D(x0,k) =

sfs.mono.drivingfunction.
sdm_2d_plane
(omega, x0, n0, n=[0, 1, 0], c=None)[source]¶ Plane wave by twodimensional SDM.
The secondary sources have to be located on the xaxis (y0=0). Derived from [Ahrens 2011, Springer], Eq.(3.73), Eq.(C.5), Eq.(C.11):
D(x0,k) = kpw,y * e^(j*kpw,x*x)
Monochromatic Sound Fields¶
Computation of synthesized sound fields.
Plotting¶
Plot sound fields etc.

sfs.plot.
virtualsource_2d
(xs, ns=None, type='point', ax=None)[source]¶ Draw position/orientation of virtual source.

sfs.plot.
loudspeaker_2d
(x0, n0, a0=0.5, size=0.08, show_numbers=False, grid=None, ax=None)[source]¶ Draw loudspeaker symbols at given locations and angles.
Parameters:  x0 ((N, 3) array_like) – Loudspeaker positions.
 n0 ((N, 3) or (3,) array_like) – Normal vector(s) of loudspeakers.
 a0 (float or (N,) array_like, optional) – Weighting factor(s) of loudspeakers.
 size (float, optional) – Size of loudspeakers in metres.
 show_numbers (bool, optional) – If
True
, loudspeaker numbers are shown.  grid (triple of numpy.ndarray, optional) – If specified, only loudspeakers within the grid are shown.
 ax (Axes object, optional) – The loudspeakers are plotted into this
Axes
object or – if not specified – into the current axes.

sfs.plot.
loudspeaker_3d
(x0, n0, a0=None, w=0.08, h=0.08)[source]¶ Plot positions and normals of a 3D secondary source distribution.
Utilities¶
Various utility functions.

sfs.util.
direction_vector
(alpha, beta=1.5707963267948966)[source]¶ Compute normal vector from azimuth, colatitude.

sfs.util.
asarray_1d
(a, **kwargs)[source]¶ Squeeze the input and check if the result is onedimensional.
Returns a converted to a
numpy.ndarray
and stripped of all singleton dimensions. Scalars are “upgraded” to 1D arrays. The result must have exactly one dimension. If not, an error is raised.

sfs.util.
asarray_of_rows
(a, **kwargs)[source]¶ Convert to 2D array, turn column vector into row vector.
Returns a converted to a
numpy.ndarray
and stripped of all singleton dimensions. If the result has exactly one dimension, it is reshaped into a 2D row vector.

sfs.util.
asarray_of_arrays
(a, **kwargs)[source]¶ Convert the input to an array consisting of arrays.
A onedimensional
numpy.ndarray
with dtype=object is returned, containing the elements of a asnumpy.ndarray
(whose dtype and other options can be specified with **kwargs).

sfs.util.
strict_arange
(start, stop, step=1, endpoint=False, dtype=None, **kwargs)[source]¶ Like
numpy.arange()
, but compensating numeric errors.Unlike
numpy.arange()
, but similar tonumpy.linspace()
, providing endpoint=True includes both endpoints.Parameters: start, stop, step, dtype – See
numpy.arange()
.endpoint – See
numpy.linspace()
.Note
With endpoint=True, the difference between start and end value must be an integer multiple of the corresponding spacing value!
**kwargs – All further arguments are forwarded to
numpy.isclose()
.
Returns: numpy.ndarray – Array of evenly spaced values. See
numpy.arange()
.

sfs.util.
xyz_grid
(x, y, z, spacing, endpoint=True, **kwargs)[source]¶ Create a grid with given range and spacing.
Parameters:  x, y, z (float or pair of float) – Inclusive range of the respective coordinate or a single value if only a slice along this dimension is needed.
 spacing (float or triple of float) – Grid spacing. If a single value is specified, it is used for all dimensions, if multiple values are given, one value is used per dimension. If a dimension (x, y or z) has only a single value, the corresponding spacing is ignored.
 endpoint (bool, optional) – If
True
(the default), the endpoint of each range is included in the grid. UseFalse
to get a result similar tonumpy.arange()
. Seestrict_arange()
.  **kwargs – All further arguments are forwarded to
strict_arange()
.
Returns: list of numpy.ndarrays – A grid that can be used for sound field calculations.
See also
Version History¶
 Version 0.1.1 (20151008):
 Fix missing
sfs.mono
subpackage in PyPI packages
 Fix missing
 Version 0.1.0 (20150922):
 Initial release.