Sound Field Synthesis (SFS) Toolbox for Python¶
A Python library for creating numercial simulations of sound field synthesis methods like Wave Field Synthesis (WFS) or Near-Field Compensated Higher Order Ambisonics (NFC-HOA).
- Documentation:
- Source code and issue tracker:
- License:
MIT – see the file
LICENSE
for details.- Quick start:
Install Python 3, NumPy, SciPy and Matplotlib
python3 -m pip install sfs --user
Check out the examples in the documentation
More information about the underlying theory can be found at https://sfs.readthedocs.io/. There is also a Sound Field Synthesis Toolbox for Octave/Matlab, see https://sfs-matlab.readthedocs.io/.
Installation¶
Requirements¶
Obviously, you’ll need Python. More specifically, you’ll need Python 3. NumPy and SciPy are needed for the calculations. If you want to use the provided functions for plotting sound fields, you’ll need Matplotlib. However, since all results are provided as plain NumPy arrays, you should also be able to use any plotting library of your choice to visualize the sound fields.
Instead of installing all of the requirements separately, you should probably get a Python distribution that already includes everything, e.g. Anaconda.
Installation¶
Once you have installed the above-mentioned dependencies, you can use pip to download and install the latest release of the Sound Field Synthesis Toolbox with a single command:
python3 -m pip install sfs --user
If you want to install it system-wide for all users (assuming you have the
necessary rights), you can just drop the --user
option.
To un-install, use:
python3 -m pip uninstall sfs
If you want to install the latest development version of the SFS Toolbox, have a look at Contributing.
Examples¶
You can play with the Jupyter notebooks (without having to install anything)
by clicking on the respective example page.
This page was generated from /home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/0.6.2/doc/examples/sound-field-synthesis.ipynb.
Interactive online version:
Sound Field Synthesis¶
Illustrates the usage of the SFS toolbox for the simulation of different sound field synthesis methods.
[1]:
import numpy as np
import matplotlib.pyplot as plt
import sfs
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/conda/0.6.2/lib/python3.9/site-packages/traitlets/traitlets.py:3030: FutureWarning: --rc={'figure.dpi': 96} for dict-traits is deprecated in traitlets 5.0. You can pass --rc <key=value> ... multiple times to add items to a dict.
warn(
[2]:
# Simulation parameters
number_of_secondary_sources = 56
frequency = 680 # in Hz
pw_angle = 30 # traveling direction of plane wave in degree
xs = [-2, -1, 0] # position of virtual point source in m
grid = sfs.util.xyz_grid([-2, 2], [-2, 2], 0, spacing=0.02)
omega = 2 * np.pi * frequency # angular frequency
npw = sfs.util.direction_vector(np.radians(pw_angle)) # normal vector of plane wave
Define a helper function for synthesize and plot the sound field from the given driving signals.
[3]:
def sound_field(d, selection, secondary_source, array, grid, tapering=True):
if tapering:
tapering_window = sfs.tapering.tukey(selection, alpha=0.3)
else:
tapering_window = sfs.tapering.none(selection)
p = sfs.fd.synthesize(d, tapering_window, array, secondary_source, grid=grid)
sfs.plot2d.amplitude(p, grid, xnorm=[0, 0, 0])
sfs.plot2d.loudspeakers(array.x, array.n, tapering_window)
Circular loudspeaker arrays¶
In the following we show different sound field synthesis methods applied to a circular loudspeaker array.
[4]:
radius = 1.5 # in m
array = sfs.array.circular(number_of_secondary_sources, radius)
Wave Field Synthesis (WFS)¶
Plane wave¶
[5]:
d, selection, secondary_source = sfs.fd.wfs.plane_25d(omega, array.x, array.n, n=npw)
sound_field(d, selection, secondary_source, array, grid)
Point source¶
[6]:
d, selection, secondary_source = sfs.fd.wfs.point_25d(omega, array.x, array.n, xs)
sound_field(d, selection, secondary_source, array, grid)
Near-Field Compensated Higher Order Ambisonics (NFC-HOA)¶
Plane wave¶
[7]:
d, selection, secondary_source = sfs.fd.nfchoa.plane_25d(omega, array.x, radius, n=npw)
sound_field(d, selection, secondary_source, array, grid, tapering=False)
Point source¶
[8]:
d, selection, secondary_source = sfs.fd.nfchoa.point_25d(omega, array.x, radius, xs)
sound_field(d, selection, secondary_source, array, grid, tapering=False)
Linear loudspeaker array¶
In the following we show different sound field synthesis methods applied to a linear loudspeaker array.
[9]:
spacing = 0.07 # in m
array = sfs.array.linear(number_of_secondary_sources, spacing,
center=[0, -0.5, 0], orientation=[0, 1, 0])
Wave Field Synthesis (WFS)¶
Plane wave¶
[10]:
d, selection, secondary_source = sfs.fd.wfs.plane_25d(omega, array.x, array.n, npw)
sound_field(d, selection, secondary_source, array, grid)
Point source¶
[11]:
d, selection, secondary_source = sfs.fd.wfs.point_25d(omega, array.x, array.n, xs)
sound_field(d, selection, secondary_source, array, grid)
This page was generated from /home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/0.6.2/doc/examples/modal-room-acoustics.ipynb.
Interactive online version:
Modal Room Acoustics¶
[1]:
import numpy as np
import matplotlib.pyplot as plt
import sfs
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/conda/0.6.2/lib/python3.9/site-packages/traitlets/traitlets.py:3030: FutureWarning: --rc={'figure.dpi': 96} for dict-traits is deprecated in traitlets 5.0. You can pass --rc <key=value> ... multiple times to add items to a dict.
warn(
[2]:
%matplotlib inline
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/conda/0.6.2/lib/python3.9/site-packages/traitlets/traitlets.py:3030: FutureWarning: --rc={'figure.dpi': 96} for dict-traits is deprecated in traitlets 5.0. You can pass --rc <key=value> ... multiple times to add items to a dict.
warn(
[3]:
x0 = 1, 3, 1.80 # source position
L = 6, 6, 3 # dimensions of room
deltan = 0.01 # absorption factor of walls
N = 20 # maximum order of modes
You can experiment with different combinations of modes:
[4]:
#N = [[1], 0, 0]
Sound Field for One Frequency¶
[5]:
f = 500 # frequency
omega = 2 * np.pi * f # angular frequency
[6]:
grid = sfs.util.xyz_grid([0, L[0]], [0, L[1]], L[2] / 2, spacing=.1)
[7]:
p = sfs.fd.source.point_modal(omega, x0, grid, L, N=N, deltan=deltan)
For now, we apply an arbitrary scaling factor to make the plot look good
TODO: proper normalization
[8]:
p *= 0.05
[9]:
sfs.plot2d.amplitude(p, grid);
[9]:
<matplotlib.image.AxesImage at 0x7fc1a4c03a00>
Frequency Response at One Point¶
[10]:
f = np.linspace(20, 200, 180) # frequency
omega = 2 * np.pi * f # angular frequency
receiver = 1, 1, 1.8
p = [sfs.fd.source.point_modal(om, x0, receiver, L, N=N, deltan=deltan)
for om in omega]
plt.plot(f, sfs.util.db(p))
plt.xlabel('frequency / Hz')
plt.ylabel('level / dB')
plt.grid()
This page was generated from /home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/0.6.2/doc/examples/mirror-image-source-model.ipynb.
Interactive online version:
Mirror Image Sources and the Sound Field in a Rectangular Room¶
[1]:
import matplotlib.pyplot as plt
import numpy as np
import sfs
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/conda/0.6.2/lib/python3.9/site-packages/traitlets/traitlets.py:3030: FutureWarning: --rc={'figure.dpi': 96} for dict-traits is deprecated in traitlets 5.0. You can pass --rc <key=value> ... multiple times to add items to a dict.
warn(
[2]:
L = 2, 2.7, 3 # room dimensions
x0 = 1.2, 1.7, 1.5 # source position
max_order = 2 # maximum order of image sources
coeffs = .8, .8, .6, .6, .7, .7 # wall reflection coefficients
2D Mirror Image Sources¶
[3]:
xs, wall_count = sfs.util.image_sources_for_box(x0[0:2], L[0:2], max_order)
source_strength = np.prod(coeffs[0:4]**wall_count, axis=1)
[4]:
from matplotlib.patches import Rectangle
[5]:
fig, ax = plt.subplots()
ax.scatter(*xs.T, source_strength * 20)
ax.add_patch(Rectangle((0, 0), L[0], L[1], fill=False))
ax.set_xlabel('x / m')
ax.set_ylabel('y / m')
ax.axis('equal');
[5]:
(-3.1999999999999997, 5.6000000000000005, -4.24, 7.640000000000001)
Monochromatic Sound Field¶
[6]:
omega = 2 * np.pi * 1000 # angular frequency
[7]:
grid = sfs.util.xyz_grid([0, L[0]], [0, L[1]], 1.5, spacing=0.02)
P = sfs.fd.source.point_image_sources(omega, x0, grid, L,
max_order=max_order, coeffs=coeffs)
[8]:
sfs.plot2d.amplitude(P, grid, xnorm=[L[0]/2, L[1]/2, L[2]/2]);
[8]:
<matplotlib.image.AxesImage at 0x7f6ac8adb190>
Spatio-temporal Impulse Response¶
[9]:
fs = 44100 # sample rate
signal = [1, 0, 0], fs
[10]:
grid = sfs.util.xyz_grid([0, L[0]], [0, L[1]], 1.5, spacing=0.005)
p = sfs.td.source.point_image_sources(x0, signal, 0.004, grid, L, max_order,
coeffs=coeffs)
[11]:
sfs.plot2d.level(p, grid)
sfs.plot2d.virtualsource(x0)
This page was generated from /home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/0.6.2/doc/examples/animations-pulsating-sphere.ipynb.
Interactive online version:
Animations of a Pulsating Sphere¶
[1]:
import sfs
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import HTML
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/conda/0.6.2/lib/python3.9/site-packages/traitlets/traitlets.py:3030: FutureWarning: --rc={'figure.dpi': 96} for dict-traits is deprecated in traitlets 5.0. You can pass --rc <key=value> ... multiple times to add items to a dict.
warn(
In this example, the sound field of a pulsating sphere is visualized. Different acoustic variables, such as sound pressure, particle velocity, and particle displacement, are simulated. The first two quantities are computed with
while the last one can be obtained by using
which converts the particle velocity into displacement.
A couple of additional functions are implemented in
in order to help creating animating pictures, which is fun!
[2]:
import animations_pulsating_sphere as animation
[3]:
# Pulsating sphere
center = [0, 0, 0]
radius = 0.25
amplitude = 0.05
f = 1000 # frequency
omega = 2 * np.pi * f # angular frequency
# Axis limits
figsize = (6, 6)
xmin, xmax = -1, 1
ymin, ymax = -1, 1
# Animations
frames = 20 # frames per period
Particle Displacement¶
[4]:
grid = sfs.util.xyz_grid([xmin, xmax], [ymin, ymax], 0, spacing=0.025)
ani = animation.particle_displacement(
omega, center, radius, amplitude, grid, frames, figsize, c='Gray')
plt.close()
HTML(ani.to_jshtml())
[4]:
Click the arrow button to start the animation. to_jshtml()
allows you to play with the animation, e.g. speed up/down the animation (+/- button). Try to reverse the playback by clicking the left arrow. You’ll see a sound sink.
You can also show the animation by using to_html5_video()
. See the documentation for more detail.
Of course, different types of grid can be chosen. Below is the particle animation using the same parameters but with a hexagonal grid.
[5]:
def hex_grid(xlim, ylim, hex_edge, align='horizontal'):
if align is 'vertical':
umin, umax = ylim
vmin, vmax = xlim
else:
umin, umax = xlim
vmin, vmax = ylim
du = np.sqrt(3) * hex_edge
dv = 1.5 * hex_edge
num_u = int((umax - umin) / du)
num_v = int((vmax - vmin) / dv)
u, v = np.meshgrid(np.linspace(umin, umax, num_u),
np.linspace(vmin, vmax, num_v))
u[::2] += 0.5 * du
if align is 'vertical':
grid = v, u, 0
elif align is 'horizontal':
grid = u, v, 0
return grid
<>:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
<>:16: SyntaxWarning: "is" with a literal. Did you mean "=="?
<>:18: SyntaxWarning: "is" with a literal. Did you mean "=="?
<>:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
<>:16: SyntaxWarning: "is" with a literal. Did you mean "=="?
<>:18: SyntaxWarning: "is" with a literal. Did you mean "=="?
<ipython-input-1-17990c1f260e>:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
if align is 'vertical':
<ipython-input-1-17990c1f260e>:16: SyntaxWarning: "is" with a literal. Did you mean "=="?
if align is 'vertical':
<ipython-input-1-17990c1f260e>:18: SyntaxWarning: "is" with a literal. Did you mean "=="?
elif align is 'horizontal':
[6]:
grid = hex_grid([xmin, xmax], [ymin, ymax], 0.0125, 'vertical')
ani = animation.particle_displacement(
omega, center, radius, amplitude, grid, frames, figsize, c='Gray')
plt.close()
HTML(ani.to_jshtml())
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/0.6.2/doc/examples/animations_pulsating_sphere.py:18: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
scat = sfs.plot2d.particles(grid + displacement, **kwargs)
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/0.6.2/doc/examples/animations_pulsating_sphere.py:21: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
position = (grid + displacement * phasor**i).apply(np.real)
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/0.6.2/doc/examples/animations_pulsating_sphere.py:21: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
position = (grid + displacement * phasor**i).apply(np.real)
[6]:
Another one using a random grid.
[7]:
grid = [np.random.uniform(xmin, xmax, 4000),
np.random.uniform(ymin, ymax, 4000), 0]
ani = animation.particle_displacement(
omega, center, radius, amplitude, grid, frames, figsize, c='Gray')
plt.close()
HTML(ani.to_jshtml())
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/0.6.2/doc/examples/animations_pulsating_sphere.py:18: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
scat = sfs.plot2d.particles(grid + displacement, **kwargs)
/home/docs/checkouts/readthedocs.org/user_builds/sfs-python/checkouts/0.6.2/doc/examples/animations_pulsating_sphere.py:21: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
position = (grid + displacement * phasor**i).apply(np.real)
[7]:
Each grid has its strengths and weaknesses. Please refer to the on-line discussion.
Particle Velocity¶
[8]:
amplitude = 1e-3
grid = sfs.util.xyz_grid([xmin, xmax], [ymin, ymax], 0, spacing=0.04)
ani = animation.particle_velocity(
omega, center, radius, amplitude, grid, frames, figsize)
plt.close()
HTML(ani.to_jshtml())
[8]:
Please notice that the amplitude of the pulsating motion is adjusted so that the arrows are neither too short nor too long. This kind of compromise is inevitable since
thus the absolute value of particle velocity is usually much larger than that of amplitude. It should be also kept in mind that the hole in the middle does not visualizes the exact motion of the pulsating sphere. According to the above equation, the actual amplitude should be much smaller than the arrow lengths. The changing rate of its size is also two times higher than the original frequency.
Sound Pressure¶
[9]:
amplitude = 0.05
impedance_pw = sfs.default.rho0 * sfs.default.c
max_pressure = omega * impedance_pw * amplitude
grid = sfs.util.xyz_grid([xmin, xmax], [ymin, ymax], 0, spacing=0.005)
ani = animation.sound_pressure(
omega, center, radius, amplitude, grid, frames, pulsate=True,
figsize=figsize, vmin=-max_pressure, vmax=max_pressure)
plt.close()
HTML(ani.to_jshtml())
[9]:
Notice that the sound pressure exceeds the atmospheric pressure (\(\approx 10^5\) Pa), which of course makes no sense. This is due to the large amplitude (50 mm) of the pulsating motion. It was chosen to better visualize the particle movements in the earlier animations.
For 1 kHz, the amplitude corresponding to a moderate sound pressure, let say 1 Pa, is in the order of micrometer. As it is very small compared to the corresponding wavelength (0.343 m), the movement of the particles and the spatial structure of the sound field cannot be observed simultaneously. Furthermore, at high frequencies, the sound pressure for a given particle displacement scales with the frequency. The smaller wavelength (higher frequency) we choose, it is more likely to end up with a prohibitively high sound pressure.
In the following examples, the amplitude is set to a realistic value 1 \(\mu\)m. Notice that the pulsating motion of the sphere is no more visible.
[10]:
amplitude = 1e-6
impedance_pw = sfs.default.rho0 * sfs.default.c
max_pressure = omega * impedance_pw * amplitude
grid = sfs.util.xyz_grid([xmin, xmax], [ymin, ymax], 0, spacing=0.005)
ani = animation.sound_pressure(
omega, center, radius, amplitude, grid, frames, pulsate=True,
figsize=figsize, vmin=-max_pressure, vmax=max_pressure)
plt.close()
HTML(ani.to_jshtml())
[10]:
Let’s zoom in closer to the boundary of the sphere.
[11]:
L = 10 * amplitude
xmin_zoom, xmax_zoom = radius - L, radius + L
ymin_zoom, ymax_zoom = -L, L
[12]:
grid = sfs.util.xyz_grid([xmin_zoom, xmax_zoom], [ymin_zoom, ymax_zoom], 0, spacing=L / 100)
ani = animation.sound_pressure(
omega, center, radius, amplitude, grid, frames, pulsate=True,
figsize=figsize, vmin=-max_pressure, vmax=max_pressure)
plt.close()
HTML(ani.to_jshtml())
[12]:
This shows how the vibrating motion of the sphere (left half) changes the sound pressure of the surrounding air (right half). Notice that the sound pressure increases/decreases (more red/blue) when the surface accelerates/decelerates.
Example Python Scripts¶
Various example scripts are located in the directory doc/examples/
, e.g.
examples/horizontal_plane_arrays.py
: Computes the sound fields for various techniques, virtual sources and loudspeaker array configurationsexamples/animations_pulsating_sphere.py
: Creates animations of a pulsating sphere, see also the corresponding Jupyter notebookexamples/soundfigures.py
: Illustrates the synthesis of sound figures with Wave Field Synthesis
API Documentation¶
Sound Field Synthesis Toolbox.
https://sfs-python.readthedocs.io/
Submodules
Submodules for monochromatic sound fields. |
|
Submodules for broadband sound fields. |
|
Compute positions of various secondary source distributions. |
|
Weights (tapering) for the driving function. |
|
2D plots of sound fields etc. |
|
3D plots of sound fields etc. |
|
Various utility functions. |
References¶
- Ahr12
J. Ahrens. Analytic Methods of Sound Field Synthesis. Springer, Berlin Heidelberg, 2012. doi:10.1007/978-3-642-25743-8.
- AB79
J. B. Allen and D. A. Berkley. Image method for efficiently simulating small-room acoustics. Journal of the Acoustical Society of America, 65:943–950, 1979. doi:10.1121/1.382599.
- Bor84
J. Borish. Extension of the image model to arbitrary polyhedra. Journal of the Acoustical Society of America, 75:1827–1836, 1984. doi:10.1121/1.390983.
- FFSS17
Gergely Firtha, Péter Fiala, Frank Schultz, and Sascha Spors. Improved Referencing Schemes for 2.5D Wave Field Synthesis Driving Functions. IEEE/ACM Trans. Audio Speech Language Process., 25(5):1117–1127, 2017. doi:10.1109/TASLP.2017.2689245.
- Mos12
M. Möser. Technische Akustik. Springer, Berlin Heidelberg, 2012. doi:10.1007/978-3-642-30933-5.
- Sch16
Frank Schultz. Sound Field Synthesis for Line Source Array Applications in Large-Scale Sound Reinforcement. PhD thesis, University of Rostock, 2016. doi:10.18453/rosdok_id00001765.
- SA09
S. Spors and J. Ahrens. Spatial Sampling Artifacts of Wave Field Synthesis for the Reproduction of Virtual Point Sources. In 126th Convention of the Audio Engineering Society. 2009. URL: http://bit.ly/2jkfboi.
- SA10
S. Spors and J. Ahrens. Analysis and Improvement of Pre-equalization in 2.5-dimensional Wave Field Synthesis. In 128th Convention of the Audio Engineering Society. 2010. URL: http://bit.ly/2Ad6RRR.
- SRA08
S. Spors, R. Rabenstein, and J. Ahrens. The Theory of Wave Field Synthesis Revisited. In 124th Convention of the Audio Engineering Society. 2008. URL: http://bit.ly/2ByRjnB.
- SSR16
S. Spors, F. Schultz, and T. Rettberg. Improved Driving Functions for Rectangular Loudspeaker Arrays Driven by Sound Field Synthesis. In 42nd German Annual Conference on Acoustics (DAGA). 2016. URL: http://bit.ly/2AWRo7G.
- Sta97
Evert W. Start. Direct Sound Enhancement by Wave Field Synthesis. PhD thesis, Delft University of Technology, 1997.
- Wie14
H. Wierstorf. Perceptual Assessment of Sound Field Synthesis. PhD thesis, Technische Universität Berlin, 2014. doi:10.14279/depositonce-4310.
Contributing¶
If you find errors, omissions, inconsistencies or other things that need improvement, please create an issue or a pull request at https://github.com/sfstoolbox/sfs-python/. Contributions are always welcome!
Development Installation¶
Instead of pip-installing the latest release from PyPI, you should get the newest development version from Github:
git clone https://github.com/sfstoolbox/sfs-python.git
cd sfs-python
python3 -m pip install --user -e .
… where -e
stands for --editable
.
This way, your installation always stays up-to-date, even if you pull new changes from the Github repository.
Building the Documentation¶
If you make changes to the documentation, you can re-create the HTML pages using Sphinx. You can install it and a few other necessary packages with:
python3 -m pip install -r doc/requirements.txt --user
To create the HTML pages, use:
python3 setup.py build_sphinx
The generated files will be available in the directory build/sphinx/html/
.
To create the EPUB file, use:
python3 setup.py build_sphinx -b epub
The generated EPUB file will be available in the directory
build/sphinx/epub/
.
To create the PDF file, use:
python3 setup.py build_sphinx -b latex
Afterwards go to the folder build/sphinx/latex/
and run LaTeX to create the
PDF file. If you don’t know how to create a PDF file from the LaTeX output, you
should have a look at Latexmk (see also this Latexmk tutorial).
It is also possible to automatically check if all links are still valid:
python3 setup.py build_sphinx -b linkcheck
Running the Tests¶
You’ll need pytest for that. It can be installed with:
python3 -m pip install -r tests/requirements.txt --user
To execute the tests, simply run:
python3 -m pytest
Creating a New Release¶
New releases are made using the following steps:
Bump version number in
sfs/__init__.py
Update
NEWS.rst
Commit those changes as “Release x.y.z”
Create an (annotated) tag with
git tag -a x.y.z
Clear the
dist/
directoryCreate a source distribution with
python3 setup.py sdist
Create a wheel distribution with
python3 setup.py bdist_wheel
Check that both files have the correct content
Upload them to PyPI with twine:
python3 -m twine upload dist/*
Push the commit and the tag to Github and add release notes containing a link to PyPI and the bullet points from
NEWS.rst
Check that the new release was built correctly on RTD and select the new release as default version
Version History¶
- Version 0.6.2 (2021-06-05):
build doc fix, use sphinx4, mathjax2, html_css_files
- Version 0.6.1 (2021-06-05):
New default driving function for
sfs.td.wfs.point_25d()
for reference curve
- Version 0.6.0 (2020-12-01):
New function
sfs.fd.source.line_bandlimited()
computing the sound field of a spatially bandlimited line sourceDrop support for Python 3.5
- Version 0.5.0 (2019-03-18):
Switching to separate
sfs.plot2d
andsfs.plot3d
for plotting functionsMove
sfs.util.displacement()
tosfs.fd.displacement()
Switch to keyword only arguments
New default driving function for
sfs.fd.wfs.point_25d()
New driving function syntax, e.g.
sfs.fd.wfs.point_25d()
Example for the sound field of a pulsating sphere
Add time domain NFC-HOA driving functions
sfs.td.nfchoa
sfs.fd.synthesize()
,sfs.td.synthesize()
for soundfield superpositionMove source selection helpers to
sfs.util
Use
sfs.default
object instead ofsfs.defs
submoduleDrop support for legacy Python 2.7
- Version 0.4.0 (2018-03-14):
Driving functions in time domain for a plane wave, point source, and focused source
Image source model for a point source in a rectangular room
sfs.util.DelayedSignal
class andsfs.util.as_delayed_signal()
Improvements to the documentation
Start using Jupyter notebooks for examples in documentation
Spherical Hankel function as
sfs.util.spherical_hn2()
Use
scipy.special.spherical_jn
,scipy.special.spherical_yn
instead ofscipy.special.sph_jnyn
Generalization of the modal order argument in
sfs.mono.source.point_modal()
Rename
sfs.util.normal_vector()
tosfs.util.normalize_vector()
Add parameter
max_order
to NFCHOA driving functionsAdd
beta
parameter to Kaiser tapering windowFix clipping problem of sound field plots with matplotlib 2.1
Fix elevation in
sfs.util.cart2sph()
Fix
sfs.tapering.tukey()
foralpha=1
- Version 0.3.1 (2016-04-08):
Fixed metadata of release
- Version 0.3.0 (2016-04-08):
Dirichlet Green’s function for the scattering of a line source at an edge
Driving functions for the synthesis of various virtual source types with edge-shaped arrays by the equivalent scattering appoach
Driving functions for the synthesis of focused sources by WFS
- Version 0.2.0 (2015-12-11):
Ability to calculate and plot particle velocity and displacement fields
Several function name and parameter name changes
- Version 0.1.1 (2015-10-08):
Fix missing
sfs.mono
subpackage in PyPI packages
- Version 0.1.0 (2015-09-22):
Initial release.