Directional Sources and Microphones

Real-world microphones and sound sources usually exhibit directional responses. That is, the impulse response (or frequency response) depends on the emission or reception angle (for sources and microphones, respectively). A concrete example is the human ear attached to the head. The left ear is typically more sensitive to sounds coming from the left side than from the right.

This sub-module provides an interface to add such directional responses to microphones and sources in the room impulse response simulation.

Warning

The directional responses are currently only supported for the image source method based simulation.

Warning

Directional responses are only supported for 3D rooms.

The directivities are described by an object of a class derived from Directivity.

Let’s dive right in with an example. Here, we simulate a shoebox room with a cardioid source and a dummy head receiver with two ears (i.e., microphones). This simulates a binaural response.

import pyroomacoustics as pra

room = pra.ShoeBox(
    p=[5, 3, 3],
    materials=pra.Material(energy_absorption),
    fs=16000,
    max_order=40,
)

# add a cardioid source
dir = pra.directivities.Cardioid(DirectionVector(azimuth=-65, colatitude=90) , gain=1.0)
room.add_source([3.75, 2.13, 1.41], directivity=dir)

# add a dummy head receiver from the MIT KEMAR database
hrtf = MeasuredDirectivityFile(
    path="mit_kemar_normal_pinna.sofa",  # SOFA file is in the database
    fs=room.fs,
    interp_order=12,  # interpolation order
    interp_n_points=1000,  # number of points in the interpolation grid
)

# provide the head rotation
orientation = Rotation3D([90.0, 30.0], "yz", degrees=True)

# choose and interpolate the directivities
dir_left = hrtf.get_mic_directivity("left", orientation=orientation)
dir_right = hrtf.get_mic_directivity("right", orientation=orientation)

# for a head-related transfer function, the microphone should be co-located
mic_pos = [1.05, 1.74, 1.81]
room.add_microphone(mic_pos, directivity=dir)
room.add_microphone(mic_pos, directivity=dir)

Analytic Directional Responses

A class of directional responses can be defined analytically. Such respones include in particular the cardioid family of patterns that describes cardioid, super-cardioid, and figure-of-eight microphones, (see cardioid family, under Polar patterns, with cardioid, hypercardioid, cardioid, subcardioid, figure-eight, and omnidirectional). In three dimensions, for an orientation given by unit vector \(\boldsymbol{u}\), a parameter \(p \in [0, 1]\), and a gain \(G\), the response to direction \(\boldsymbol{r}\) (also a unit vector) is given by the following equation.

\[f(\boldsymbol{r}\,;\,\boldsymbol{d}, p, G) = G (p + (1 - p) \boldsymbol{d}^\top \boldsymbol{r}),\]

Note that \(\boldsymbol{d}^\top \boldsymbol{r}\) is the inner product of two unit vectors, that is, the cosine of the angle between them.

Different values of \(p\) correspond to different patterns: 0 for figure-eight, 0.25 for hyper-cardioid, 0.5 for cardioid, 0.75 for sub-cardioid, and 1 for omni.

Specialized objects Cardioid, FigureEight, SubCardioid, HyperCardioid, and Omnidirectional are provided for the different patterns. The class CardioidFamily can be used to make a pattern with arbitrary parameter \(p\).

# a cardioid pointing toward the ``z`` direction
from pyroomacoustics.directivities import CardioidFamily

dir = Cardioid([0, 0, 1], gain=1.0)
class pyroomacoustics.directivities.analytic.Cardioid(orientation, gain=1.0)

Bases: CardioidFamily

Cardioid directivity pattern.

Parameters:
  • orientation (DirectionVector) – Indicates direction of the pattern.

  • gain (float) – The linear gain of the directivity pattern (default is 1.0)

class pyroomacoustics.directivities.analytic.CardioidFamily(orientation, p, gain=1.0)

Bases: Directivity

Object for directivities coming from the cardioid family. In three dimensions, for an orientation given by unit vector \(\\boldsymbol{u}\), a parameter \(p \in [0, 1]\), and a gain \(G\), the pattern is given by the following equation.

\[f(\boldsymbol{r}\,;\,\boldsymbol{d}, p, G) = G (p + (1 - p) \boldsymbol{d}^\top \boldsymbol{r}),\]

Different values of \(p\) correspond to different patterns: 0 for figure-eight, 0.25 for hyper-cardioid, 0.5 for cardioid, 0.75 for sub-cardioid, and 1 for omni.

Note that all the patterns are cylindrically symmetric around the orientation vector.

Parameters:
  • orientation (DirectionVector or numpy.ndarray) – Indicates direction of the pattern.

  • p (float) – Parameter of the cardioid pattern. A value of 0 corresponds to a figure-eight pattern, 0.5 to a cardioid pattern, and 1 to an omni pattern The parameter must be between 0 and 1

  • gain (float) – The linear gain of the directivity pattern (default is 1.0)

property directivity_pattern

Name of cardioid directivity pattern.

property filter_len_ir

When is_impulse_response returns True, this property returns the lengths of the impulse responses returned. All impulse responses are assumed to have the same length.

get_azimuth(degrees=True)
get_colatitude(degrees=True)
get_response(azimuth, colatitude=None, magnitude=False, frequency=None, degrees=True)

Get response for provided angles.

Parameters:
  • azimuth (array_like) – Azimuth

  • colatitude (array_like, optional) – Colatitude. Default is to be on XY plane.

  • magnitude (bool, optional) – Whether to return magnitude of response.

  • frequency (float, optional) – For which frequency to compute the response. Cardioid are frequency-independent so this value has no effect.

  • degrees (bool, optional) – If True, azimuth and colatitude are in degrees. Otherwise, they are in radians.

Returns:

resp – Response at provided angles.

Return type:

ndarray

property is_impulse_response

Indicates whether the array contains coefficients for octave bands (returns False) or is a full-size impulse response (returns True).

plot_response(azimuth, colatitude=None, degrees=True, ax=None, offset=None)

Plot directivity response at specified angles.

Parameters:
  • azimuth (array_like) – Azimuth values for plotting.

  • colatitude (array_like, optional) – Colatitude values for plotting. If not provided, 2D plot.

  • degrees (bool) – Whether provided values are in degrees (True) or radians (False).

  • ax (axes object)

  • offset (list) – 3-D coordinates of the point where the response needs to be plotted.

Returns:

ax

Return type:

Axes

set_orientation(orientation)

Set orientation of directivity pattern.

Parameters:

orientation (DirectionVector) – New direction for the directivity pattern.

class pyroomacoustics.directivities.analytic.FigureEight(orientation, gain=1.0)

Bases: CardioidFamily

Figure-of-eight directivity pattern.

Parameters:
  • orientation (DirectionVector) – Indicates direction of the pattern.

  • gain (float) – The linear gain of the directivity pattern (default is 1.0)

class pyroomacoustics.directivities.analytic.HyperCardioid(orientation, gain=1.0)

Bases: CardioidFamily

Hyper-cardioid directivity pattern.

Parameters:
  • orientation (DirectionVector) – Indicates direction of the pattern.

  • gain (float) – The linear gain of the directivity pattern (default is 1.0)

class pyroomacoustics.directivities.analytic.Omnidirectional(gain=1.0)

Bases: CardioidFamily

Hyper-cardioid directivity pattern.

Parameters:
  • orientation (DirectionVector) – Indicates direction of the pattern.

  • gain (float) – The linear gain of the directivity pattern (default is 1.0)

class pyroomacoustics.directivities.analytic.SubCardioid(orientation, gain=1.0)

Bases: CardioidFamily

Sub-cardioid directivity pattern.

Parameters:
  • orientation (DirectionVector) – Indicates direction of the pattern.

  • gain (float) – The linear gain of the directivity pattern (default is 1.0)

pyroomacoustics.directivities.analytic.cardioid_energy(p, gain=1.0)

This function gives the exact value of the surface integral of the cardioid (family) function on the unit sphere

\[E(p, G) = \iint_{\mathbb{S}^2} G^2 \left( p + (1 - p) \boldsymbol{d}^\top \boldsymbol{r} \right)^2 d\boldsymbol{r} = \frac{4 \pi}{3} G^2 \left( 4 p^2 - 2 p + 1 \right).\]

This can be used to normalize the energy sent/received.

Parameters:
  • p (float) – The parameter of the cardioid function (between 0 and 1)

  • gain (float) – The gain of the cardioid function

pyroomacoustics.directivities.analytic.cardioid_func(x, direction, p, gain=1.0, normalize=True, magnitude=False)

One-shot function for computing cardioid response.

Parameters:
  • x (array_like, shape (n_dim, ...)) – Cartesian coordinates

  • direction (array_like, shape (n_dim)) – Direction vector, should be normalized

  • p (float) – Parameter for the cardioid function (between 0 and 1)

  • gain (float) – The gain

  • normalize (bool) – Whether to normalize coordinates and direction vector

  • magnitude (bool) – Whether to return magnitude, default is False

Returns:

resp – Response at provided angles for the speficied cardioid function.

Return type:

ndarray

Measured Directivities

Source and microphone directivities can be measured in an anechoic chamber. Such measurements result in a collection of impulse responses or transfer functions each associated with a specific source and receiver (i.e., microphone) location. The SOFA file format has been proposed as a standard for the storage of such measurements.

This sub-module offers a way to read such measurements from (SOFA) files and use the measurement to obtain a more faithful simulation.

The workhorse of this module is the class MeasuredDirectivityFile which reads the content of a file and standardize the data for futher use. A single SOFA file can contain multiple measurements (for example corresponding to different devices). The class provies a method to retrieve measurements from individual sources and turn them into a py:class:MeasuredDirectivity object that can be used to create a py:class:pyroomacoustics.MicrophoneArray object with this directivity.

Such measurements do not provide impulse responses for every possible impinging direction. Instead, during simulation the impulse response closest to the desired direction is used instead. To avoid sharp transitions, the py:class:MeasuredDirectivityFile provides an interpolation method in the spherical harmonics domain. This can be activated by providing an order for the interpolation, e.g, interp_order=12.

Here is an example of loading a head-related transfer function and load the directivities for left and right ears of a dummy head HRTF.

from pyroomacoustics.directivities import MeasuredDirectivityFile, Rotation3D

# the file reader object reads the file and optionally performs interpolation
# if the file contains multiple directivities, they are all read
hrtf = MeasuredDirectivityFile(
    path="mit_kemar_normal_pinna.sofa",  # SOFA file is in the database
    fs=fs,
    interp_order=12,
    interp_n_points=1000,
)

# orientations can be provided as rotation matrices
orientation = Rotation3D([colatitude_deg, azimuth_deg], "yz", degrees=True)

# we can then choose which directivities we want from the file
dir_left = hrtf.get_mic_directivity("left", orientation=orientation)
dir_right = hrtf.get_mic_directivity("right", orientation=orientation)
class pyroomacoustics.directivities.measured.MeasuredDirectivity(orientation, grid, impulse_responses, fs)

Bases: Directivity

A class to store directivity patterns obtained by measurements.

Parameters:
  • orientation (Rotation3D) – A rotation to apply to the pattern

  • grid (doa.Grid) – The grid of unit vectors where the measurements were taken

  • impulse_responses (np.ndarray, (n_grid, n_ir)) – The impulse responses corresponding to the grid points

  • fs (int) – The sampling frequency of the impulse responses

property filter_len_ir

Length of the impulse response in samples

get_response(azimuth, colatitude=None, magnitude=False, frequency=None, degrees=True)

Get response for provided angles and frequency.

Parameters:
  • azimuth (np.ndarray, (n_points,)) – The azimuth of the desired responses

  • colatitude (np.ndarray, (n_points,)) – The colatitude of the desired responses

  • magnitude (bool) – Ignored

  • frequency (np.ndarray, (n_freq,)) – Ignored

  • degrees (bool) – If True, indicates that azimuth and colatitude are provided in degrees

property is_impulse_response

Indicates whether the array contains coefficients for octave bands (returns False) or is a full-size impulse response (returns True).

plot(freq_bin=0, n_grid=100, ax=None, depth=False, offset=None)

Plot the directivity pattern at a given frequency.

Parameters:
  • freq_bin (int) – The frequency bin to plot

  • n_grid (int) – The number of points to use for the interpolation grid

  • ax (matplotlib.axes.Axes, optional) – The axes to plot on. If not provided, a new figure is created

  • depth (bool) – If True, directive response is both depicted by color and depth of the surface. If False, then only the color map denotes the intensity. (default False)

  • offset (float) – An offset to apply to the directivity pattern

Returns:

ax – The axes on which the directivity is plotted

Return type:

matplotlib.axes.Axes

set_orientation(orientation)

Set orientation of directivity pattern.

Parameters:

orientation (Rotation3D) – New direction for the directivity pattern.

class pyroomacoustics.directivities.measured.MeasuredDirectivityFile(path, fs=None, interp_order=None, interp_n_points=1000, file_reader_callback=None, mic_labels=None, source_labels=None)

Bases: object

This class reads measured directivities from a SOFA format file. Optionally, it can perform interpolation of the impulse responses onto a finer grid. The interpolation is done in the spherical harmonics domain.

Parameters:
  • path ((string)) – Path towards the specific DIRPAT file

  • fs ((int)) – The desired sampling frequency. If the impulse responses were stored at a different sampling frequency, they are resampled at fs.

  • interp_order ((int)) – The order of spherical harmonics to use for interpolation. If None interpolation is not used.

  • interp_n_points ((int)) – Number of points for the interpolation grid. The interpolation grid is a Fibonnaci pseudo-uniform sampling of the sphere.

  • file_reader_callback ((callable)) – A callback function that reads the SOFA file and returns the impulse responses The signature should be the same as the function open_sofa_file

  • mic_labels ((list of strings)) – List of labels for the microphones. If not provided, the labels are simply the indices of the microphones in the array

  • source_labels ((list of strings)) – List of labels for the sources. If not provided, the labels are simply the indices of the measurements in the array

get_mic_directivity(measurement_id, orientation)

Get a directivity for a microphone

Parameters:
  • measurement_id (int or str) – The id of the microphone

  • orientation (Rotation3D) – The orientation of the directivity pattern

get_mic_position(measurement_id)

Get the position of source with id measurement_id

Parameters:

measurement_id (int or str) – The id of the source

get_source_directivity(measurement_id, orientation)

Get a directivity for a source

Parameters:
  • measurement_id (int or str) – The id of the source

  • orientation (Rotation3D) – The orientation of the directivity pattern

get_source_position(measurement_id)

Get the position of source with id measurement_id

Parameters:

measurement_id (int or str) – The id of the source

Built-in SOFA Files Database

Pyroomacoustics contains a small database of SOFA files that have been tested and can be used for simultions. The database can be loaded using the function SOFADatabase.

# load and display the list of available SOFA files and their content
from pyroomacoustics.datasets import SOFADatabase

db = SOFADatabase()
db.list()

The database contains the following files.

  • Three files from the DIRPAT database collected by Manuel Brandner, Matthias Frank, and Daniel Rudrich University of Music and Performing Arts, Graz.

    • AKG_c480_c414_CUBE.sofa containing the directive responses of a microphone with 4 different patterns.

    • EM32_Directivity.sofa that contains the directional response of the Eigenmike em32 microphone array.

    • LSPs_HATS_GuitarCabinets_Akustikmessplatz.sofa that contains 12 source directivities. This file is dynamically downloaded upon its first use.

    • The files are public domain (CC0), but if you use them in your research, please cite the following paper.

      M. Brandner, M. Frank, and D. Rudrich, "DirPat—Database and
      Viewer of 2D/3D Directivity Patterns of Sound Sources and Receivers,"
      in Audio Engineering Society Convention 144, Paper 425, 2018.
      
  • Two head-related transfer functions of the MIT KEMAR dummy head with normal and large pinna. The data was collected by Bill Gardner and Keith Martin from MIT and is free to use provided the authors are cited. See the full license for more details.

class pyroomacoustics.datasets.sofa.SOFADatabase(download=True)

A small database of SOFA files containing source/microphone directional impulse responses

The database object is a dictionary-like object where the keys are the names of the SOFA files and the values are objects with the following attributes:

db = SOFADatabase()

# type of device: 'sources' or 'microphones'
db["Soundfield_ST450_CUBE"].type

# list of the labels of the sources/microphones
db["Soundfield_ST450_CUBE"].contains
Parameters:

download (bool, optional) – If set to True, the SOFA files are downloaded if they are not already present in the default folder

property db_info_path

The path to the JSON file containing the SOFA files information

list()

Print a list of the available SOFA files and the labels of the different devices they contain

property root

The path to the folder containing the SOFA files

pyroomacoustics.datasets.sofa.get_sofa_db()

A helper function to quickly load the SOFA database

Reading Other or Custom File Types

It is possible to read other file types by providing a custom reader function to MeasuredDirectivityFile with the argument file_reader_callback. The function should have the same signature as open_sofa_file().

SOFA File Readers

SOFA is a very flexible file format for storing direction impulse responses. This module provides a function to read SOFA files and extract the impulse responses in a format that can be used for simulation.

pyroomacoustics.directivities.sofa.open_sofa_file(path, fs=16000)

Open a SOFA file and read the impulse responses

Parameters:
  • path (str or Path) – Path to the SOFA file

  • fs (int, optional) – The desired sampling frequency. If the impulse responses were stored at a different sampling frequency, they are resampled at fs.

Returns:

  • ir (np.ndarray (n_sources, n_mics, taps)) – The impulse responses

  • fs (int) – The sampling frequency of the impulse responses

  • source_dir (np.ndarray (3, n_sources)) – The direction of the sources in spherical coordinates

  • rec_loc (np.ndarray (3, n_mics)) – The location of the receivers in cartesian coordinates

  • source_labels (List[str]) – If available, a list of human readable labels for the sources is returned. Otherwise, None is returned

  • mic_labels (List[str]) – If available, a list of human readable labels for the microphones is returned. Otherwise, None is returned

Direction of the Patterns

Using directivities makes sources and microphones having a different response depending on the location of other objects. This means that their orientation in 3D space matters.

Some types of directivities such as CardioidFamily and derived classes are defined only by a vector (i.e., direction). The response is then symmetric around the axis defined by this vector.

However, in general, not all directivities are symmetric in this way. For the general case, the orientation can be defined by Euler angles. This is implemented in the class pyroomacoustics.direction.Rotation3D.

class pyroomacoustics.directivities.direction.DirectionVector(azimuth, colatitude=None, degrees=True)

Bases: object

Object for representing direction vectors in 3D, parameterized by an azimuth and colatitude angle.

Parameters:
  • azimuth (float)

  • colatitude (float, optional) – Default to PI / 2, only XY plane.

  • degrees (bool) – Whether provided values are in degrees (True) or radians (False).

property unit_vector

Direction vector in cartesian coordinates.

class pyroomacoustics.directivities.direction.Rotation3D(angles, rot_order='zyx', degrees=True)

Bases: object

An object representing 3D rotations by their Euler angles.

A rotation in 3D space can be fully described by 3 angles (i.e., the Euler angles). Each rotation is applied around one of the three axes and there are 12 possible ways of pickinig the order or the rotations.

This class can apply full or partial rotations to sets of points.

The angles are provided as an array angles. The axes of rotation for the angles are provided in rot_order as a string of three characters out of ["x", "y", "z"] or a list of three integers out of [0, 1, 2]. Each axis can be repeated. To obtain full rotations, the same axis should not be used twice in a row.

By default, the angles are specified in degrees. This can be changed by setting degrees=False. In that case, the angles are assumed to be in radians.

Parameters:
  • angles (array_like) – An array containing between 0 and 3 angles.

  • rot_order (str of List[int]) – The order of the rotations. The default is “zyx”. The order indicates around which axis the rotation is performed. For example, “zyx” means that the rotation is first around the z-axis, then the y-axis, and finally the x-axis.

  • degrees (bool) – Whether the angles are in degrees (True) or radians (False).

rotate(points)

Rotate a set of points.

Parameters:

points (array_like (3, ...)) – The points to rotate. The first dimension must be 3.

Returns:

rotated_points – The rotated points.

Return type:

np.ndarray

rotate_transpose(points)

Transposed rotations of a set of points.

Parameters:

points (array_like (3, ...)) – The points to rotate. The last dimension must be 3.

Returns:

rotated_points – The rotated points.

Return type:

np.ndarray

Creating New Types of Directivities

The Directivity class is an abstract class that can be subclassed to create new types of directivities. The class provides a common interface to access the directivity patterns.

The class should implement the following methods:

  • get_response to get the response for a given angle and frequency

  • is_impulse_response to indicate whether the directivity is an impulse response or just band coefficients

  • filter_len_ir to return the length of the impulse response. This should return 1 if the directivity is not an impulse response.

class pyroomacoustics.directivities.base.Directivity

Bases: ABC

Abstract class for directivity patterns.

abstract property filter_len_ir

When is_impulse_response returns True, this property returns the lengths of the impulse responses returned. All impulse responses are assumed to have the same length.

abstractmethod get_response(azimuth, colatitude=None, magnitude=False, frequency=None, degrees=True)

Get response for provided angles.

Parameters:
  • azimuth (array_like) – Azimuth

  • colatitude (array_like, optional) – Colatitude in degrees. Default is to be on XY plane.

  • magnitude (bool, optional) – Whether to return magnitude of response.

  • frequency (float, optional) – For which frequency to compute the response. If the response is frequency independent, this parameter is ignored.

  • degrees (bool, optional) – If True, azimuth and colatitude are in degrees. Otherwise, they are in radians.

Returns:

resp – Response at provided angles.

Return type:

ndarray

abstract property is_impulse_response

Indicates whether the array contains coefficients for octave bands (returns False) or is a full-size impulse response (returns True).

Spherical Interpolation

This module provides functions to interpolate impulse responses on a sphere. The interpolation is done in the spherical harmonics domain.

pyroomacoustics.directivities.interp.spherical_interpolation(grid, impulse_responses, new_grid, spherical_harmonics_order=12, axis=-2, nfft=None)
Parameters:
  • grid (pyroomacoustics.doa.GridSphere) – The grid of the measurements

  • impulse_responses (numpy.ndarray, (..., n_measurements, ..., n_samples)) – The impulse responses to interpolate, the last axis is time and one other axis should have dimension matching the length of the grid. By default, it is assumed to be second from the end, but can be specified with the axis argument.

  • new_grid (pyroomacoustics.doa.GridSphere) – Grid of points at which to interpolate

  • spherical_harmonics_order (int) – The order of spherical harmonics to use for interpolation

  • axis (int) – The axis of the grid in the impulse responses array

  • nfft (int) – The length of the FFT to use for the interpolation (default n_samples)

Numerical Spherical Integral

Provides a function to numerically integrate a function over the 3D unit-sphere (\(\mathbb{S}^2\)).

\[\iint_{\mathbb{S}^2} f(\mathbf{x})\, d\mathbf{x}\]
pyroomacoustics.directivities.integration.spherical_integral(func, n_points)

Numerically integrate a function over the sphere.

Parameters:
  • func (callable) – The function to integrate. It should take an array of shape (3, n_points) and return an array of shape (n_points,)

  • n_points (int) – The number of points to use for integration

Returns:

value – The value of the integral

Return type:

np.ndarray