Skip to content

Commit 082eea5

Browse files
committed
Harmonize the Sabine reverberation time function with Eyrings reverberation time function.
1 parent 2df8561 commit 082eea5

File tree

2 files changed

+86
-69
lines changed

2 files changed

+86
-69
lines changed

pyrato/parametric.py

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -333,53 +333,64 @@ def reverberation_time_eyring(
333333
return reverberation_time
334334

335335

336-
def calculate_sabine_reverberation_time(surfaces, alphas, volume):
337-
"""Calculate the reverberation time using Sabine's equation.
336+
def reverberation_time_sabine(
337+
volume: float,
338+
surface_area: float,
339+
mean_absorption: Union[float, np.ndarray],
340+
speed_of_sound: float = 343.4,
341+
) -> np.ndarray:
342+
r"""
343+
Calculate the reverberation time in rooms as defined by Wallace Sabine.
338344
339-
Calculation according to [#]_.
345+
The reverberation time is calculated according to Ref. [#]_ as
346+
347+
.. math::
348+
T_{60} = \frac{24 \cdot \ln(10)}{c}
349+
\cdot \frac{V}{S\tilde{\alpha}}
350+
351+
where :math:`V` is the room volume, :math:`S` is the total surface area
352+
of the room, :math:`\tilde{\alpha}` is the average absorption
353+
coefficient of the room surfaces, and :math:`c` is the speed of sound.
340354
341355
Parameters
342356
----------
343-
surfaces : ndarray, double
344-
Surface areas of all surfaces in the room in square meters.
345-
alphas : ndarray, double
346-
Absorption coefficients corresponding to each surface
347-
volume : double
348-
Room volume in cubic meters
349-
350-
The shape of `surfaces` and `alphas` must match.
357+
surface_area : float
358+
Total surface area of the room in :math:`\mathrm{m}^2`.
359+
mean_absorption : float, numpy.ndarray
360+
Average absorption coefficient of room surfaces between 0 and 1. If
361+
an array is passed, the reverberation time is calculated for each value
362+
in the array.
363+
volume : float
364+
Room volume in :math:`\mathrm{m}^3`.
365+
speed_of_sound : float
366+
Speed of sound in m/s. Default is 343.4 m/s, which corresponds to the
367+
speed of sound in air at 20 °C.
351368
352369
Returns
353370
-------
354-
reverberation_time_sabine : double
355-
The value of calculated reverberation time in seconds
371+
numpy.ndarray
372+
Reverberation time in seconds.
356373
357374
References
358375
----------
359376
.. [#] H. Kuttruff, Room acoustics, 4th Ed. Taylor & Francis, 2009.
360377
361378
"""
362-
surfaces = np.asarray(surfaces)
363-
alphas = np.asarray(alphas)
364-
365-
if alphas.shape != surfaces.shape:
366-
raise ValueError("Size of alphas and surfaces " \
367-
"ndarray sizes must match.")
368379

369-
if np.any(alphas) < 0 or np.any(alphas > 1):
370-
raise ValueError("Absorption coefficient values must "\
371-
f"be in range [0, 1]. Got {alphas}.")
372-
if np.any(surfaces < 0):
373-
raise ValueError("Surface areas cannot "\
374-
f"be negative. Got {surfaces}.")
375-
if volume < 0:
376-
raise ValueError(f"Volume cannot be negative. Got {volume}.")
380+
if speed_of_sound <= 0:
381+
raise ValueError("Speed of sound should be larger than 0")
382+
if volume <= 0:
383+
raise ValueError("Volume should be larger than 0")
384+
if surface_area <= 0:
385+
raise ValueError("Surface area should be larger than 0")
377386

378-
absorption_area = np.sum(surfaces*alphas)
387+
mean_absorption = np.asarray(mean_absorption)
388+
if np.any(mean_absorption < 0) or np.any(mean_absorption > 1):
389+
raise ValueError("mean_absorption should be between 0 and 1")
379390

380-
if absorption_area == 0:
381-
raise ZeroDivisionError("Absorption area should be positive.")
391+
factor = 24 * np.log(10) / speed_of_sound
382392

383-
reverberation_time_sabine = 0.161*volume/(absorption_area)
393+
with np.errstate(divide='ignore'):
394+
reverberation_time = factor * volume / (surface_area * mean_absorption)
384395

385-
return reverberation_time_sabine
396+
return reverberation_time

tests/test_parametric.py

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,53 +41,59 @@ def test_mean_free_path_wrong_surface_area():
4141
mean_free_path(100, -1)
4242

4343

44-
def test_calculate_sabine_reverberation_time():
45-
alphas = [0.9, 0.1]
46-
surfaces = [2, 5*2]
47-
volume = 2*2*2
48-
assert ra.parametric.calculate_sabine_reverberation_time(surfaces,
49-
alphas, volume) == 0.46
50-
51-
def test_sabine_array_size_match():
52-
message = "Size of alphas and surfaces ndarray sizes must match."
53-
alphas = [0.9, 0.1, 0.3]
54-
surfaces = [2, 5*2]
55-
volume = 2*2*2
44+
def test_reverberation_time_sabine():
45+
volume = 2 * 2 * 2
46+
surface_area = 2 + 5 * 2
47+
mean_absorption = (2 * 0.9 + 5 * 2 * 0.1) / surface_area
48+
npt.assert_allclose(
49+
ra.parametric.reverberation_time_sabine(
50+
volume, surface_area, mean_absorption),
51+
0.46,
52+
atol=1e-2,
53+
)
54+
55+
def test_sabine_speed_of_sound_non_positive():
56+
message = "Speed of sound should be larger than 0"
57+
volume = 8
58+
surface_area = 12
59+
mean_absorption = 0.2
5660
with pytest.raises(ValueError, match=message):
57-
ra.parametric.calculate_sabine_reverberation_time(surfaces,
58-
alphas, volume)
61+
ra.parametric.reverberation_time_sabine(
62+
volume, surface_area, mean_absorption, speed_of_sound=0)
5963

6064
def test_sabine_alphas_outside_range():
61-
message=r"Absorption coefficient values must be in range"
62-
alphas = [2, 0]
63-
surfaces = [1, 1]
64-
volume = 2*2*2
65-
with pytest.raises(ValueError, match = message):
66-
ra.parametric.calculate_sabine_reverberation_time(surfaces,
67-
alphas, volume)
65+
message = r"mean_absorption should be between 0 and 1"
66+
volume = 8
67+
surface_area = 2
68+
mean_absorption = [2, 0]
69+
with pytest.raises(ValueError, match=message):
70+
ra.parametric.reverberation_time_sabine(
71+
volume, surface_area, mean_absorption)
6872

6973
def test_sabine_negative_surfaces():
70-
message = r"Surface areas cannot be negative"
71-
alphas = [1, 0]
72-
surfaces = [-1, 1]
73-
volume = 2*2*2
74+
message = r"Surface area should be larger than 0"
75+
volume = 8
76+
surface_area = -1
77+
mean_absorption = 0.5
7478
with pytest.raises(ValueError, match=message):
75-
ra.parametric.calculate_sabine_reverberation_time(surfaces,
76-
alphas, volume)
79+
ra.parametric.reverberation_time_sabine(
80+
volume, surface_area, mean_absorption)
7781

7882
def test_sabine_negative_volume():
79-
message=r"Volume cannot be negative."
80-
alphas = [1, 0]
81-
surfaces = [1, 1]
83+
message = r"Volume should be larger than 0"
8284
volume = -3
85+
surface_area = 2
86+
mean_absorption = 0.5
8387
with pytest.raises(ValueError, match=message):
84-
ra.parametric.calculate_sabine_reverberation_time(surfaces,
85-
alphas, volume)
88+
ra.parametric.reverberation_time_sabine(
89+
volume, surface_area, mean_absorption)
8690

8791
def test_sabine_zero_absorption_area():
88-
alphas = [1, 0]
89-
surfaces = [0, 1]
92+
mean_absorption = 0
93+
surface_area = 1
9094
volume = 3
91-
with pytest.raises(ZeroDivisionError):
92-
ra.parametric.calculate_sabine_reverberation_time(surfaces,
93-
alphas, volume)
95+
npt.assert_equal(
96+
ra.parametric.reverberation_time_sabine(
97+
volume, surface_area, mean_absorption),
98+
np.inf,
99+
)

0 commit comments

Comments
 (0)