|
@@ -11,6 +11,49 @@ def is_nanV( xV ):
|
11
|
11
|
return True
|
12
|
12
|
|
13
|
13
|
return False
|
|
14
|
+
|
|
15
|
+def find_min_max_peak_index( rmsV, pkIdxL, minDb, maxDbOffs=0.5 ):
|
|
16
|
+
|
|
17
|
+ # select only the peaks from rmsV[] to work with
|
|
18
|
+ yV = rmsV[ pkIdxL ]
|
|
19
|
+
|
|
20
|
+ # get the max volume note
|
|
21
|
+ max_i = np.argmax( yV )
|
|
22
|
+ maxDb = yV[ max_i ]
|
|
23
|
+
|
|
24
|
+ min_i = max_i
|
|
25
|
+
|
|
26
|
+ # starting from the max volume peak go backwards
|
|
27
|
+ for i in range( max_i, 0, -1 ):
|
|
28
|
+
|
|
29
|
+ # if this peak is within maxDbOffs of the loudest then choose this one instead
|
|
30
|
+ if maxDb - yV[i] < maxDbOffs:
|
|
31
|
+ max_i = i
|
|
32
|
+
|
|
33
|
+ # if this peak is less than minDb then the previous note is the min note
|
|
34
|
+ if yV[i] < minDb:
|
|
35
|
+ break
|
|
36
|
+
|
|
37
|
+ min_i = i
|
|
38
|
+
|
|
39
|
+ assert( min_i < max_i )
|
|
40
|
+
|
|
41
|
+ return min_i, max_i
|
|
42
|
+
|
|
43
|
+def find_skip_peaks( rmsV, pkIdxL, min_pk_idx, max_pk_idx ):
|
|
44
|
+ skipPkIdxL = []
|
|
45
|
+ yV = rmsV[pkIdxL]
|
|
46
|
+ refPkDb = yV[min_pk_idx]
|
|
47
|
+
|
|
48
|
+ for i in range( min_pk_idx+1, max_pk_idx+1 ):
|
|
49
|
+ if yV[i] > refPkDb:
|
|
50
|
+ refPkDb = yV[i]
|
|
51
|
+ else:
|
|
52
|
+ skipPkIdxL.append(i)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+ return skipPkIdxL
|
|
56
|
+
|
14
|
57
|
|
15
|
58
|
def calc_harm_bins( srate, binHz, midiPitch, harmN ):
|
16
|
59
|
|
|
@@ -33,9 +76,14 @@ def calc_harm_bins( srate, binHz, midiPitch, harmN ):
|
33
|
76
|
|
34
|
77
|
return fund_l_binL, fund_m_binL, fund_u_binL
|
35
|
78
|
|
|
79
|
+def rms_to_db( xV, rms_srate, refWndMs ):
|
|
80
|
+ dbWndN = int(round(refWndMs * rms_srate / 1000.0))
|
|
81
|
+ dbRef = ref = np.mean(xV[0:dbWndN])
|
|
82
|
+ rmsDbV = 20.0 * np.log10( xV / dbRef )
|
36
|
83
|
|
|
84
|
+ return rmsDbV
|
37
|
85
|
|
38
|
|
-def audio_rms( srate, xV, rmsWndMs, hopMs ):
|
|
86
|
+def audio_rms( srate, xV, rmsWndMs, hopMs, refWndMs ):
|
39
|
87
|
|
40
|
88
|
wndSmpN = int(round( rmsWndMs * srate / 1000.0))
|
41
|
89
|
hopSmpN = int(round( hopMs * srate / 1000.0))
|
|
@@ -60,19 +108,12 @@ def audio_rms( srate, xV, rmsWndMs, hopMs ):
|
60
|
108
|
|
61
|
109
|
i += hopSmpN
|
62
|
110
|
j += 1
|
63
|
|
-
|
64
|
|
- return yV, srate / hopSmpN
|
65
|
|
-
|
66
|
|
-def audio_db_rms( srate, xV, rmsWndMs, hopMs, dbRefWndMs ):
|
67
|
111
|
|
68
|
|
- rmsV, rms_srate = audio_rms( srate, xV, rmsWndMs, hopMs )
|
69
|
|
- dbWndN = int(round(dbRefWndMs * rms_srate / 1000.0))
|
70
|
|
- dbRef = ref = np.mean(rmsV[0:dbWndN])
|
71
|
|
- return 20.0 * np.log10( rmsV / dbRef ), rms_srate
|
|
112
|
+ rms_srate = srate / hopSmpN
|
|
113
|
+ return rms_to_db( yV, rms_srate, refWndMs ), rms_srate
|
72
|
114
|
|
73
|
115
|
|
74
|
|
-
|
75
|
|
-def audio_stft_rms( srate, xV, rmsWndMs, hopMs, spectrumIdx ):
|
|
116
|
+def audio_stft_rms( srate, xV, rmsWndMs, hopMs, refWndMs, spectrumIdx ):
|
76
|
117
|
|
77
|
118
|
wndSmpN = int(round( rmsWndMs * srate / 1000.0))
|
78
|
119
|
hopSmpN = int(round( hopMs * srate / 1000.0))
|
|
@@ -88,18 +129,14 @@ def audio_stft_rms( srate, xV, rmsWndMs, hopMs, spectrumIdx ):
|
88
|
129
|
for i in range(xM.shape[1]):
|
89
|
130
|
mV[i] = np.max(np.sqrt(np.abs(xM[:,i])))
|
90
|
131
|
|
91
|
|
- return mV, srate / hopSmpN, specV, specHopIdx, binHz
|
92
|
132
|
|
93
|
|
-def audio_stft_db_rms( srate, xV, rmsWndMs, hopMs, dbRefWndMs, spectrumIdx ):
|
94
|
|
- rmsV, rms_srate, specV, specHopIdx, binHz = audio_stft_rms( srate, xV, rmsWndMs, hopMs, spectrumIdx )
|
95
|
|
-
|
96
|
|
- dbWndN = int(round(dbRefWndMs * rms_srate / 1000.0))
|
97
|
|
- dbRef = ref = np.mean(rmsV[0:dbWndN])
|
98
|
|
- rmsDbV = 20.0 * np.log10( rmsV / dbRef )
|
|
133
|
+ rms_srate = srate / hopSmpN
|
|
134
|
+ mV = rms_to_db( mV, rms_srate, refWndMs )
|
|
135
|
+
|
|
136
|
+ return mV, rms_srate, specV, specHopIdx, binHz
|
99
|
137
|
|
100
|
|
- return rmsDbV, rms_srate, specV, specHopIdx, binHz
|
101
|
138
|
|
102
|
|
-def audio_harm_rms( srate, xV, rmsWndMs, hopMs, midiPitch, harmCandN, harmN ):
|
|
139
|
+def audio_harm_rms( srate, xV, rmsWndMs, hopMs, dbRefWndMs, midiPitch, harmCandN, harmN ):
|
103
|
140
|
|
104
|
141
|
wndSmpN = int(round( rmsWndMs * srate / 1000.0))
|
105
|
142
|
hopSmpN = int(round( hopMs * srate / 1000.0))
|
|
@@ -125,20 +162,9 @@ def audio_harm_rms( srate, xV, rmsWndMs, hopMs, midiPitch, harmCandN, harmN ):
|
125
|
162
|
|
126
|
163
|
|
127
|
164
|
|
128
|
|
-
|
129
|
|
- return rmsV, srate / hopSmpN, binHz
|
130
|
|
-
|
131
|
|
-
|
132
|
|
-
|
133
|
|
-def audio_harm_db_rms( srate, xV, rmsWndMs, hopMs, dbRefWndMs, midiPitch, harmCandN, harmN ):
|
134
|
|
-
|
135
|
|
- rmsV, rms_srate, binHz = audio_harm_rms( srate, xV, rmsWndMs, hopMs, midiPitch, harmCandN, harmN )
|
136
|
|
-
|
137
|
|
- dbWndN = int(round(dbRefWndMs * rms_srate / 1000.0))
|
138
|
|
- dbRef = ref = np.mean(rmsV[0:dbWndN])
|
139
|
|
- rmsDbV = 20.0 * np.log10( rmsV / dbRef )
|
140
|
|
-
|
141
|
|
- return rmsDbV, rms_srate, binHz
|
|
165
|
+ rms_srate = srate / hopSmpN
|
|
166
|
+ rmsV = rms_to_db( rmsV, rms_srate, dbRefWndMs )
|
|
167
|
+ return rmsV, rms_srate, binHz
|
142
|
168
|
|
143
|
169
|
|
144
|
170
|
|
|
@@ -156,7 +182,8 @@ def locate_peak_indexes( xV, xV_srate, eventMsL ):
|
156
|
182
|
|
157
|
183
|
|
158
|
184
|
def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
|
159
|
|
-
|
|
185
|
+ """ Plot a single spectrum, 'specV' and the harmonic peak location boundaries."""
|
|
186
|
+
|
160
|
187
|
binN = specV.shape[0]
|
161
|
188
|
harmLBinL,harmMBinL,harmUBinL = calc_harm_bins( srate, binHz, midiPitch, harmN )
|
162
|
189
|
|
|
@@ -178,7 +205,8 @@ def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
|
178
|
205
|
ax.set_ylabel(str(midiPitch))
|
179
|
206
|
|
180
|
207
|
def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbRefWndMs=500 ):
|
181
|
|
-
|
|
208
|
+ """ Plot the spectrum from one note (7th from last) in each attack pulse length sequence referred to by pitchL."""
|
|
209
|
+
|
182
|
210
|
plotN = len(pitchL)
|
183
|
211
|
fig,axL = plt.subplots(plotN,1)
|
184
|
212
|
|
|
@@ -197,7 +225,7 @@ def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbR
|
197
|
225
|
sigV = signalM / float(0x7fff)
|
198
|
226
|
|
199
|
227
|
# calc. the RMS envelope in the time domain
|
200
|
|
- rms0DbV, rms0_srate = audio_db_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs )
|
|
228
|
+ rms0DbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs )
|
201
|
229
|
|
202
|
230
|
# locate the sample index of the peak of each note attack
|
203
|
231
|
pkIdx0L = locate_peak_indexes( rms0DbV, rms0_srate, r['eventTimeL'] )
|
|
@@ -211,7 +239,7 @@ def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbR
|
211
|
239
|
|
212
|
240
|
|
213
|
241
|
# calc. the RMS envelope by taking the max spectral peak in each STFT window
|
214
|
|
- rmsDbV, rms_srate, specV, specHopIdx, binHz = audio_stft_db_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs, spectrumSmpIdx)
|
|
242
|
+ rmsDbV, rms_srate, specV, specHopIdx, binHz = audio_stft_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs, spectrumSmpIdx)
|
215
|
243
|
|
216
|
244
|
# specV[] is the spectrum of the note at spectrumSmpIdx
|
217
|
245
|
|
|
@@ -222,12 +250,12 @@ def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbR
|
222
|
250
|
|
223
|
251
|
|
224
|
252
|
def do_td_plot( inDir ):
|
225
|
|
-
|
226
|
253
|
rmsWndMs = 300
|
227
|
254
|
rmsHopMs = 30
|
228
|
255
|
dbRefWndMs = 500
|
229
|
256
|
harmCandN = 5
|
230
|
257
|
harmN = 3
|
|
258
|
+ minAttkDb = 5.0
|
231
|
259
|
|
232
|
260
|
seqFn = os.path.join( inDir, "seq.json")
|
233
|
261
|
audioFn = os.path.join( inDir, "audio.wav")
|
|
@@ -241,12 +269,17 @@ def do_td_plot( inDir ):
|
241
|
269
|
srate, signalM = wavfile.read(audioFn)
|
242
|
270
|
sigV = signalM / float(0x7fff)
|
243
|
271
|
|
244
|
|
- rms0DbV, rms0_srate = audio_db_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs )
|
|
272
|
+ rms0DbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs )
|
245
|
273
|
|
246
|
|
- rmsDbV, rms_srate, binHz = audio_harm_db_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs, midiPitch, harmCandN, harmN )
|
|
274
|
+ rmsDbV, rms_srate, binHz = audio_harm_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs, midiPitch, harmCandN, harmN )
|
247
|
275
|
|
248
|
276
|
pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'] )
|
249
|
|
-
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+ min_pk_idx, max_pk_idx = find_min_max_peak_index( rmsDbV, pkIdxL, minAttkDb )
|
|
280
|
+
|
|
281
|
+ skipPkIdxL = find_skip_peaks( rmsDbV, pkIdxL, min_pk_idx, max_pk_idx )
|
|
282
|
+
|
250
|
283
|
fig,ax = plt.subplots()
|
251
|
284
|
fig.set_size_inches(18.5, 10.5, forward=True)
|
252
|
285
|
|
|
@@ -255,13 +288,17 @@ def do_td_plot( inDir ):
|
255
|
288
|
ax.plot( secV, rmsDbV )
|
256
|
289
|
ax.plot( np.arange(0,len(rms0DbV)) / rms0_srate, rms0DbV, color="black" )
|
257
|
290
|
|
258
|
|
- for begMs, endMs in r['eventTimeL']:
|
|
291
|
+ # print beg/end boundaries
|
|
292
|
+ for i,(begMs, endMs) in enumerate(r['eventTimeL']):
|
259
|
293
|
ax.axvline( x=begMs/1000.0, color="green")
|
260
|
294
|
ax.axvline( x=endMs/1000.0, color="red")
|
|
295
|
+ ax.text(begMs/1000.0, 20.0, str(i) )
|
261
|
296
|
|
262
|
|
-
|
|
297
|
+ # plot peak markers
|
263
|
298
|
for i,pki in enumerate(pkIdxL):
|
264
|
|
- ax.plot( [pki / rms_srate], [ rmsDbV[pki] ], marker='.', color="black")
|
|
299
|
+ marker = "o" if i==min_pk_idx or i==max_pk_idx else "."
|
|
300
|
+ color = "red" if i in skipPkIdxL else "black"
|
|
301
|
+ ax.plot( [pki / rms_srate], [ rmsDbV[pki] ], marker=marker, color=color)
|
265
|
302
|
|
266
|
303
|
plt.show()
|
267
|
304
|
|