# -*- coding: utf-8 -*-
import numpy as np
from pyrato.rap import reverberation_time_linear_regression
import warnings
[docs]
def reverberation_time_energy_decay_curve(
energy_decay_curve,
T='T20'):
"""Estimate the reverberation time from a given energy decay curve.
The linear regression is performed using least squares error minimization
according to the ISO standard 3382 [#]_.
Parameters
----------
energy_decay_curve : ndarray, double
Energy decay curve. The time needs to be the arrays last dimension.
times : ndarray, double
Time vector corresponding to each sample of the EDC.
T : 'T20', 'T30', 'T40', 'T50', 'T60', 'EDT', 'LDT'
Decay interval to be used for the reverberation time extrapolation. EDT
corresponds to the early decay time extrapolated from the interval
[0, -10] dB, LDT corresponds to the late decay time extrapolated from
the interval [-25, -35] dB.
normalize : bool, True
Normalize the EDC to the steady state energy level
Returns
-------
reverberation_time : double
The reverberation time
References
----------
.. [#] ISO 3382, Acoustics - Measurement of the reverberation time of
rooms with reference to other acoustical parameters.
Examples
--------
Estimate the reverberation time from an energy decay curve.
>>> import numpy as np
>>> import pyfar as pf
>>> import pyrato as ra
>>> from pyrato.analytic import rectangular_room_rigid_walls
...
>>> L = np.array([8, 5, 3])/10
>>> source_pos = np.array([5, 3, 1.2])/10
>>> receiver_pos = np.array([1, 1, 1.2])/10
>>> rir, _ = rectangular_room_rigid_walls(
... L, source_pos, receiver_pos,
... reverberation_time=1, max_freq=1.5e3, n_samples=2**12,
... speed_of_sound=343.9, samplingrate=3e3)
>>> rir = rir/rir.time.max()
...
>>> awgn = pf.signals.noise(
... rir.n_samples, rms=10**(-50/20),
... sampling_rate=rir.sampling_rate)
>>> rir = rir + awgn
...
>>> edc = ra.energy_decay_curve_chu_lundeby(rir)
>>> t_20 = ra.reverberation_time_energy_decay_curve(edc, 'T20')
>>> t_20
... array([0.99526253])
"""
warnings.warn(
"This function will be deprecated in version 0.5.0 "
"Use pyrato.reverberation_time_linear_regression instead",
DeprecationWarning)
return reverberation_time_linear_regression(energy_decay_curve, T)
[docs]
def energy_decay_curve_analytic(
surfaces, alphas, volume, times, source=None,
receiver=None, method='eyring', c=343.4, frequency=None,
air_absorption=True):
"""Calculate the energy decay curve analytically by using Eyring's or
Sabine's equation [#]_.
Parameters
----------
surfaces : ndarray, double
Surface areas of all surfaces in the room
alphas : ndarray, double
Absorption coefficients corresponding to each surface
volume : double
Room volume
times : ndarray, double
Time vector for which the decay curve is calculated
source : Coordinates
Coordinate object with the source coordinates
receiver : Coordinates
Coordinate object with the receiver coordinates
method : 'eyring', 'sabine'
Use either Eyring's or Sabine's equation
c : double
Speed of sound
frequency : double, optional
Center frequency of the respective octave band. This is only used for
the air absorption calculation.
Returns
-------
energy_decay_curve : ndarray, double
The energy decay curve
References
----------
.. [#] H. Kuttruff, Room acoustics, 4th Ed. Taylor & Francis, 2009.
"""
alphas = np.asarray(alphas)
surfaces = np.asarray(surfaces)
surface_room = np.sum(surfaces)
alpha_mean = np.sum(surfaces*alphas) / surface_room
if air_absorption:
m = air_attenuation_coefficient(frequency)
else:
m = 0
if all([source, receiver]):
dist_source_receiver = np.linalg.norm(
source.cartesian - receiver.cartesian)
delay_direct = dist_source_receiver / c
else:
delay_direct = 0
if method == 'eyring':
energy_decay_curve = np.exp(
-c*(times - delay_direct) *
((-surface_room * np.log(1 - alpha_mean) / 4 / volume) + m))
elif method == 'sabine':
energy_decay_curve = np.exp(
-c*(times - delay_direct) *
((surface_room * alpha_mean / 4 / volume) + m))
else:
raise ValueError("The method has to be either 'eyring' or 'sabine'.")
return energy_decay_curve
[docs]
def air_attenuation_coefficient(
frequency,
temperature=20,
humidity=50,
atmospheric_pressure=101325):
"""Calculate the attenuation coefficient m for the absorption caused
by friction with the surrounding air.
Parameters
----------
frequency : double
The frequency for which the attenuation coefficient is calculated.
When processing in fractional octave bands use the center frequency.
temperature : double
Temperature in degrees Celsius.
humidity : double
Humidity in percent.
atmospheric_pressure : double
Atmospheric pressure.
Returns
-------
attenuation_coefficient : double
The resulting attenuation coefficient.
"""
# room temperature in Kelvin
t_K = temperature + 273.16
p_ref_kPa = 101.325
p_kPa = atmospheric_pressure/1000.0
# determine molar concentration of water vapor
tmp = (
(10.79586 * (1.0 - (273.16/t_K))) -
(5.02808 * np.log10((t_K/273.16))) +
(1.50474 * 0.0001 * (1.0 - 10.0 ** (-8.29692*((t_K/273.16) - 1.0)))) +
(0.42873 * 0.001 * (-1.0 + 10.0 ** (-4.76955*(1.0 - (273.16/t_K))))) -
2.2195983)
# molar concentration water vapor in percent
molar_water_vapor = (humidity * 10.0 ** tmp) / (p_kPa/p_ref_kPa)
# determine relaxation frequencies of oxygen and nitrogen
relax_oxygen = ((p_kPa/p_ref_kPa) * (24.0 + (
4.04 * 10000.0 * molar_water_vapor * (
(0.02 + molar_water_vapor) / (0.391 + molar_water_vapor)))))
relax_nitrogen = ((p_kPa/p_ref_kPa) * (
(t_K / 293.16) ** (-0.5)) *
(9.0 + 280.0 * molar_water_vapor * np.exp(
-4.17 * (((t_K / 293.16) ** (-0.3333333)) - 1.0))))
# Neper/m -> dB/m
air_abs_coeff = ((frequency**2 * (
(1.84 * 10.0**(-11.0) * (p_ref_kPa / p_kPa) * (t_K/293.16)**0.5) +
((t_K/293.16)**(-2.5) * (
(1.278 * 0.01 * np.exp(-2239.1/t_K) / (
relax_oxygen + (frequency**2)/relax_oxygen)) +
(1.068 * 0.1 * np.exp((-3352.0/t_K) / (
relax_nitrogen + (frequency**2)/relax_nitrogen))))))
) * 20.0 / np.log(10.0) / (np.log10(np.exp(1.0)) * 10.0))
return air_abs_coeff