Skip to content

Commit f6d11c9

Browse files
authored
Fixes: Room.add_microphone_array discards MicrophoneArray directivity. (#385)
Fixes issue #382: When providing a ``MicrophoneArray`` object with directivity to ``Room.add_microphone_array``, the directivity was dropped from the object.
1 parent 1f1d644 commit f6d11c9

File tree

3 files changed

+152
-25
lines changed

3 files changed

+152
-25
lines changed

CHANGELOG.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ adheres to `Semantic Versioning <http://semver.org/spec/v2.0.0.html>`_.
1111
`Unreleased`_
1212
-------------
1313

14-
Nothing yet
14+
Bugfix
15+
~~~~~~
16+
17+
- Fixes issue #382: When providing a ``MicrophoneArray`` object with
18+
directivity to ``Room.add_microphone_array``, the directivity was dropped
19+
from the object.
1520

1621
`0.8.2`_ - 2024-11-06
1722
---------------------

pyroomacoustics/room.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -1980,7 +1980,7 @@ def add(self, obj):
19801980
).format(self.dim, obj.dim)
19811981
)
19821982

1983-
if "mic_array" not in self.__dict__ or self.mic_array is None:
1983+
if not hasattr(self, "mic_array") or self.mic_array is None:
19841984
self.mic_array = obj
19851985
else:
19861986
self.mic_array.append(obj)
@@ -2046,6 +2046,12 @@ def add_microphone_array(self, mic_array, directivity=None):
20462046
As an alternative, a
20472047
:py:obj:`~pyroomacoustics.beamforming.MicrophoneArray` can be
20482048
provided.
2049+
directivity: list of Directivity objects, optional
2050+
If ``mic_array`` is provided as a numpy array, an optional
2051+
:py:obj:`~pyroomacoustics.directivities.Directivity` object or
2052+
list thereof can be provided.
2053+
If ``mic_array`` is a MicrophoneArray object, passing an argument here
2054+
will result in an error.
20492055
20502056
Returns
20512057
-------
@@ -2064,7 +2070,12 @@ def add_microphone_array(self, mic_array, directivity=None):
20642070
mic_array = MicrophoneArray(mic_array, self.fs, directivity)
20652071
else:
20662072
# if the type is microphone array
2067-
mic_array.set_directivity(directivity)
2073+
if directivity is not None:
2074+
raise ValueError(
2075+
"When providing a MicrophoneArray object, the directivities should "
2076+
"be provided in the object, not via the `directivity` parameter "
2077+
"of this method."
2078+
)
20682079

20692080
if self.simulator_state["rt_needed"] and mic_array.is_directive:
20702081
raise NotImplementedError("Directivity not supported with ray tracing.")

pyroomacoustics/tests/test_room_add.py

+133-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import numpy as np
2+
import pytest
23

34
import pyroomacoustics as pra
45

@@ -8,35 +9,82 @@
89
source_loc1 = [3.5, 7.7, 2.1]
910
mic0 = [7, 8, 3.9]
1011
mic1 = [7.87, 3.6, 6.1]
12+
mic_dir0 = pra.FigureEight(
13+
orientation=pra.DirectionVector(azimuth=90, colatitude=15, degrees=True)
14+
)
15+
mic_dir1 = pra.FigureEight(
16+
orientation=pra.DirectionVector(azimuth=180, colatitude=15, degrees=True)
17+
)
18+
src_dir0 = pra.FigureEight(
19+
orientation=pra.DirectionVector(azimuth=270, colatitude=15, degrees=True)
20+
)
21+
src_dir1 = pra.FigureEight(
22+
orientation=pra.DirectionVector(azimuth=0, colatitude=15, degrees=True)
23+
)
24+
25+
26+
@pytest.mark.parametrize("with_dir", ((True,), (False,)))
27+
def test_add_source_mic(with_dir):
28+
room = pra.ShoeBox(room_size)
1129

30+
if with_dir:
31+
sdir0 = src_dir0
32+
sdir1 = src_dir1
33+
mdir0 = mic_dir0
34+
mdir1 = mic_dir1
35+
else:
36+
sdir0 = sdir1 = None
37+
mdir0 = mdir1 = None
1238

13-
def test_add_source_mic():
14-
room = pra.ShoeBox(room_size).add_source(source_loc0).add_microphone(mic0)
39+
room = (
40+
pra.ShoeBox(room_size)
41+
.add_source(source_loc0, directivity=sdir0)
42+
.add_microphone(mic0, directivity=mdir0)
43+
)
1544

1645
assert len(room.sources) == 1
1746
assert np.allclose(room.sources[0].position, source_loc0)
1847
assert len(room.mic_array) == 1
1948
assert room.mic_array.R.shape == (3, 1)
2049
assert np.allclose(room.mic_array.R[:, 0], mic0)
50+
# Test directivities.
51+
assert room.sources[0].directivity is sdir0
52+
assert all(d is md for d, md in zip(room.mic_array.directivity, [mdir0]))
2153

22-
room.add_microphone(mic1).add_source(source_loc1)
54+
room.add_microphone(mic1, directivity=mdir1).add_source(
55+
source_loc1, directivity=sdir1
56+
)
2357

2458
assert len(room.sources) == 2
2559
assert np.allclose(room.sources[1].position, source_loc1)
2660
assert len(room.mic_array) == 2
2761
assert np.allclose(room.mic_array.R[:, 0], mic0)
2862
assert np.allclose(room.mic_array.R[:, 1], mic1)
2963
assert room.mic_array.R.shape == (3, 2)
64+
# Test directivities.
65+
assert room.sources[0].directivity is sdir0
66+
assert room.sources[1].directivity is sdir1
67+
assert all(d is md for d, md in zip(room.mic_array.directivity, [mdir0, mdir1]))
3068

3169

32-
def test_add_source_mic_obj():
70+
@pytest.mark.parametrize("with_dir", ((True,), (False,)))
71+
def test_add_source_mic_obj(with_dir):
3372
room = pra.ShoeBox(room_size)
3473

35-
source0 = pra.SoundSource(source_loc0, signal=sig)
36-
source1 = pra.SoundSource(source_loc1, signal=sig)
74+
if with_dir:
75+
sdir0 = src_dir0
76+
sdir1 = src_dir1
77+
mdir0 = mic_dir0
78+
mdir1 = mic_dir1
79+
else:
80+
sdir0 = sdir1 = None
81+
mdir0 = mdir1 = None
82+
83+
source0 = pra.SoundSource(source_loc0, signal=sig, directivity=sdir0)
84+
source1 = pra.SoundSource(source_loc1, signal=sig, directivity=sdir1)
3785

38-
mic_array0 = pra.MicrophoneArray(np.c_[mic0], fs=room.fs)
39-
mic_array1 = pra.MicrophoneArray(np.c_[mic1], fs=room.fs)
86+
mic_array0 = pra.MicrophoneArray(np.c_[mic0], fs=room.fs, directivity=mdir0)
87+
mic_array1 = pra.MicrophoneArray(np.c_[mic1], fs=room.fs, directivity=mdir1)
4088

4189
room.add(source0).add(mic_array0)
4290

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

49100
room.add(mic_array1).add(source1)
50101

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

58113

59-
def test_add_source_mic_obj_2():
114+
@pytest.mark.parametrize("with_dir", ((True,), (False,)))
115+
def test_add_source_mic_obj_2(with_dir):
60116
room = pra.ShoeBox(room_size)
61117

62-
source0 = pra.SoundSource(source_loc0, signal=sig)
63-
source1 = pra.SoundSource(source_loc1, signal=sig)
64-
mic_array = pra.MicrophoneArray(np.c_[mic0, mic1], fs=room.fs)
118+
if with_dir:
119+
sdir0 = src_dir0
120+
sdir1 = src_dir1
121+
mdir = [mic_dir0, mic_dir1]
122+
else:
123+
sdir0 = sdir1 = None
124+
mdir = [None, None]
125+
126+
source0 = pra.SoundSource(source_loc0, signal=sig, directivity=sdir0)
127+
source1 = pra.SoundSource(source_loc1, signal=sig, directivity=sdir1)
128+
mic_array = pra.MicrophoneArray(np.c_[mic0, mic1], fs=room.fs, directivity=mdir)
65129

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

@@ -72,15 +136,40 @@ def test_add_source_mic_obj_2():
72136
assert np.allclose(room.mic_array.R[:, 0], mic0)
73137
assert np.allclose(room.mic_array.R[:, 1], mic1)
74138
assert room.mic_array.R.shape == (3, 2)
139+
# Test directivities.
140+
assert room.sources[0].directivity is sdir0
141+
assert room.sources[1].directivity is sdir1
142+
assert all(d is md for d, md in zip(room.mic_array.directivity, mdir))
143+
144+
145+
def test_add_source_mic_obj_with_dir_error():
146+
room = pra.ShoeBox(room_size)
147+
148+
mic_array = pra.MicrophoneArray(np.c_[mic0, mic1], fs=room.fs)
149+
150+
with pytest.raises(ValueError):
151+
room.add_microphone_array(mic_array, directivity=[mic_dir0, mic_dir1])
152+
75153

154+
@pytest.mark.parametrize("with_dir", ((True,), (False,)))
155+
def test_add_source_mic_ndarray(with_dir):
156+
if with_dir:
157+
sdir0 = src_dir0
158+
sdir1 = src_dir1
159+
mdir = [mic_dir0, mic_dir1]
160+
else:
161+
sdir0 = sdir1 = None
162+
mdir = [None, None]
76163

77-
def test_add_source_mic_ndarray():
78-
source0 = pra.SoundSource(source_loc0, signal=sig)
79-
source1 = pra.SoundSource(source_loc1, signal=sig)
164+
source0 = pra.SoundSource(source_loc0, signal=sig, directivity=sdir0)
165+
source1 = pra.SoundSource(source_loc1, signal=sig, directivity=sdir1)
80166
mic_array = np.c_[mic0, mic1]
81167

82168
room = (
83-
pra.ShoeBox(room_size).add(source0).add(source1).add_microphone_array(mic_array)
169+
pra.ShoeBox(room_size)
170+
.add(source0)
171+
.add(source1)
172+
.add_microphone_array(mic_array, directivity=mdir)
84173
)
85174

86175
assert len(room.sources) == 2
@@ -90,14 +179,32 @@ def test_add_source_mic_ndarray():
90179
assert np.allclose(room.mic_array.R[:, 0], mic0)
91180
assert np.allclose(room.mic_array.R[:, 1], mic1)
92181
assert room.mic_array.R.shape == (3, 2)
93-
94-
95-
def test_add_source_mic_ndarray_2():
96-
source0 = pra.SoundSource(source_loc0, signal=sig)
97-
source1 = pra.SoundSource(source_loc1, signal=sig)
182+
# Test directivities.
183+
assert room.sources[0].directivity is sdir0
184+
assert room.sources[1].directivity is sdir1
185+
assert all(d is md for d, md in zip(room.mic_array.directivity, mdir))
186+
187+
188+
@pytest.mark.parametrize("with_dir", ((True,), (False,)))
189+
def test_add_source_mic_ndarray_2(with_dir):
190+
if with_dir:
191+
sdir0 = src_dir0
192+
sdir1 = src_dir1
193+
mdir = [mic_dir0, mic_dir1]
194+
else:
195+
sdir0 = sdir1 = None
196+
mdir = [None, None]
197+
198+
source0 = pra.SoundSource(source_loc0, signal=sig, directivity=sdir0)
199+
source1 = pra.SoundSource(source_loc1, signal=sig, directivity=sdir1)
98200
mic_array = np.c_[mic0, mic1]
99201

100-
room = pra.ShoeBox(room_size).add(source0).add(source1).add_microphone(mic_array)
202+
room = (
203+
pra.ShoeBox(room_size)
204+
.add(source0)
205+
.add(source1)
206+
.add_microphone(mic_array, directivity=mdir)
207+
)
101208

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

110221

111222
if __name__ == "__main__":

0 commit comments

Comments
 (0)