Skip to content

Commit 564f844

Browse files
author
Martin Glesser
authored
Merge pull request #51 from wantysal/roughness_correction
Roughness correction
2 parents 8e484e3 + 86c39fb commit 564f844

File tree

6 files changed

+150
-1117
lines changed

6 files changed

+150
-1117
lines changed

mosqito/sound_level_meter/spectrum.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from mosqito.utils.conversion import amp2db
88

99

10-
def spectrum(signal,fs, nfft='default', window='hanning',db=True):
10+
def spectrum(signal,fs, nfft='default', window='hanning', one_sided=True, db=True):
1111
"""
1212
Compute one-sided spectrum from a time signal in Pa.
1313
@@ -53,8 +53,12 @@ def spectrum(signal,fs, nfft='default', window='hanning',db=True):
5353
window = np.tile(window,(nseg,1)).T
5454

5555
# Creation of the spectrum by FFT
56-
spectrum = fft(signal * window, axis=0)[0:nfft//2] * 1.42
57-
freq_axis = np.arange(0, nfft//2, 1) * (fs / nfft)
56+
if one_sided == True:
57+
spectrum = fft(signal * window, n=nfft, axis=0)[0:nfft//2] * 1.42
58+
freq_axis = np.arange(1, nfft//2+1, 1) * (fs / nfft)
59+
else:
60+
spectrum = fft(signal * window, n=nfft, axis=0) * 1.42
61+
freq_axis = np.concatenate((np.arange(1, nfft//2+1, 1) * (fs / nfft), np.arange(nfft//2+1, 1, -1) * (fs / nfft)))
5862

5963
if db == True:
6064
# Conversion into dB level

mosqito/sq_metrics/roughness/roughness_dw/_roughness_dw_main_calc.py

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,15 @@
1212
)
1313
from mosqito.utils.conversion import freq2bark, db2amp, amp2db, bark2freq
1414

15-
16-
def _roughness_dw_main_calc(spectrum, freqs, fs, gzi, hWeight):
15+
def _roughness_dw_main_calc(spec, freq_axis, fs, gzi, hWeight):
1716
"""
1817
Daniel and Weber roughness main calculation
1918
2019
Parameters
2120
----------
22-
spectrum : array
21+
spec : array
2322
An amplitude or complex spectrum.
24-
freqs : array
23+
freq_axis : array
2524
Frequency axis in [Hz].
2625
fs : integer
2726
Sampling frequency.
@@ -36,29 +35,31 @@ def _roughness_dw_main_calc(spectrum, freqs, fs, gzi, hWeight):
3635
Roughness computed for the given spectrum.
3736
3837
"""
39-
40-
if len(spectrum) != len(freqs):
38+
if len(spec) != len(freq_axis):
4139
raise ValueError(
42-
"Spectrum and frequency axis should have the same number of points !"
40+
"spectrum and frequency axis should have the same number of points !"
4341
)
4442

45-
n = len(spectrum)
43+
# convert spectrum to 2-sided
44+
spec = np.concatenate((spec, spec[len(spec)::-1]))
45+
46+
n = len(spec)
4647
# Frequency axis in Bark
47-
barks = freq2bark(freqs)
48+
bark_axis = freq2bark(freq_axis)
4849
# Highest frequency
49-
nZ = np.arange(1, n + 1, 1)
50+
nZ = np.arange(1, n//2 + 1, 1)
5051

5152
# Calculate Zwicker a0 factor (transfer characteristic of the outer and inner ear)
5253
a0 = np.zeros((n))
53-
a0[nZ - 1] = db2amp(_ear_filter_coeff(barks), ref=1)
54-
spectrum = a0 * spectrum
54+
a0[nZ - 1] = db2amp(_ear_filter_coeff(bark_axis), ref=1)
55+
spec = a0 * spec
5556

56-
# Conversion of the spectrum into dB
57-
module = np.abs(spectrum)
57+
# Conversion of the spec into dB
58+
module = np.abs(spec[0:n//2])
5859
spec_dB = amp2db(module, ref=2e-5)
59-
60-
# Find the audible components within the spectrum
61-
threshold = LTQ(barks, reference="roughness")
60+
61+
# Find the audible components within the spec
62+
threshold = LTQ(bark_axis, reference="roughness")
6263
audible_index = np.where(spec_dB > threshold)[0]
6364
# Number of audible frequencies
6465
n_aud = len(audible_index)
@@ -73,7 +74,7 @@ def _roughness_dw_main_calc(spectrum, freqs, fs, gzi, hWeight):
7374
# upper slope [dB/Bark]
7475
for k in np.arange(0, n_aud, 1):
7576
s2[k] = min(
76-
-24 - (230 / freqs[audible_index[k]]) + (0.2 * spec_dB[audible_index[k]]),
77+
-24 - (230 / freq_axis[audible_index[k]]) + (0.2 * spec_dB[audible_index[k]]),
7778
0,
7879
)
7980

@@ -85,20 +86,20 @@ def _roughness_dw_main_calc(spectrum, freqs, fs, gzi, hWeight):
8586
zb = bark2freq(zi) * n / fs
8687
# Minimum excitation level
8788
minExcitDB = np.interp(zb, nZ, threshold)
88-
89+
8990
ch_low = np.zeros((n_aud))
9091
ch_high = np.zeros((n_aud))
9192
for i in np.arange(0, n_aud):
9293
# Lower limit of the channel corresponding to each component
93-
ch_low[i] = math.floor(2 * barks[audible_index[i]]) - 1
94+
ch_low[i] = math.floor(2 * bark_axis[audible_index[i]]) - 1
9495
# Higher limit
95-
ch_high[i] = math.ceil(2 * barks[audible_index[i]]) - 1
96+
ch_high[i] = math.ceil(2 * bark_axis[audible_index[i]]) - 1
9697

9798
# Creation of the excitation pattern
9899
slopes = np.zeros((n_aud, n_channel))
99100
for k in np.arange(0, n_aud):
100101
levDB = spec_dB[audible_index[k]]
101-
b = barks[audible_index[k]]
102+
b = bark_axis[audible_index[k]]
102103
for j in np.arange(0, int(ch_low[k] + 1)):
103104
sl = (s1 * (b - ((j + 1) * 0.5))) + levDB
104105
if sl > minExcitDB[j]:
@@ -129,24 +130,22 @@ def _roughness_dw_main_calc(spectrum, freqs, fs, gzi, hWeight):
129130
else:
130131
ampl = slopes[j, i - 1] / module[ind]
131132

132-
# reconstruction of the spectrum
133-
exc[ind] = ampl * spectrum[ind]
133+
# reconstruction of the spec
134+
exc[ind] = ampl * spec[ind]
134135

135136
# The temporal specific excitation functions are obtained by IFFT
136137
temporal_excitation = np.abs(n * np.real(ifft(exc)))
137-
138138
# ------------------------------- stage 2 --------------------------------------
139139
# ---------------------modulation depth calculation-----------------------------
140140

141141
# The fluctuations of the envelope are contained in the low frequency part
142-
# of the spectrum of specific excitations in absolute value
142+
# of the spec of specific excitations in absolute value
143143
h0 = np.mean(temporal_excitation)
144144
envelope_spec = fft(temporal_excitation - h0)
145145

146-
# This spectrum is weighted to model the low-frequency bandpass
146+
# This spec is weighted to model the low-frequency bandpass
147147
# characteristic of the roughness on modulation frequency
148148
envelope_spec = envelope_spec * hWeight[i, :]
149-
150149
# The time functions of the bandpass filtered envelopes hBPi(t)
151150
# are calculated via inverse Fourier transform :
152151
hBP[i, :] = 2 * np.real(ifft(envelope_spec))
@@ -160,7 +159,6 @@ def _roughness_dw_main_calc(spectrum, freqs, fs, gzi, hWeight):
160159
mod_depth[i] = 1
161160
else:
162161
mod_depth[i] = 0
163-
164162
# ------------------------------- stage 3 --------------------------------------
165163
# ----------------roughness calculation with cross correlation------------------
166164

@@ -190,3 +188,6 @@ def _roughness_dw_main_calc(spectrum, freqs, fs, gzi, hWeight):
190188
R = 0.25 * sum(R_spec)
191189

192190
return R, R_spec, zi
191+
192+
193+

mosqito/sq_metrics/roughness/roughness_dw/roughness_dw.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,18 @@ def roughness_dw(signal, fs=None, overlap=0.5, is_sdt_output=False):
6767
sig, time = time_segmentation(
6868
signal, fs, nperseg=nperseg, noverlap=noverlap, is_ecma=False
6969
)
70-
nseg = sig.shape[1]
70+
if len(sig.shape) == 1:
71+
nseg = 1
72+
else:
73+
nseg = sig.shape[1]
7174

7275
spec, _ = spectrum(sig, fs, nfft="default", window="blackman", db=False)
7376

7477
# Frequency axis in Hertz
7578
freq_axis = np.arange(1, nperseg // 2 + 1, 1) * (fs / nperseg)
7679

7780
# Initialization of the weighting functions H and g
78-
hWeight = _H_weighting(nperseg // 2, fs)
81+
hWeight = _H_weighting(nperseg, fs)
7982
# Aures modulation depth weighting function
8083
gzi = _gzi_weighting(np.arange(1, 48, 1) / 2)
8184

mosqito/sq_metrics/roughness/roughness_dw/roughness_dw_freq.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def roughness_dw_freq(spectrum, freqs):
6464
freqs = np.tile(freqs, (nseg, 1)).T
6565

6666
# Initialization of the weighting functions H and g
67-
hWeight = _H_weighting(nperseg, fs)
67+
hWeight = _H_weighting(2*nperseg, fs)
6868
# Aures modulation depth weighting function
6969
gzi = _gzi_weighting(np.arange(1, 48, 1) / 2)
7070

tests/sq_metrics/roughness/test_roughness_dw.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def test_roughness_dw():
4444

4545
# Stimulus generation
4646
stimulus, _ = signal_test(
47-
fc=1000, fmod=70, mdepth=1, fs=44100, d=0.2, dB=70)
47+
fc=1000, fmod=70, mdepth=1, fs=44100, d=0.2, dB=60)
4848

4949
# Roughness calculation
5050
roughness, time, _, _ = roughness_dw(stimulus, fs=44100, overlap=0)
@@ -82,7 +82,7 @@ def test_roughness_dw_sdt():
8282

8383
# Stimulus generation
8484
stimulus, time = signal_test(
85-
fc=1000, fmod=70, mdepth=1, fs=44100, d=0.2, dB=70)
85+
fc=1000, fmod=70, mdepth=1, fs=44100, d=0.2, dB=60)
8686
time = DataLinspace(
8787
name="time",
8888
unit="s",
@@ -141,11 +141,9 @@ def test_roughness_dw_freq():
141141

142142
# conversion into frequency domain
143143
n = len(stimulus)
144-
window = np.blackman(n)
145-
window = window / sum(window)
146144

147145
# Creation of the spectrum by FFT using the Blackman window
148-
spec = fft(stimulus * window)[0:n//2] * 1.42
146+
spec = fft(stimulus )[0:n//2] * 1.42
149147
# Highest frequency
150148
nMax = round(n / 2)
151149
# Frequency axis in Hertz
@@ -157,6 +155,7 @@ def test_roughness_dw_freq():
157155
"name": "Roughness",
158156
"values": roughness,
159157
}
158+
print(R)
160159

161160
# Check compliance
162161
tst = check_compliance(R)

0 commit comments

Comments
 (0)