Skip to content

Commit 033126f

Browse files
authored
Fixes from_corner/extrude for mixed single/multi-band materials (#388)
Fixes a bug where the single/multi-band materials is not properly handled when using from_corners together with extrude. (issue #381)
1 parent d8d5e84 commit 033126f

File tree

3 files changed

+120
-14
lines changed

3 files changed

+120
-14
lines changed

CHANGELOG.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ Bugfix
1818
directivity to ``Room.add_microphone_array``, the directivity was dropped
1919
from the object.
2020

21+
- Fixes issues #381: When creating a room with from_corners with multi-band
22+
material, and then making it 3D with ``extrude`` with a single band material
23+
for the floor and ceiling, an error would occur.
24+
After the fix, the materials for floor and ceiling are automatically extended
25+
to multi-band, as expected.
26+
2127
- Fixes issue #380: Caused by the attribute ``cartesian`` of ``GridSphere`` not
2228
being set properly when the grid is only initialized with a number of points.
2329

@@ -30,7 +36,7 @@ Bugfix
3036
Changed
3137
~~~~~~~
3238

33-
- Makes the ``pyroomacoustics.utilities.resample`` backend is made configurable
39+
- Makes the ``pyroomacoustics.utilities.resample`` backend configurable
3440
to avoid ``soxr`` dependency. The resample backend is configurable to
3541
``soxr``, ``samplerate``, if these packages are available, and otherwise
3642
falls back to ``scipy.signal.resample_poly``.

pyroomacoustics/room.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,24 +1395,45 @@ def extrude(self, height, v_vec=None, absorption=None, materials=None):
13951395
if libroom.area_2d_polygon(floor_corners) <= 0:
13961396
floor_corners = np.fliplr(floor_corners)
13971397

1398-
walls = []
1398+
wall_corners = {}
1399+
wall_materials = {}
13991400
for i in range(nw):
1400-
corners = np.array(
1401+
name = str(i)
1402+
wall_corners[name] = np.array(
14011403
[
14021404
np.r_[floor_corners[:, i], 0],
14031405
np.r_[floor_corners[:, (i + 1) % nw], 0],
14041406
np.r_[floor_corners[:, (i + 1) % nw], 0] + height * v_vec,
14051407
np.r_[floor_corners[:, i], 0] + height * v_vec,
14061408
]
14071409
).T
1408-
walls.append(
1409-
wall_factory(
1410-
corners,
1411-
self.walls[i].absorption,
1412-
self.walls[i].scatter,
1413-
name=str(i),
1410+
1411+
if len(self.walls[i].absorption) == 1:
1412+
# Single band
1413+
wall_materials[name] = Material(
1414+
energy_absorption=float(self.walls[i].absorption),
1415+
scattering=float(self.walls[i].scatter),
1416+
)
1417+
elif len(self.walls[i].absorption) == self.octave_bands.n_bands:
1418+
# Multi-band
1419+
abs_dict = {
1420+
"coeffs": self.walls[i].absorption,
1421+
"center_freqs": self.octave_bands.centers,
1422+
"description": "",
1423+
}
1424+
sca_dict = {
1425+
"coeffs": self.walls[i].scatter,
1426+
"center_freqs": self.octave_bands.centers,
1427+
"description": "",
1428+
}
1429+
wall_materials[name] = Material(
1430+
energy_absorption=abs_dict,
1431+
scattering=sca_dict,
1432+
)
1433+
else:
1434+
raise ValueError(
1435+
"Encountered a material with inconsistent number of bands."
14141436
)
1415-
)
14161437

14171438
############################
14181439
# BEGIN COMPATIBILITY CODE #
@@ -1477,12 +1498,23 @@ def extrude(self, height, v_vec=None, absorption=None, materials=None):
14771498
# we need the floor corners to ordered clockwise (for the normal to point outward)
14781499
new_corners["floor"] = np.fliplr(new_corners["floor"])
14791500

1480-
for key in ["floor", "ceiling"]:
1501+
# Concatenate new walls param with old ones.
1502+
wall_corners.update(new_corners)
1503+
wall_materials.update(materials)
1504+
1505+
# If some of the materials used are multi-band, we need to resample
1506+
# all of them to have the same number of values
1507+
if not Material.all_flat(wall_materials):
1508+
for name, mat in wall_materials.items():
1509+
mat.resample(self.octave_bands)
1510+
1511+
walls = []
1512+
for key, corners in wall_corners.items():
14811513
walls.append(
14821514
wall_factory(
1483-
new_corners[key],
1484-
materials[key].absorption_coeffs,
1485-
materials[key].scattering_coeffs,
1515+
corners,
1516+
wall_materials[key].absorption_coeffs,
1517+
wall_materials[key].scattering_coeffs,
14861518
name=key,
14871519
)
14881520
)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import numpy as np
2+
import pytest
3+
4+
import pyroomacoustics as pra
5+
6+
_FS = 16000
7+
_MAX_ORDER = 3
8+
9+
10+
def test_from_corner_extrude():
11+
12+
room_dim = [3, 4, 5]
13+
# This test is sensitive to the selection of this value.
14+
# If some symmetries are present, for some reasons differences between
15+
# the two simulated methods happen.
16+
src_loc = [1.001, 0.999, 1.002]
17+
mic_loc = [2, 3, 4]
18+
mat = pra.Material(energy_absorption=0.1)
19+
20+
room_ref = pra.ShoeBox(room_dim, fs=_FS, max_order=_MAX_ORDER, materials=mat)
21+
room_ref.add_source(src_loc).add_microphone(mic_loc)
22+
room_ref.compute_rir()
23+
24+
# Now construct the same room with the other set of primitives.
25+
corners = np.array(
26+
[[0, 0], [room_dim[0], 0], [room_dim[0], room_dim[1]], [0, room_dim[1]]]
27+
).T
28+
room = pra.Room.from_corners(corners, fs=_FS, max_order=_MAX_ORDER, materials=mat)
29+
room.extrude(height=room_dim[2], materials=mat)
30+
room.add_source(src_loc).add_microphone(mic_loc)
31+
room.compute_rir()
32+
33+
assert np.allclose(room_ref.rir[0][0], room.rir[0][0], rtol=1e-4, atol=1e-4)
34+
35+
36+
def test_from_corner_extrude_different_materials():
37+
38+
room_dim = [3, 4, 5]
39+
# This test is sensitive to the selection of this value.
40+
# If some symmetries are present, for some reasons differences between
41+
# the two simulated methods happen.
42+
src_loc = [1.001, 0.999, 1.002]
43+
mic_loc = [2, 3, 4]
44+
mat1 = "hard_surface"
45+
mat2 = 0.1
46+
47+
materials = pra.make_materials(
48+
east=mat1, west=mat1, south=mat1, north=mat1, floor=mat2, ceiling=mat2
49+
)
50+
room_ref = pra.ShoeBox(room_dim, fs=_FS, max_order=_MAX_ORDER, materials=materials)
51+
room_ref.add_source(src_loc).add_microphone(mic_loc)
52+
room_ref.compute_rir()
53+
54+
# Now construct the same room with the other set of primitives.
55+
corners = np.array(
56+
[[0, 0], [room_dim[0], 0], [room_dim[0], room_dim[1]], [0, room_dim[1]]]
57+
).T
58+
room = pra.Room.from_corners(
59+
corners,
60+
fs=_FS,
61+
max_order=_MAX_ORDER,
62+
materials=pra.Material(energy_absorption=mat1),
63+
)
64+
room.extrude(height=room_dim[2], materials=pra.Material(energy_absorption=mat2))
65+
room.add_source(src_loc).add_microphone(mic_loc)
66+
room.compute_rir()
67+
68+
assert np.allclose(room_ref.rir[0][0], room.rir[0][0], rtol=1e-4, atol=1e-4)

0 commit comments

Comments
 (0)