Skip to content

Commit

Permalink
Fixes: Room.add_microphone_array discards MicrophoneArray directivity. (
Browse files Browse the repository at this point in the history
#385)

Fixes issue #382: When providing a ``MicrophoneArray`` object with directivity to ``Room.add_microphone_array``, the directivity was dropped from the object.
  • Loading branch information
fakufaku authored Dec 7, 2024
1 parent 1f1d644 commit f6d11c9
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 25 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ adheres to `Semantic Versioning <http://semver.org/spec/v2.0.0.html>`_.
`Unreleased`_
-------------

Nothing yet
Bugfix
~~~~~~

- Fixes issue #382: When providing a ``MicrophoneArray`` object with
directivity to ``Room.add_microphone_array``, the directivity was dropped
from the object.

`0.8.2`_ - 2024-11-06
---------------------
Expand Down
15 changes: 13 additions & 2 deletions pyroomacoustics/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -1980,7 +1980,7 @@ def add(self, obj):
).format(self.dim, obj.dim)
)

if "mic_array" not in self.__dict__ or self.mic_array is None:
if not hasattr(self, "mic_array") or self.mic_array is None:
self.mic_array = obj
else:
self.mic_array.append(obj)
Expand Down Expand Up @@ -2046,6 +2046,12 @@ def add_microphone_array(self, mic_array, directivity=None):
As an alternative, a
:py:obj:`~pyroomacoustics.beamforming.MicrophoneArray` can be
provided.
directivity: list of Directivity objects, optional
If ``mic_array`` is provided as a numpy array, an optional
:py:obj:`~pyroomacoustics.directivities.Directivity` object or
list thereof can be provided.
If ``mic_array`` is a MicrophoneArray object, passing an argument here
will result in an error.
Returns
-------
Expand All @@ -2064,7 +2070,12 @@ def add_microphone_array(self, mic_array, directivity=None):
mic_array = MicrophoneArray(mic_array, self.fs, directivity)
else:
# if the type is microphone array
mic_array.set_directivity(directivity)
if directivity is not None:
raise ValueError(
"When providing a MicrophoneArray object, the directivities should "
"be provided in the object, not via the `directivity` parameter "
"of this method."
)

if self.simulator_state["rt_needed"] and mic_array.is_directive:
raise NotImplementedError("Directivity not supported with ray tracing.")
Expand Down
155 changes: 133 additions & 22 deletions pyroomacoustics/tests/test_room_add.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
import pytest

import pyroomacoustics as pra

Expand All @@ -8,35 +9,82 @@
source_loc1 = [3.5, 7.7, 2.1]
mic0 = [7, 8, 3.9]
mic1 = [7.87, 3.6, 6.1]
mic_dir0 = pra.FigureEight(
orientation=pra.DirectionVector(azimuth=90, colatitude=15, degrees=True)
)
mic_dir1 = pra.FigureEight(
orientation=pra.DirectionVector(azimuth=180, colatitude=15, degrees=True)
)
src_dir0 = pra.FigureEight(
orientation=pra.DirectionVector(azimuth=270, colatitude=15, degrees=True)
)
src_dir1 = pra.FigureEight(
orientation=pra.DirectionVector(azimuth=0, colatitude=15, degrees=True)
)


@pytest.mark.parametrize("with_dir", ((True,), (False,)))
def test_add_source_mic(with_dir):
room = pra.ShoeBox(room_size)

if with_dir:
sdir0 = src_dir0
sdir1 = src_dir1
mdir0 = mic_dir0
mdir1 = mic_dir1
else:
sdir0 = sdir1 = None
mdir0 = mdir1 = None

def test_add_source_mic():
room = pra.ShoeBox(room_size).add_source(source_loc0).add_microphone(mic0)
room = (
pra.ShoeBox(room_size)
.add_source(source_loc0, directivity=sdir0)
.add_microphone(mic0, directivity=mdir0)
)

assert len(room.sources) == 1
assert np.allclose(room.sources[0].position, source_loc0)
assert len(room.mic_array) == 1
assert room.mic_array.R.shape == (3, 1)
assert np.allclose(room.mic_array.R[:, 0], mic0)
# Test directivities.
assert room.sources[0].directivity is sdir0
assert all(d is md for d, md in zip(room.mic_array.directivity, [mdir0]))

room.add_microphone(mic1).add_source(source_loc1)
room.add_microphone(mic1, directivity=mdir1).add_source(
source_loc1, directivity=sdir1
)

assert len(room.sources) == 2
assert np.allclose(room.sources[1].position, source_loc1)
assert len(room.mic_array) == 2
assert np.allclose(room.mic_array.R[:, 0], mic0)
assert np.allclose(room.mic_array.R[:, 1], mic1)
assert room.mic_array.R.shape == (3, 2)
# Test directivities.
assert room.sources[0].directivity is sdir0
assert room.sources[1].directivity is sdir1
assert all(d is md for d, md in zip(room.mic_array.directivity, [mdir0, mdir1]))


def test_add_source_mic_obj():
@pytest.mark.parametrize("with_dir", ((True,), (False,)))
def test_add_source_mic_obj(with_dir):
room = pra.ShoeBox(room_size)

source0 = pra.SoundSource(source_loc0, signal=sig)
source1 = pra.SoundSource(source_loc1, signal=sig)
if with_dir:
sdir0 = src_dir0
sdir1 = src_dir1
mdir0 = mic_dir0
mdir1 = mic_dir1
else:
sdir0 = sdir1 = None
mdir0 = mdir1 = None

source0 = pra.SoundSource(source_loc0, signal=sig, directivity=sdir0)
source1 = pra.SoundSource(source_loc1, signal=sig, directivity=sdir1)

mic_array0 = pra.MicrophoneArray(np.c_[mic0], fs=room.fs)
mic_array1 = pra.MicrophoneArray(np.c_[mic1], fs=room.fs)
mic_array0 = pra.MicrophoneArray(np.c_[mic0], fs=room.fs, directivity=mdir0)
mic_array1 = pra.MicrophoneArray(np.c_[mic1], fs=room.fs, directivity=mdir1)

room.add(source0).add(mic_array0)

Expand All @@ -45,6 +93,9 @@ def test_add_source_mic_obj():
assert len(room.mic_array) == 1
assert room.mic_array.R.shape == (3, 1)
assert np.allclose(room.mic_array.R[:, 0], mic0)
# Test directivities.
assert room.sources[0].directivity is sdir0
assert all(d is md for d, md in zip(room.mic_array.directivity, [mdir0]))

room.add(mic_array1).add(source1)

Expand All @@ -54,14 +105,27 @@ def test_add_source_mic_obj():
assert np.allclose(room.mic_array.R[:, 0], mic0)
assert np.allclose(room.mic_array.R[:, 1], mic1)
assert room.mic_array.R.shape == (3, 2)
# Test directivities.
assert room.sources[0].directivity is sdir0
assert room.sources[1].directivity is sdir1
assert all(d is md for d, md in zip(room.mic_array.directivity, [mdir0, mdir1]))


def test_add_source_mic_obj_2():
@pytest.mark.parametrize("with_dir", ((True,), (False,)))
def test_add_source_mic_obj_2(with_dir):
room = pra.ShoeBox(room_size)

source0 = pra.SoundSource(source_loc0, signal=sig)
source1 = pra.SoundSource(source_loc1, signal=sig)
mic_array = pra.MicrophoneArray(np.c_[mic0, mic1], fs=room.fs)
if with_dir:
sdir0 = src_dir0
sdir1 = src_dir1
mdir = [mic_dir0, mic_dir1]
else:
sdir0 = sdir1 = None
mdir = [None, None]

source0 = pra.SoundSource(source_loc0, signal=sig, directivity=sdir0)
source1 = pra.SoundSource(source_loc1, signal=sig, directivity=sdir1)
mic_array = pra.MicrophoneArray(np.c_[mic0, mic1], fs=room.fs, directivity=mdir)

room.add(source0).add(source1).add(mic_array)

Expand All @@ -72,15 +136,40 @@ def test_add_source_mic_obj_2():
assert np.allclose(room.mic_array.R[:, 0], mic0)
assert np.allclose(room.mic_array.R[:, 1], mic1)
assert room.mic_array.R.shape == (3, 2)
# Test directivities.
assert room.sources[0].directivity is sdir0
assert room.sources[1].directivity is sdir1
assert all(d is md for d, md in zip(room.mic_array.directivity, mdir))


def test_add_source_mic_obj_with_dir_error():
room = pra.ShoeBox(room_size)

mic_array = pra.MicrophoneArray(np.c_[mic0, mic1], fs=room.fs)

with pytest.raises(ValueError):
room.add_microphone_array(mic_array, directivity=[mic_dir0, mic_dir1])


@pytest.mark.parametrize("with_dir", ((True,), (False,)))
def test_add_source_mic_ndarray(with_dir):
if with_dir:
sdir0 = src_dir0
sdir1 = src_dir1
mdir = [mic_dir0, mic_dir1]
else:
sdir0 = sdir1 = None
mdir = [None, None]

def test_add_source_mic_ndarray():
source0 = pra.SoundSource(source_loc0, signal=sig)
source1 = pra.SoundSource(source_loc1, signal=sig)
source0 = pra.SoundSource(source_loc0, signal=sig, directivity=sdir0)
source1 = pra.SoundSource(source_loc1, signal=sig, directivity=sdir1)
mic_array = np.c_[mic0, mic1]

room = (
pra.ShoeBox(room_size).add(source0).add(source1).add_microphone_array(mic_array)
pra.ShoeBox(room_size)
.add(source0)
.add(source1)
.add_microphone_array(mic_array, directivity=mdir)
)

assert len(room.sources) == 2
Expand All @@ -90,14 +179,32 @@ def test_add_source_mic_ndarray():
assert np.allclose(room.mic_array.R[:, 0], mic0)
assert np.allclose(room.mic_array.R[:, 1], mic1)
assert room.mic_array.R.shape == (3, 2)


def test_add_source_mic_ndarray_2():
source0 = pra.SoundSource(source_loc0, signal=sig)
source1 = pra.SoundSource(source_loc1, signal=sig)
# Test directivities.
assert room.sources[0].directivity is sdir0
assert room.sources[1].directivity is sdir1
assert all(d is md for d, md in zip(room.mic_array.directivity, mdir))


@pytest.mark.parametrize("with_dir", ((True,), (False,)))
def test_add_source_mic_ndarray_2(with_dir):
if with_dir:
sdir0 = src_dir0
sdir1 = src_dir1
mdir = [mic_dir0, mic_dir1]
else:
sdir0 = sdir1 = None
mdir = [None, None]

source0 = pra.SoundSource(source_loc0, signal=sig, directivity=sdir0)
source1 = pra.SoundSource(source_loc1, signal=sig, directivity=sdir1)
mic_array = np.c_[mic0, mic1]

room = pra.ShoeBox(room_size).add(source0).add(source1).add_microphone(mic_array)
room = (
pra.ShoeBox(room_size)
.add(source0)
.add(source1)
.add_microphone(mic_array, directivity=mdir)
)

assert len(room.sources) == 2
assert np.allclose(room.sources[0].position, source_loc0)
Expand All @@ -106,6 +213,10 @@ def test_add_source_mic_ndarray_2():
assert np.allclose(room.mic_array.R[:, 0], mic0)
assert np.allclose(room.mic_array.R[:, 1], mic1)
assert room.mic_array.R.shape == (3, 2)
# Test directivities.
assert room.sources[0].directivity is sdir0
assert room.sources[1].directivity is sdir1
assert all(d is md for d, md in zip(room.mic_array.directivity, mdir))


if __name__ == "__main__":
Expand Down

0 comments on commit f6d11c9

Please sign in to comment.