Skip to content

Commit

Permalink
[CC] corrections following review before merge
Browse files Browse the repository at this point in the history
* precision of the standard name in the functions
* input checks in the freq_band_synthesis to prevent error linked to small spectrum
* all improts updated
  • Loading branch information
wantysal committed Dec 21, 2023
1 parent d24981d commit af68d18
Show file tree
Hide file tree
Showing 13 changed files with 77 additions and 70 deletions.
6 changes: 3 additions & 3 deletions mosqito/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
from mosqito.sq_metrics.sharpness.sharpness_din.sharpness_din_perseg import sharpness_din_perseg
from mosqito.sq_metrics.sharpness.sharpness_din.sharpness_din_freq import sharpness_din_freq

from mosqito.sq_metrics.speech_intelligibility.sii import sii
from mosqito.sq_metrics.speech_intelligibility.sii_freq import sii_freq
from mosqito.sq_metrics.speech_intelligibility.sii_level import sii_level
from mosqito.sq_metrics.speech_intelligibility.sii_ansi.sii_ansi import sii_ansi
from mosqito.sq_metrics.speech_intelligibility.sii_ansi.sii_ansi_freq import sii_ansi_freq
from mosqito.sq_metrics.speech_intelligibility.sii_ansi.sii_ansi_level import sii_ansi_level

from mosqito.sq_metrics.loudness.utils.sone_to_phon import sone_to_phon
from mosqito.utils.isoclose import isoclose
Expand Down
3 changes: 2 additions & 1 deletion mosqito/sound_level_meter/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from mosqito.sound_level_meter.noct_spectrum.noct_spectrum import noct_spectrum
from mosqito.sound_level_meter.noct_spectrum.noct_synthesis import noct_synthesis

from mosqito.sound_level_meter.spectrum import spectrum
from mosqito.sound_level_meter.freq_band_synthesis import freq_band_synthesis
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-

# Standard library import
from numpy import array, concatenate, zeros, log10, power, argmin, split
from numpy import array, concatenate, zeros, log10, power, argmin, split, arange, interp, iscomplex

def band_spectrum_synthesis(spectrum, freqs, fmin, fmax):
def freq_band_synthesis(spectrum, freqs, fmin, fmax):
"""Adapt input spectrum to frequency band levels
Convert the input spectrum to frequency band spectrum
Expand All @@ -12,7 +12,7 @@ def band_spectrum_synthesis(spectrum, freqs, fmin, fmax):
Parameters
----------
spectrum : numpy.ndarray
amplitude rms of the one-sided spectrum of the signal, size (nperseg, nseg).
One-sided spectrum of the signal in [dB], size (nperseg, nseg).
freqs : list
List of input frequency , size (nperseg) or (nperseg, nseg).
fmin : float
Expand All @@ -35,6 +35,20 @@ def band_spectrum_synthesis(spectrum, freqs, fmin, fmax):
fpref : numpy.ndarray
Corresponding preferred third octave band center frequencies, size (nbands).
"""
if iscomplex(spectrum).any():
raise ValueError('Input spectrum must be in dB, no complex value allowed.')

if (fmin.min() < freqs.min()):
print("[WARNING] Input spectrum minimum frequency if higher than fmin. Empty values will be filled with 0.")
df = freqs[1] - freqs[0]
spectrum = interp(arange(fmin.min(),fmax.max()+df, df), freqs, spectrum)
freqs = arange(fmin.min(),fmax.max()+df, df)

if (fmax.max() > freqs.max()):
print("[WARNING] Input spectrum maximum frequency if lower than fmax. Empty values will be filled with 0.")
df = freqs[1] - freqs[0]
spectrum = interp(arange(fmin.min(),fmax.max()+df, df), freqs, spectrum)
freqs = arange(fmin.min(),fmax.max()+df, df)

# Find the lower and upper index of each band
idx_low = argmin(abs(freqs[:,None] - fmin), axis=0)
Expand Down
6 changes: 3 additions & 3 deletions mosqito/sq_metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@

from mosqito.sq_metrics.loudness.utils.sone_to_phon import sone_to_phon

from mosqito.sq_metrics.speech_intelligibility.sii import sii
from mosqito.sq_metrics.speech_intelligibility.sii_freq import sii_freq
from mosqito.sq_metrics.speech_intelligibility.sii_level import sii_level
from mosqito.sq_metrics.speech_intelligibility.sii_ansi.sii_ansi import sii_ansi
from mosqito.sq_metrics.speech_intelligibility.sii_ansi.sii_ansi_freq import sii_ansi_freq
from mosqito.sq_metrics.speech_intelligibility.sii_ansi.sii_ansi_level import sii_ansi_level
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from numpy import array

def _get_critical_band_data():
""" See § 3.4 of the standard ANSI S3.5. """
CENTER_FREQUENCIES = array(
[
150,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

from numpy import array, zeros, log10, maximum, where, sum

from mosqito.sq_metrics.speech_intelligibility._band_procedure_data import _get_critical_band_data, _get_equal_critical_band_data, _get_octave_band_data, _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility._speech_data import _get_critical_band_speech_data, _get_equal_critical_band_speech_data, _get_octave_band_speech_data, _get_third_octave_band_speech_data
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._band_procedure_data import _get_critical_band_data, _get_equal_critical_band_data, _get_octave_band_data, _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._speech_data import _get_critical_band_speech_data, _get_equal_critical_band_speech_data, _get_octave_band_speech_data, _get_third_octave_band_speech_data
from mosqito.utils.LTQ import LTQ
from mosqito.utils.conversion import freq2bark

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from numpy import array

def _get_critical_band_speech_data(speech_level):

""" See § 3.4 of the standard ANSI S3.5. """

if speech_level == "normal":
SPEECH_SPECTRUM = array(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

from numpy import array, zeros

from mosqito.sq_metrics.speech_intelligibility._band_procedure_data import _get_critical_band_data, _get_equal_critical_band_data, _get_octave_band_data, _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility._speech_data import _get_critical_band_speech_data, _get_equal_critical_band_speech_data, _get_octave_band_speech_data, _get_third_octave_band_speech_data
from mosqito.sq_metrics.speech_intelligibility._main_sii import _main_sii
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._band_procedure_data import _get_critical_band_data, _get_equal_critical_band_data, _get_octave_band_data, _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._speech_data import _get_critical_band_speech_data, _get_equal_critical_band_speech_data, _get_octave_band_speech_data, _get_third_octave_band_speech_data
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._main_sii import _main_sii
from mosqito.sound_level_meter.spectrum import spectrum
from mosqito.sound_level_meter.band_spectrum_synthesis import band_spectrum_synthesis
from mosqito.sound_level_meter.freq_band_synthesis import freq_band_synthesis


def sii(noise, fs, method, speech_level, threshold=None):
def sii_ansi(noise, fs, method, speech_level, threshold=None):
"""Calculate speech intelligibility index
This function computes SII values for a noise time signal according to ANSI S3.5 standard.
Expand All @@ -21,7 +21,7 @@ def sii(noise, fs, method, speech_level, threshold=None):
fs: float
Sampling frequency of the input noise signal.
method: {"critical", "equally_critical", "third_octave", "octave"}
Type of frequency band to be used for the calculation.
Type of frequency band to be used for the calculation. See § 3.4 of the standard.
speech_level : {'normal', 'raised', 'loud', 'shout'}
Speech level to assess, the corresponding speech spectrum defined in the standard is used for calculation.
threshold : array_like or 'zwicker'
Expand Down Expand Up @@ -84,7 +84,7 @@ def sii(noise, fs, method, speech_level, threshold=None):

# Compute noise spectrum in dB
spec, freqs = spectrum(noise, fs, nfft="default", window="blackman", db=True)
noise_spectrum, _ = band_spectrum_synthesis(spec, freqs, LOWER_FREQUENCIES, UPPER_FREQUENCIES)
noise_spectrum, _ = freq_band_synthesis(spec, freqs, LOWER_FREQUENCIES, UPPER_FREQUENCIES)

SII, SII_specific, freq_axis = _main_sii(method, speech_spectrum, noise_spectrum, threshold)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-

from mosqito.sq_metrics.speech_intelligibility._band_procedure_data import _get_critical_band_data, _get_equal_critical_band_data, _get_octave_band_data, _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility._speech_data import _get_critical_band_speech_data, _get_equal_critical_band_speech_data, _get_octave_band_speech_data, _get_third_octave_band_speech_data
from mosqito.sq_metrics.speech_intelligibility._main_sii import _main_sii
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._band_procedure_data import _get_critical_band_data, _get_equal_critical_band_data, _get_octave_band_data, _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._speech_data import _get_critical_band_speech_data, _get_equal_critical_band_speech_data, _get_octave_band_speech_data, _get_third_octave_band_speech_data
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._main_sii import _main_sii

from mosqito.sound_level_meter.band_spectrum_synthesis import band_spectrum_synthesis
from mosqito.sound_level_meter.freq_band_synthesis import freq_band_synthesis

def sii_freq(spectrum, freqs, method, speech_level, threshold=None):
def sii_ansi_freq(spectrum, freqs, method, speech_level, threshold=None):
"""Calculate speech intelligibility index
This function computes SII values for a noise spectrum in dB according to ANSI S3.5 standard.
Expand All @@ -18,7 +18,7 @@ def sii_freq(spectrum, freqs, method, speech_level, threshold=None):
freqs: array_like
Frequency axis [Hz] of the spectrum.
method: {"critical", "equally_critical", "third_octave", "octave"}
Type of frequency band to be used for the calculation.
Type of frequency band to be used for the calculation. See § 3.4 of the standard.
speech_level : {'normal', 'raised', 'loud', 'shout'}
Speech level to assess, the corresponding speech spectrum defined in the standard is used for calculation.
threshold : array_like or 'zwicker'
Expand All @@ -44,7 +44,7 @@ def sii_freq(spectrum, freqs, method, speech_level, threshold=None):
.. plot::
:include-source:
>>> from mosqito.sq_metrics.speech_intelligibility import sii_freq
>>> from mosqito.sq_metrics import sii_ansi_freq
>>> from mosqito.sound_level_meter.spectrum import spectrum
>>> import matplotlib.pyplot as plt
>>> import numpy as np
Expand All @@ -57,7 +57,7 @@ def sii_freq(spectrum, freqs, method, speech_level, threshold=None):
>>> rms = np.sqrt(np.mean(np.power(stimulus, 2)))
>>> ampl = 0.00002 * np.power(10, dB / 20) / rms
>>> stimulus = stimulus * ampl
>>> spec, freqs = spectrum(stimulus, fs, db=False)
>>> spec, freqs = spectrum(stimulus, fs, db=True)
>>> SII, SII_spec, freq_axis = sii_freq(spec, freqs, method='critical', speech_level='normal')
>>> plt.plot(freq_axis, SII_spec)
>>> plt.xlabel("Frequency [Hz]")
Expand Down Expand Up @@ -88,13 +88,11 @@ def sii_freq(spectrum, freqs, method, speech_level, threshold=None):
nbands = len(speech_spectrum)

if (len(spectrum) != nbands) or (freqs != CENTER_FREQUENCIES).any():
noise_spectrum,_ = band_spectrum_synthesis(spectrum, freqs, LOWER_FREQUENCIES, UPPER_FREQUENCIES)
noise_spectrum,_ = freq_band_synthesis(spectrum, freqs, LOWER_FREQUENCIES, UPPER_FREQUENCIES)
else:
noise_spectrum = spectrum

SII, SII_specific, freq_axis = _main_sii(method, speech_spectrum, noise_spectrum, threshold)

return SII, SII_specific, freq_axis



Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@

from numpy import ones, log10, power

from mosqito.sq_metrics.speech_intelligibility._band_procedure_data import _get_critical_band_data, _get_equal_critical_band_data, _get_octave_band_data, _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility._speech_data import _get_critical_band_speech_data, _get_equal_critical_band_speech_data, _get_octave_band_speech_data, _get_third_octave_band_speech_data
from mosqito.sq_metrics.speech_intelligibility._main_sii import _main_sii
from mosqito.utils.LTQ import LTQ
from mosqito.utils.conversion import freq2bark
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._band_procedure_data import _get_critical_band_data, _get_equal_critical_band_data, _get_octave_band_data, _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._speech_data import _get_critical_band_speech_data, _get_equal_critical_band_speech_data, _get_octave_band_speech_data, _get_third_octave_band_speech_data
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._main_sii import _main_sii


def sii_level(noise_level, method, speech_level, threshold=None):
def sii_ansi_level(noise_level, method, speech_level, threshold=None):
"""Calculate speech intelligibility index
This function computes SII values for an overall noise level in dB according to ANSI S3.5 standard.
This value is used to create a uniform noise spectrum, with the same deduced level on each frequency band.
Parameters
----------
noise_level : float
Overall noise level in [dB ref. 2e-5 Pa]. This value is used to create a uniform noise spectrum.
Overall noise level in [dB ref. 2e-5 Pa].
method: {"critical", "equally_critical", "third_octave", "octave"}
Type of frequency band to be used for the calculation.
Type of frequency band to be used for the calculation. See § 3.4 of the standard.
speech_level : {'normal', 'raised', 'loud', 'shout'}
Speech level to assess, the corresponding speech spectrum defined in the standard is used for calculation.
threshold : array_like or 'zwicker'
Expand Down
2 changes: 1 addition & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ markers =
pr_st: marks tests related to stationary prominence ratio
pr_tv: marks tests related to time-varying prominence ratio
pr_freq: marks tests related to prominence ratio from a spectrum
sii: marks tests related to speech intelligibility
sii_ansi: marks tests related to speech intelligibility
noct_spectrum: marks tests related to n_octave spectra computation
noct_synthesis: marks test related to n_octave spectra adaptation
utils: marks tests related to utils functions
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
# -*- coding: utf-8 -*-

import numpy as np
from numpy import array, interp, linspace, concatenate, flip
from numpy import array, interp, linspace

from scipy.fft import fft, fftfreq, ifft
from scipy.fft import ifft

# Optional package import
try:
import pytest
except ImportError:
raise RuntimeError(
"In order to perform the tests you need the 'pytest' package.")
try:
from SciDataTool import DataLinspace, DataTime
except ImportError:
raise RuntimeError(
"In order to handle Data objects you need the 'SciDataTool' package."
)

# Local application imports
from mosqito.utils import load
from mosqito import sii, sii_freq, sii_level
from mosqito.sq_metrics.speech_intelligibility._main_sii import _main_sii
from mosqito import sii_ansi, sii_ansi_freq, sii_ansi_level
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._main_sii import _main_sii


@pytest.fixture
Expand All @@ -45,7 +38,7 @@ def test_signal():

return test_signal

@pytest.mark.sii # to skip or run sharpness test
@pytest.mark.sii_ansi # to skip or run sharpness test
def test_sii(test_signal):
"""Test function for the sharpness calculation of an audio signal
The input signals come from DIN 45692_2009E. The compliance is assessed
Expand All @@ -63,13 +56,13 @@ def test_sii(test_signal):
fs = test_signal["fs"]

# Compute sharpness
SII, _, _ = sii(sig, fs, 'third_octave', 'raised')
SII, _, _ = sii(sig, fs, 'critical', 'loud')
SII, _, _ = sii(sig, fs, 'equally_critical', 'shout')
SII, _, _ = sii(sig, fs, 'octave', 'normal')
SII, _, _ = sii_ansi(sig, fs, 'third_octave', 'raised')
SII, _, _ = sii_ansi(sig, fs, 'critical', 'loud')
SII, _, _ = sii_ansi(sig, fs, 'equally_critical', 'shout')
SII, _, _ = sii_ansi(sig, fs, 'octave', 'normal')


@pytest.mark.sii # to skip or run sharpness test
@pytest.mark.sii_ansi # to skip or run sharpness test
def test_sii_freq(test_signal):
"""Test function for the sharpness calculation of an time-varying audio signal.
Expand All @@ -85,28 +78,28 @@ def test_sii_freq(test_signal):
spec = test_signal["noise_spectrum"]
freqs = test_signal["freq_axis"]
# Compute sharpness
SII, _, _ = sii_freq(spec, freqs, "critical", 'loud')
SII, _, _ = sii_freq(spec, freqs, "equally_critical", 'raised')
SII, _, _ = sii_freq(spec, freqs, "third_octave", 'shout')
SII, _, _ = sii_freq(spec, freqs, "octave", 'normal')
SII, _, _ = sii_ansi_freq(spec, freqs, "critical", 'loud')
SII, _, _ = sii_ansi_freq(spec, freqs, "equally_critical", 'raised')
SII, _, _ = sii_ansi_freq(spec, freqs, "third_octave", 'shout')
SII, _, _ = sii_ansi_freq(spec, freqs, "octave", 'normal')

@pytest.mark.sii
@pytest.mark.sii_ansi
def test_sii_level():

# Compute sharpness
SII, _, _ = sii_level(60, 'critical', 'normal')
SII, _, _ = sii_level(60, 'equally_critical', 'raised')
SII, _, _ = sii_level(60, 'octave', 'loud')
SII, _, _ = sii_level(60, 'third_octave', 'shout')
SII, _, _ = sii_ansi_level(60, 'critical', 'normal')
SII, _, _ = sii_ansi_level(60, 'equally_critical', 'raised')
SII, _, _ = sii_ansi_level(60, 'octave', 'loud')
SII, _, _ = sii_ansi_level(60, 'third_octave', 'shout')


@pytest.mark.sii # to skip or run sharpness test
@pytest.mark.sii_ansi # to skip or run sharpness test
def test_main_sii(test_signal):
"""Test function for the sharpness calculation of an time-varying audio signal.
"""

SII, SII_spec, _ = _main_sii(test_signal["method"], test_signal["speech_spectrum"], test_signal["noise_spectrum"], threshold=None)
SII, _, _ = _main_sii(test_signal["method"], test_signal["speech_spectrum"], test_signal["noise_spectrum"], threshold=None)

assert check_compliance(SII, test_signal["SII"])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from numpy import array, empty, amin, amax, zeros, log10, maximum, float64

# Local application imports
from mosqito.sq_metrics.speech_intelligibility._band_procedure_data import _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility._main_sii import _main_sii
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._band_procedure_data import _get_third_octave_band_data
from mosqito.sq_metrics.speech_intelligibility.sii_ansi._main_sii import _main_sii

# Reference values from ANSI S3.5 standard
reference = empty(2, dtype=dict)
Expand Down

0 comments on commit af68d18

Please sign in to comment.