Skip to content

Commit d344e84

Browse files
authored
Merge pull request #72 from Eomys/minor
Minor corrections
2 parents ab69768 + 6a56d71 commit d344e84

File tree

20 files changed

+203
-508
lines changed

20 files changed

+203
-508
lines changed

README.md

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,6 @@ MOSQITO is available on [pip](https://pypi.org/project/pip/). Simply type in a s
4242

4343
This command line should download and install MOSQITO on your computer, along with the dependencies needed to compute SQ metrics.
4444

45-
If you need to import .uff or .unv files, you will need the pyuff package dependency. Note that 'pyuff' is released under the GPL license which prevents MOSQITO from being used in other software that must be under a more permissive license. To include the 'pyuff' dependancy anyway, type the following command:
46-
47-
pip install mosqito[uff]
48-
49-
If you want to use MOSQITO coupled with SciDataTool, you will need SDT package dependency. To install it along with MOSQITO, use the following command:
50-
51-
pip install mosqito[SciDataTool]
52-
53-
Note that all the depencies can be installed at once using:
54-
55-
pip install mosqito[all]
56-
5745
## Contact
5846

5947
You can contact us on Github by opening an issue (to request a feature, ask a question or report a bug).

full_requirements.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ numpy
22
scipy
33
matplotlib
44
pyuff
5-
pytest
6-
scidatatool
5+
pytest>=5.4.1
6+
pandas
7+
openpyxl

mosqito/sq_metrics/loudness/loudness_zwst/loudness_zwst.py

Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,8 @@
99
from mosqito.sq_metrics.loudness.loudness_zwst._calc_slopes import _calc_slopes
1010
from mosqito.utils.conversion import amp2db
1111

12-
# Optional package import
13-
try:
14-
from SciDataTool import DataTime, DataLinspace, DataFreq
15-
except ImportError:
16-
DataTime = None
17-
DataLinspace = None
18-
DataFreq = None
1912

20-
21-
def loudness_zwst(signal, fs=None, field_type="free", is_sdt_output=False):
13+
def loudness_zwst(signal, fs, field_type="free"):
2214
"""Zwicker-loudness calculation for stationary signals
2315
2416
Calculates the acoustic loudness according to Zwicker method for
@@ -36,33 +28,32 @@ def loudness_zwst(signal, fs=None, field_type="free", is_sdt_output=False):
3628
3729
Parameters
3830
----------
39-
signal : numpy.array or DataTime object
31+
signal : numpy.array
4032
Signal time values [Pa]
41-
fs : float, optional
42-
Sampling frequency, can be omitted if the input is a DataTime
43-
object. Default to None
33+
fs : float
34+
Sampling frequency [Hz]
4435
field_type : str
4536
Type of soundfield corresponding to spec_third ("free" by
4637
default or "diffuse").
47-
is_sdt_output : Bool, optional
48-
If True, the outputs are returned as SciDataTool objects.
49-
Default to False
5038
5139
Outputs
5240
-------
5341
N : float or numpy.array
5442
The overall loudness array [sones], size (Ntime,).
55-
N_specific : numpy.ndarray or DataFreq object
43+
N_specific : numpy.ndarray
5644
The specific loudness array [sones/bark], size (Nbark, Ntime).
5745
bark_axis: numpy.array
5846
The Bark axis array, size (Nbark,).
5947
"""
6048

61-
# Manage SciDataTool input type
62-
if DataTime is not None and isinstance(signal, DataTime):
63-
time = signal.get_along("time")["time"]
64-
fs = 1 / (time[1] - time[0])
65-
signal = signal.get_along("time")[signal.symbol]
49+
if fs < 48000:
50+
print(
51+
"[Warning] Signal resampled to 48 kHz to allow calculation. To fulfill the standard requirements fs should be >=48 kHz."
52+
)
53+
from scipy.signal import resample
54+
55+
signal = resample(signal, int(48000 * len(signal) / fs))
56+
fs = 48000
6657

6758
# Compute third octave band spectrum
6859
spec_third, _ = noct_spectrum(signal, fs, fmin=24, fmax=12600)
@@ -80,27 +71,4 @@ def loudness_zwst(signal, fs=None, field_type="free", is_sdt_output=False):
8071
# Define Bark axis
8172
bark_axis = np.linspace(0.1, 24, int(24 / 0.1))
8273

83-
# Manage SciDataTool output type
84-
if is_sdt_output:
85-
if DataLinspace is None:
86-
raise RuntimeError(
87-
"In order to handle Data objects you need the 'SciDataTool' package."
88-
)
89-
else:
90-
bark_data = DataLinspace(
91-
name="Critical band rate",
92-
unit="Bark",
93-
initial=0,
94-
final=24,
95-
number=int(24 / 0.1),
96-
include_endpoint=True,
97-
)
98-
N_specific = DataFreq(
99-
name="Specific loudness (Zwicker method for stationnary signal)",
100-
symbol="N'_{zwst}",
101-
axes=[bark_data],
102-
values=N_specific,
103-
unit="sone/Bark",
104-
)
105-
10674
return N, N_specific, bark_axis

mosqito/sq_metrics/loudness/loudness_zwst/loudness_zwst_freq.py

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# -*- coding: utf-8 -*-
22

33
# Third party imports
4-
import numpy as np
4+
from numpy import linspace, arange, tile, diff
5+
from scipy.interpolate import interp1d
56

67
# Local application imports
78
from mosqito.sound_level_meter.noct_spectrum.noct_synthesis import noct_synthesis
@@ -45,8 +46,45 @@ def loudness_zwst_freq(spectrum, freqs, field_type="free"):
4546
bark_axis : numpy.array
4647
Frequency axis in bark, size (Nbark,).
4748
"""
48-
if len(spectrum) != len(freqs):
49-
raise ValueError('Input spectrum and frequency axis must have the same shape')
49+
50+
# 1D spectrum
51+
if len(spectrum.shape) == 1:
52+
if len(spectrum) != len(freqs):
53+
raise ValueError(
54+
'Input spectrum and frequency axis do not have the same length')
55+
if (freqs.max() < 24000) or (freqs.min() > 24):
56+
print("[WARNING] freqs argument is not wide enough to cover the full audio range. Missing frequency bands will be filled with 0. To fulfill the standard requirements, the frequency axis should go from 24Hz up to 24 kHz."
57+
)
58+
df = freqs[1] - freqs[0]
59+
spectrum = interp1d(freqs, spectrum, axis=0, bounds_error=False, fill_value=0)(linspace(0, 24000, int(24000//df)))
60+
freqs = linspace(0, 24000, int(24000//df))
61+
# 2D spectrum
62+
elif len(spectrum.shape) > 1:
63+
nseg = spectrum.shape[1]
64+
# one frequency axis per segment
65+
if len(freqs.shape) > 1:
66+
if spectrum.shape != freqs.shape:
67+
raise ValueError(
68+
'Input spectrum and frequency axis do not have the same shape.')
69+
if (freqs.max() < 24000) or (freqs.min() > 24):
70+
print("[WARNING] freqs argument is not wide enough to cover the full audio range. Missing frequency bands will be filled with 0. To fulfill the standard requirements, the frequency axis should go from 24Hz up to 24 kHz."
71+
)
72+
df = diff(freqs, axis=0).min()
73+
nperseg = int(24000//df)
74+
spectrum = interp1d(freqs.ravel(), spectrum.ravel(), bounds_error=False, fill_value=0)(tile(linspace(0, 24000, nperseg), nseg)).reshape((nseg, nperseg)).T
75+
freqs = tile(linspace(0, 24000, nperseg), (nseg, 1)).T
76+
# one frequency axis for all the segments
77+
elif len(freqs.shape) == 1:
78+
if spectrum.shape[0] != len(freqs):
79+
raise ValueError(
80+
'Input spectra and frequency axis do not have the same length. "freqs" must have dim(nperseg) and "spectra" must have dim(nperseg,nseg).')
81+
if (freqs.max() < 24000) or (freqs.min() > 24):
82+
print("[WARNING] freqs argument is not wide enough to cover the full audio range. Missing frequency bands will be filled with 0. To fulfill the standard requirements, the frequency axis should go from 24Hz up to 24 kHz."
83+
)
84+
df = freqs[1] - freqs[0]
85+
nperseg = int(24000//df)
86+
spectrum = interp1d(freqs, spectrum, axis=0, bounds_error=False, fill_value=0)(linspace(0, 24000, nperseg))
87+
freqs = tile(linspace(0, 24000, nperseg), (nseg, 1)).T
5088

5189
# Compute third octave band spectrum
5290
spec_third, _ = noct_synthesis(spectrum, freqs, fmin=24, fmax=12600)
@@ -62,6 +100,6 @@ def loudness_zwst_freq(spectrum, freqs, field_type="free"):
62100
N, N_specific = _calc_slopes(Nm)
63101

64102
# Define Bark axis
65-
bark_axis = np.linspace(0.1, 24, int(24 / 0.1))
103+
bark_axis = linspace(0.1, 24, int(24 / 0.1))
66104

67105
return N, N_specific, bark_axis

mosqito/sq_metrics/loudness/loudness_zwst/loudness_zwst_perseg.py

Lines changed: 14 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,8 @@
44
from mosqito.utils import time_segmentation
55
from mosqito.sq_metrics.loudness.loudness_zwst.loudness_zwst import loudness_zwst
66

7-
# Optional package import
8-
try:
9-
from SciDataTool import DataTime, DataLinspace, DataFreq, Norm_func
10-
except ImportError:
11-
DataTime = None
12-
DataLinspace = None
13-
DataFreq = None
147

15-
16-
def loudness_zwst_perseg(
17-
signal, fs=None, nperseg=4096, noverlap=None, field_type="free", is_sdt_output=False
18-
):
8+
def loudness_zwst_perseg(signal, fs, nperseg=4096, noverlap=None, field_type="free"):
199
"""Zwicker-loudness calculation for stationary signals
2010
2111
Calculates the acoustic loudness according to Zwicker method for
@@ -33,10 +23,10 @@ def loudness_zwst_perseg(
3323
3424
Parameters
3525
----------
36-
signal : numpy.array or DataTime object
26+
signal : numpy.array
3727
Time signal values [Pa].
38-
fs : float, optional
39-
Sampling frequency, can be omitted if the input is a DataTime
28+
fs : float
29+
Sampling frequency [Hz]
4030
object. Default to None
4131
nperseg: int, optional
4232
Length of each segment. Defaults to 4096.
@@ -46,74 +36,32 @@ def loudness_zwst_perseg(
4636
field_type : str
4737
Type of soundfield corresponding to spec_third ("free" by
4838
default or "diffuse").
49-
is_sdt_output : Bool, optional
50-
If True, the outputs are returned as SciDataTool objects.
51-
Default to False
5239
5340
Outputs
5441
-------
55-
N : numpy.array or DataTime object
42+
N : numpy.array
5643
The overall loudness array [sones], size (Ntime,).
57-
N_specific : numpy.ndarray or DataFreq object
44+
N_specific : numpy.ndarray
5845
The specific loudness array [sones/bark], size (Nbark, Ntime).
5946
bark_axis: numpy.array
6047
The Bark axis array, size (Nbark,).
6148
time_axis: numpy.array
62-
The time axis array, size (Ntime,) or None.
49+
The time axis array, size (Ntime,).
6350
6451
"""
52+
if fs < 48000:
53+
print(
54+
"[Warning] Signal resampled to 48 kHz to allow calculation. To fulfill the standard requirements fs should be >=48 kHz."
55+
)
56+
from scipy.signal import resample
6557

66-
# Manage input type
67-
if DataTime is not None and isinstance(signal, DataTime):
68-
time = signal.get_along("time")["time"]
69-
fs = 1 / (time[1] - time[0])
70-
signal = signal.get_along("time")[signal.symbol]
58+
signal = resample(signal, int(48000 * len(signal) / fs))
59+
fs = 48000
7160

7261
# Time signal segmentation
7362
signal, time_axis = time_segmentation(signal, fs, nperseg, noverlap)
7463

7564
# Compute loudness
7665
N, N_specific, bark_axis = loudness_zwst(signal, fs, field_type=field_type)
7766

78-
# Manage SciDataTool output type
79-
if is_sdt_output:
80-
if DataLinspace is None:
81-
raise RuntimeError(
82-
"In order to handle Data objects you need the 'SciDataTool' package."
83-
)
84-
else:
85-
bark_data = DataLinspace(
86-
name="Critical band rate",
87-
unit="Bark",
88-
initial=0,
89-
final=24,
90-
number=int(24 / 0.1),
91-
include_endpoint=True,
92-
normalizations={
93-
"Hz": Norm_func(function=lambda x: 1960 * (x + 0.53) / (26.28 - x))
94-
},
95-
)
96-
time = DataLinspace(
97-
name="time",
98-
unit="s",
99-
initial=time_axis[0],
100-
final=time_axis[-1],
101-
number=len(time_axis),
102-
include_endpoint=True,
103-
)
104-
N_specific = DataFreq(
105-
name="Specific loudness (Zwicker method for stationnary signal)",
106-
symbol="N'_{zwst}",
107-
axes=[bark_data, time],
108-
values=N_specific,
109-
unit="sone/Bark",
110-
)
111-
N = DataTime(
112-
name="Loudness (Zwicker method for stationnary signal)",
113-
symbol="N_{zwst}",
114-
axes=[time],
115-
values=N,
116-
unit="sone",
117-
)
118-
11967
return N, N_specific, bark_axis, time_axis

mosqito/sq_metrics/loudness/loudness_zwtv/loudness_zwtv.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ def loudness_zwtv(signal, fs, field_type='free'):
4545
4646
"""
4747

48+
if fs < 48000:
49+
print("[Warning] Signal resampled to 48 kHz to allow calculation. To fulfill the standard requirements fs should be >=48 kHz."
50+
)
51+
from scipy.signal import resample
52+
signal = resample(signal, int(48000 * len(signal) / fs))
53+
fs = 48000
54+
55+
4856
# Compute third octave band spectrum vs. time
4957
spec_third, time_axis, _ = _third_octave_levels(signal, fs)
5058

0 commit comments

Comments
 (0)