|
@@ -1,8 +1,8 @@
|
1
|
|
-import os, sys, json
|
2
|
|
-from scipy.io import wavfile
|
3
|
|
-from scipy.signal import stft
|
|
1
|
+import os, sys
|
4
|
2
|
import matplotlib.pyplot as plt
|
5
|
3
|
import numpy as np
|
|
4
|
+from common import parse_yaml_cfg
|
|
5
|
+from rms_analysis import rms_analysis_main
|
6
|
6
|
|
7
|
7
|
def is_nanV( xV ):
|
8
|
8
|
|
|
@@ -12,173 +12,325 @@ def is_nanV( xV ):
|
12
|
12
|
|
13
|
13
|
return False
|
14
|
14
|
|
15
|
|
-def find_min_max_peak_index( rmsV, pkIdxL, minDb, maxDbOffs=0.5 ):
|
|
15
|
+def _find_max_take_id( inDir ):
|
16
|
16
|
|
17
|
|
- # select only the peaks from rmsV[] to work with
|
18
|
|
- yV = rmsV[ pkIdxL ]
|
|
17
|
+ id = 0
|
|
18
|
+ while os.path.isdir( os.path.join(inDir, "%i" % id) ):
|
|
19
|
+ id += 1
|
19
|
20
|
|
20
|
|
- # get the max volume note
|
21
|
|
- max_i = np.argmax( yV )
|
22
|
|
- maxDb = yV[ max_i ]
|
|
21
|
+ if id > 0:
|
|
22
|
+ id -= 1
|
|
23
|
+
|
|
24
|
+ return id
|
|
25
|
+
|
23
|
26
|
|
24
|
|
- min_i = max_i
|
|
27
|
+def form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None ):
|
25
|
28
|
|
26
|
|
- # starting from the max volume peak go backwards
|
27
|
|
- for i in range( max_i, 0, -1 ):
|
|
29
|
+ # append the midi pitch to the input directory
|
|
30
|
+ #inDir = os.path.join( inDir, "%i" % (midi_pitch))
|
28
|
31
|
|
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
|
+ if False:
|
|
33
|
+ # determine the take id if none was given
|
|
34
|
+ if take_id is None:
|
|
35
|
+ take_id = _find_max_take_id( inDir )
|
32
|
36
|
|
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
|
|
37
|
+ inDir = os.path.join(inDir,"%i" % (take_id))
|
38
|
38
|
|
39
|
|
- assert( min_i < max_i )
|
40
|
|
-
|
41
|
|
- return min_i, max_i
|
|
39
|
+ assert( os.path.isdir(inDir))
|
42
|
40
|
|
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]
|
|
41
|
+ # analyze the requested take audio
|
|
42
|
+ r = rms_analysis_main( inDir, midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
|
47
|
43
|
|
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
|
|
-
|
|
44
|
+ pkL = []
|
|
45
|
+ # store the peaks in pkL[ (db,us) ]
|
|
46
|
+ for db,us in zip(r.pkDbL,r.pkUsL):
|
|
47
|
+ pkL.append( (db,us) )
|
|
48
|
+
|
|
49
|
+ # sort the peaks on increasing attack pulse microseconds
|
|
50
|
+ pkL = sorted( pkL, key= lambda x: x[1] )
|
|
51
|
+
|
|
52
|
+ # split pkL
|
|
53
|
+ pkDbL,pkUsL = tuple(zip(*pkL))
|
|
54
|
+
|
|
55
|
+ dirL = os.listdir(inDir)
|
|
56
|
+
|
|
57
|
+ pkL = []
|
|
58
|
+
|
|
59
|
+ # for each take in this directory
|
|
60
|
+ for idir in dirL:
|
|
61
|
+
|
|
62
|
+ take_number = int(idir)
|
|
63
|
+
|
|
64
|
+ # analyze this takes audio and locate the note peaks
|
|
65
|
+ r = rms_analysis_main( os.path.join(inDir,idir), midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
|
|
66
|
+
|
|
67
|
+ # store the peaks in pkL[ (db,us) ]
|
|
68
|
+ for db,us in zip(r.pkDbL,r.pkUsL):
|
|
69
|
+ pkL.append( (db,us) )
|
54
|
70
|
|
55
|
|
- return skipPkIdxL
|
|
71
|
+ # sort the peaks on increasing attack pulse microseconds
|
|
72
|
+ pkL = sorted( pkL, key= lambda x: x[1] )
|
|
73
|
+
|
|
74
|
+ # merge sample points that separated by less than 'minSampleDistUs' milliseconds
|
|
75
|
+ pkL = merge_close_sample_points( pkL, analysisArgsD['minSampleDistUs'] )
|
|
76
|
+
|
|
77
|
+ # split pkL
|
|
78
|
+ pkDbL,pkUsL = tuple(zip(*pkL))
|
56
|
79
|
|
|
80
|
+ #-------------------------------------------
|
57
|
81
|
|
58
|
|
-def calc_harm_bins( srate, binHz, midiPitch, harmN ):
|
|
82
|
+ # locate the first and last note
|
|
83
|
+ min_pk_idx, max_pk_idx = find_min_max_peak_index( pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
|
|
84
|
+
|
|
85
|
+ db1 = pkDbL[ max_pk_idx ]
|
|
86
|
+ db0 = pkDbL[ min_pk_idx ]
|
|
87
|
+
|
|
88
|
+ pulseUsL = []
|
|
89
|
+ pulseDbL = []
|
|
90
|
+ multValL = []
|
|
91
|
+ for out_idx in range(128):
|
|
92
|
+
|
|
93
|
+ # calc the target volume
|
|
94
|
+ db = db0 + (out_idx * (db1-db0)/127.0)
|
|
95
|
+
|
|
96
|
+ multi_value_count = 0
|
|
97
|
+
|
|
98
|
+ # look for the target between each of the sampled points
|
|
99
|
+ for i in range(1,len(pkDbL)):
|
|
100
|
+
|
|
101
|
+ # if the target volume is between these two sample points
|
|
102
|
+ if pkDbL[i-1] <= db and db < pkDbL[i]:
|
|
103
|
+
|
|
104
|
+ # if the target has not already been located
|
|
105
|
+ if len(pulseUsL) == out_idx:
|
59
|
106
|
|
60
|
|
- semi_tone = 1.0/12
|
61
|
|
- quarter_tone = 1.0/24
|
62
|
|
- eigth_tone = 1.0/48
|
63
|
|
- band_width_st = 3.0/48 # 3/8 tone
|
|
107
|
+ # interpolate the pulse time from between the sampled points
|
|
108
|
+ frac = (db - pkDbL[i-1]) / (pkDbL[i] - pkDbL[i-1])
|
|
109
|
+ us = pkUsL[i-1] + frac * (pkUsL[i] - pkUsL[i-1])
|
|
110
|
+ db = pkDbL[i-1] + frac * (pkDbL[i] - pkDbL[i-1])
|
|
111
|
+ pulseUsL.append(us)
|
|
112
|
+ pulseDbL.append(db)
|
|
113
|
+
|
|
114
|
+ else:
|
|
115
|
+ # this target db value was found between multiple sampled points
|
|
116
|
+ # therefore the sampled volume function is not monotonic
|
|
117
|
+ multi_value_count += 1
|
|
118
|
+
|
|
119
|
+ if multi_value_count > 0:
|
|
120
|
+ multValL.append((out_idx,multi_value_count))
|
|
121
|
+
|
|
122
|
+ if len(multValL) > 0:
|
|
123
|
+ print("Multi-value pulse locations were found during velocity table formation: ",multValL)
|
|
124
|
+
|
|
125
|
+ return pulseUsL,pulseDbL
|
64
|
126
|
|
65
|
|
- fundHz = (13.75 * pow(2.0,(-9.0/12.0))) * pow(2.0,(midiPitch / 12))
|
66
|
|
- fund_l_binL = [int(round(fundHz * pow(2.0,-band_width_st) * i/binHz)) for i in range(1,harmN+1)]
|
67
|
|
- fund_m_binL = [int(round(fundHz * i/binHz)) for i in range(1,harmN+1)]
|
68
|
|
- fund_u_binL = [int(round(fundHz * pow(2.0, band_width_st) * i/binHz)) for i in range(1,harmN+1)]
|
69
|
127
|
|
70
|
|
- for i in range(len(fund_m_binL)):
|
71
|
|
- if fund_l_binL[i] >= fund_m_binL[i] and fund_l_binL[i] > 0:
|
72
|
|
- fund_l_binL[i] = fund_m_binL[i] - 1
|
73
|
128
|
|
74
|
|
- if fund_u_binL[i] <= fund_m_binL[i] and fund_u_binL[i] < len(fund_u_binL)-1:
|
75
|
|
- fund_u_binL[i] = fund_m_binL[i] + 1
|
|
129
|
+def merge_close_sample_points( pkDbUsL, minSampleDistanceUs ):
|
|
130
|
+
|
|
131
|
+ avg0Us = np.mean(np.diff([ x[1] for x in pkDbUsL ]))
|
|
132
|
+ n0 = len(pkDbUsL)
|
|
133
|
+
|
|
134
|
+ while True:
|
|
135
|
+ us0 = None
|
|
136
|
+ db0 = None
|
|
137
|
+
|
|
138
|
+ for i,(db,us) in enumerate(pkDbUsL):
|
|
139
|
+ if i > 0 and us - us0 < minSampleDistanceUs:
|
|
140
|
+ us1 = (us0 + us)/2
|
|
141
|
+ db1 = (db0 + db)/2
|
|
142
|
+ pkDbUsL[i-1] = (db1,us1)
|
|
143
|
+ del pkDbUsL[i]
|
|
144
|
+ break
|
|
145
|
+ else:
|
|
146
|
+ us0 = us
|
|
147
|
+ db0 = db
|
|
148
|
+
|
|
149
|
+ if i+1 == len(pkDbUsL):
|
|
150
|
+ break
|
|
151
|
+
|
|
152
|
+ avg1Us = np.mean(np.diff([ x[1] for x in pkDbUsL ]))
|
76
|
153
|
|
77
|
|
- return fund_l_binL, fund_m_binL, fund_u_binL
|
|
154
|
+ print("%i sample points deleted by merging close points." % (n0 - len(pkDbUsL)))
|
|
155
|
+ print("Mean time between samples - before:%f after:%f " % (avg0Us,avg1Us))
|
|
156
|
+ print("Min time between samples: %i " % (np.min(np.diff([x[1] for x in pkDbUsL]))))
|
78
|
157
|
|
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 )
|
|
158
|
+ return pkDbUsL
|
83
|
159
|
|
84
|
|
- return rmsDbV
|
85
|
160
|
|
86
|
|
-def audio_rms( srate, xV, rmsWndMs, hopMs, refWndMs ):
|
87
|
161
|
|
88
|
|
- wndSmpN = int(round( rmsWndMs * srate / 1000.0))
|
89
|
|
- hopSmpN = int(round( hopMs * srate / 1000.0))
|
|
162
|
+def calc_resample_ranges( pkDbL, pkUsL, min_pk_idx, max_pk_idx, maxDeltaDb, samplePerDb ):
|
90
|
163
|
|
91
|
|
- xN = xV.shape[0]
|
92
|
|
- yN = int(((xN - wndSmpN) / hopSmpN) + 1)
|
93
|
|
- assert( yN > 0)
|
94
|
|
- yV = np.zeros( (yN, ) )
|
|
164
|
+ if min_pk_idx == 0:
|
|
165
|
+ print("No silent notes were generated. Decrease the minimum peak level or the hold voltage.")
|
|
166
|
+ return None
|
95
|
167
|
|
96
|
|
- assert( wndSmpN > 1 )
|
|
168
|
+ resampleUsSet = set()
|
|
169
|
+ refPkDb = pkDbL[min_pk_idx]
|
97
|
170
|
|
98
|
|
- i = 0
|
99
|
|
- j = 0
|
100
|
|
- while i < xN and j < yN:
|
|
171
|
+ #pkDbL = pkDbL[ pkIdxL ]
|
101
|
172
|
|
102
|
|
- if i == 0:
|
103
|
|
- yV[j] = np.sqrt(xV[0]*xV[0])
|
104
|
|
- elif i < wndSmpN:
|
105
|
|
- yV[j] = np.sqrt( np.mean( xV[0:i] * xV[0:i] ) )
|
106
|
|
- else:
|
107
|
|
- yV[j] = np.sqrt( np.mean( xV[i-wndSmpN:i] * xV[i-wndSmpN:i] ) )
|
|
173
|
+ for i in range( min_pk_idx, max_pk_idx+1 ):
|
108
|
174
|
|
109
|
|
- i += hopSmpN
|
110
|
|
- j += 1
|
|
175
|
+ d = pkDbL[i] - pkDbL[i-1]
|
111
|
176
|
|
112
|
|
- rms_srate = srate / hopSmpN
|
113
|
|
- return rms_to_db( yV, rms_srate, refWndMs ), rms_srate
|
|
177
|
+ usL = []
|
114
|
178
|
|
|
179
|
+ # if this peak is less than maxDeltaDb above the previous pk or
|
|
180
|
+ # it is below the previous max peak
|
|
181
|
+ if d > maxDeltaDb or d <= 0 or pkDbL[i] < refPkDb:
|
115
|
182
|
|
116
|
|
-def audio_stft_rms( srate, xV, rmsWndMs, hopMs, refWndMs, spectrumIdx ):
|
117
|
|
-
|
118
|
|
- wndSmpN = int(round( rmsWndMs * srate / 1000.0))
|
119
|
|
- hopSmpN = int(round( hopMs * srate / 1000.0))
|
120
|
|
- binHz = srate / wndSmpN
|
121
|
|
-
|
122
|
|
- f,t,xM = stft( xV, fs=srate, window="hann", nperseg=wndSmpN, noverlap=wndSmpN-hopSmpN, return_onesided=True )
|
|
183
|
+ sampleCnt = max(int(round(abs(d) * samplePerDb)),samplePerDb)
|
|
184
|
+ dUs = int(round((pkUsL[i] - pkUsL[i-1])/sampleCnt))
|
|
185
|
+ usL = [ pkUsL[i-1] + dUs*j for j in range(sampleCnt)]
|
|
186
|
+
|
|
187
|
+ if i + 1 < len(pkDbL):
|
|
188
|
+ d = pkDbL[i+1] - pkDbL[i]
|
|
189
|
+
|
|
190
|
+ sampleCnt = max(int(round(abs(d) * samplePerDb)),samplePerDb)
|
|
191
|
+ dUs = int(round((pkUsL[i+1] - pkUsL[i])/sampleCnt))
|
|
192
|
+ usL += [ pkUsL[i] + dUs*j for j in range(sampleCnt)]
|
|
193
|
+
|
|
194
|
+ if pkDbL[i] > refPkDb:
|
|
195
|
+ refPkDb = pkDbL[i]
|
|
196
|
+
|
|
197
|
+ if usL:
|
|
198
|
+ resampleUsSet = resampleUsSet.union( usL )
|
|
199
|
+
|
|
200
|
+ return resampleUsSet
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+def form_resample_pulse_time_list( inDir, analysisArgsD ):
|
|
205
|
+ """" This function merges all available data from previous takes to form
|
|
206
|
+ a new list of pulse times to sample.
|
|
207
|
+ """
|
|
208
|
+
|
|
209
|
+ # the last folder is always the midi pitch of the note under analysis
|
|
210
|
+ midi_pitch = int( inDir.split("/")[-1] )
|
123
|
211
|
|
124
|
|
- specHopIdx = int(round( spectrumIdx ))
|
125
|
|
- specV = np.sqrt(np.abs(xM[:, specHopIdx ]))
|
|
212
|
+ dirL = os.listdir(inDir)
|
|
213
|
+
|
|
214
|
+ pkL = []
|
|
215
|
+
|
|
216
|
+ # for each take in this directory
|
|
217
|
+ for idir in dirL:
|
|
218
|
+
|
|
219
|
+ take_number = int(idir)
|
|
220
|
+
|
|
221
|
+ # analyze this takes audio and locate the note peaks
|
|
222
|
+ r = rms_analysis_main( os.path.join(inDir,idir), midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
|
|
223
|
+
|
|
224
|
+ # store the peaks in pkL[ (db,us) ]
|
|
225
|
+ for db,us in zip(r.pkDbL,r.pkUsL):
|
|
226
|
+ pkL.append( (db,us) )
|
|
227
|
+
|
|
228
|
+ # sort the peaks on increasing attack pulse microseconds
|
|
229
|
+ pkL = sorted( pkL, key= lambda x: x[1] )
|
|
230
|
+
|
|
231
|
+ # merge sample points that separated by less than 'minSampleDistUs' milliseconds
|
|
232
|
+ pkL = merge_close_sample_points( pkL, analysisArgsD['minSampleDistUs'] )
|
126
|
233
|
|
127
|
|
- mV = np.zeros((xM.shape[1]))
|
|
234
|
+ # split pkL
|
|
235
|
+ pkDbL,pkUsL = tuple(zip(*pkL))
|
128
|
236
|
|
129
|
|
- for i in range(xM.shape[1]):
|
130
|
|
- mV[i] = np.max(np.sqrt(np.abs(xM[:,i])))
|
131
|
237
|
|
|
238
|
+ # locate the first and last note
|
|
239
|
+ min_pk_idx, max_pk_idx = find_min_max_peak_index( pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
|
132
|
240
|
|
133
|
|
- rms_srate = srate / hopSmpN
|
134
|
|
- mV = rms_to_db( mV, rms_srate, refWndMs )
|
135
|
|
-
|
136
|
|
- return mV, rms_srate, specV, specHopIdx, binHz
|
|
241
|
+ # estimate the microsecond locations to resample
|
|
242
|
+ resampleUsSet = calc_resample_ranges( pkDbL, pkUsL, min_pk_idx, max_pk_idx, analysisArgsD['maxDeltaDb'], analysisArgsD['samplesPerDb'] )
|
|
243
|
+
|
|
244
|
+ resampleUsL = sorted( list(resampleUsSet) )
|
137
|
245
|
|
|
246
|
+ return resampleUsL, pkDbL, pkUsL
|
138
|
247
|
|
139
|
|
-def audio_harm_rms( srate, xV, rmsWndMs, hopMs, dbRefWndMs, midiPitch, harmCandN, harmN ):
|
140
|
248
|
|
141
|
|
- wndSmpN = int(round( rmsWndMs * srate / 1000.0))
|
142
|
|
- hopSmpN = int(round( hopMs * srate / 1000.0))
|
143
|
249
|
|
144
|
|
- binHz = srate / wndSmpN
|
|
250
|
+def plot_resample_pulse_times( inDir, analysisArgsD ):
|
|
251
|
+
|
|
252
|
+ newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, analysisArgsD )
|
|
253
|
+
|
|
254
|
+ midi_pitch = int( inDir.split("/")[-1] )
|
|
255
|
+ velTblUsL,velTblDbL = form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None )
|
145
|
256
|
|
146
|
|
- f,t,xM = stft( xV, fs=srate, window="hann", nperseg=wndSmpN, noverlap=wndSmpN-hopSmpN, return_onesided=True )
|
|
257
|
+ fig,ax = plt.subplots()
|
|
258
|
+
|
|
259
|
+ ax.plot(pulseUsL,rmsDbV )
|
|
260
|
+
|
|
261
|
+ for us in newPulseUsL:
|
|
262
|
+ ax.axvline( x = us )
|
147
|
263
|
|
148
|
|
- harmLBinL,harmMBinL,harmUBinL = calc_harm_bins( srate, binHz, midiPitch, harmCandN )
|
|
264
|
+ ax.plot(velTblUsL,velTblDbL,marker='.',linestyle='None')
|
149
|
265
|
|
150
|
|
- rmsV = np.zeros((xM.shape[1],))
|
|
266
|
+ plt.show()
|
|
267
|
+
|
|
268
|
+def find_min_max_peak_index( pkDbL, minDb, maxDbOffs ):
|
|
269
|
+ """
|
|
270
|
+ Find the min db and max db peak.
|
|
271
|
+ """
|
|
272
|
+ # select only the peaks from rmsV[] to work with
|
|
273
|
+ yV = pkDbL
|
151
|
274
|
|
|
275
|
+ # get the max volume note
|
|
276
|
+ max_i = np.argmax( yV )
|
|
277
|
+ maxDb = yV[ max_i ]
|
|
278
|
+
|
|
279
|
+ min_i = max_i
|
152
|
280
|
|
153
|
|
- for i in range(xM.shape[1]):
|
154
|
|
- mV = np.sqrt(np.abs(xM[:,i]))
|
|
281
|
+ # starting from the max volume peak go backwards
|
|
282
|
+ for i in range( max_i, 0, -1 ):
|
|
283
|
+
|
|
284
|
+ # if this peak is within maxDbOffs of the loudest then choose this one instead
|
|
285
|
+ if maxDb - yV[i] < maxDbOffs:
|
|
286
|
+ max_i = i
|
155
|
287
|
|
156
|
|
- pV = np.zeros((len(harmLBinL,)))
|
|
288
|
+ # if this peak is less than minDb then the previous note is the min note
|
|
289
|
+ if yV[i] < minDb:
|
|
290
|
+ break
|
157
|
291
|
|
158
|
|
- for j,(b0i,b1i) in enumerate(zip( harmLBinL, harmUBinL )):
|
159
|
|
- pV[j] = np.max(mV[b0i:b1i])
|
|
292
|
+ min_i = i
|
|
293
|
+
|
|
294
|
+ assert( min_i < max_i )
|
160
|
295
|
|
161
|
|
- rmsV[i] = np.mean( sorted(pV)[-harmN:] )
|
|
296
|
+ if min_i == 0:
|
|
297
|
+ print("No silent notes were generated. Decrease the minimum peak level or the hold voltage.")
|
|
298
|
+
|
|
299
|
+ return min_i, max_i
|
|
300
|
+
|
|
301
|
+def find_skip_peaks( rmsV, pkIdxL, min_pk_idx, max_pk_idx ):
|
|
302
|
+ """ Fine peaks associated with longer attacks pulses that are lower than peaks with a shorter attack pulse.
|
|
303
|
+ These peaks indicate degenerate portions of the pulse/db curve which must be skipped during velocity table formation
|
|
304
|
+ """
|
|
305
|
+ skipPkIdxL = []
|
|
306
|
+ yV = rmsV[pkIdxL]
|
|
307
|
+ refPkDb = yV[min_pk_idx]
|
|
308
|
+
|
|
309
|
+ for i in range( min_pk_idx+1, max_pk_idx+1 ):
|
|
310
|
+ if yV[i] > refPkDb:
|
|
311
|
+ refPkDb = yV[i]
|
|
312
|
+ else:
|
|
313
|
+ skipPkIdxL.append(i)
|
162
|
314
|
|
163
|
315
|
|
164
|
|
-
|
165
|
|
- rms_srate = srate / hopSmpN
|
166
|
|
- rmsV = rms_to_db( rmsV, rms_srate, dbRefWndMs )
|
167
|
|
- return rmsV, rms_srate, binHz
|
168
|
|
-
|
169
|
|
-
|
170
|
|
-
|
171
|
|
-def locate_peak_indexes( xV, xV_srate, eventMsL ):
|
|
316
|
+ return skipPkIdxL
|
|
317
|
+
|
|
318
|
+def find_out_of_range_peaks( rmsV, pkIdxL, min_pk_idx, max_pk_idx, maxDeltaDb ):
|
|
319
|
+ """ Locate peaks which are more than maxDeltaDb from the previous peak.
|
|
320
|
+ If two peaks are separated by more than maxDeltaDb then the range must be resampled
|
|
321
|
+ """
|
172
|
322
|
|
173
|
|
- pkIdxL = []
|
174
|
|
- for begMs, endMs in eventMsL:
|
|
323
|
+ oorPkIdxL = []
|
|
324
|
+ yV = rmsV[pkIdxL]
|
175
|
325
|
|
176
|
|
- begSmpIdx = int(begMs * xV_srate / 1000.0)
|
177
|
|
- endSmpIdx = int(endMs * xV_srate / 1000.0)
|
|
326
|
+ for i in range( min_pk_idx, max_pk_idx+1 ):
|
|
327
|
+ if i > 0:
|
|
328
|
+ d = yV[i] - yV[i-1]
|
|
329
|
+ if d > maxDeltaDb or d < 0:
|
|
330
|
+ oorPkIdxL.append(i)
|
178
|
331
|
|
179
|
|
- pkIdxL.append( begSmpIdx + np.argmax( xV[begSmpIdx:endSmpIdx] ) )
|
|
332
|
+ return oorPkIdxL
|
180
|
333
|
|
181
|
|
- return pkIdxL
|
182
|
334
|
|
183
|
335
|
|
184
|
336
|
def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
|
|
@@ -249,68 +401,82 @@ def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbR
|
249
|
401
|
|
250
|
402
|
|
251
|
403
|
|
252
|
|
-def do_td_plot( inDir ):
|
253
|
|
- rmsWndMs = 300
|
254
|
|
- rmsHopMs = 30
|
255
|
|
- dbRefWndMs = 500
|
256
|
|
- harmCandN = 5
|
257
|
|
- harmN = 3
|
258
|
|
- minAttkDb = 5.0
|
259
|
|
-
|
260
|
|
- seqFn = os.path.join( inDir, "seq.json")
|
261
|
|
- audioFn = os.path.join( inDir, "audio.wav")
|
262
|
|
- midiPitch = int(inDir.split("/")[-1])
|
263
|
|
-
|
|
404
|
+def td_plot( ax, inDir, midi_pitch, id, analysisArgsD ):
|
264
|
405
|
|
265
|
|
- with open( seqFn, "rb") as f:
|
266
|
|
- r = json.load(f)
|
|
406
|
+ r = rms_analysis_main( inDir, midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
|
267
|
407
|
|
268
|
|
-
|
269
|
|
- srate, signalM = wavfile.read(audioFn)
|
270
|
|
- sigV = signalM / float(0x7fff)
|
271
|
|
-
|
272
|
|
- rms0DbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs )
|
273
|
|
-
|
274
|
|
- rmsDbV, rms_srate, binHz = audio_harm_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs, midiPitch, harmCandN, harmN )
|
275
|
|
-
|
276
|
|
- pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'] )
|
|
408
|
+ min_pk_idx, max_pk_idx = find_min_max_peak_index( r.pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
|
277
|
409
|
|
|
410
|
+ skipPkIdxL = find_skip_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx )
|
278
|
411
|
|
279
|
|
- min_pk_idx, max_pk_idx = find_min_max_peak_index( rmsDbV, pkIdxL, minAttkDb )
|
|
412
|
+ jmpPkIdxL = find_out_of_range_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx, analysisArgsD['maxDeltaDb'] )
|
280
|
413
|
|
281
|
|
- skipPkIdxL = find_skip_peaks( rmsDbV, pkIdxL, min_pk_idx, max_pk_idx )
|
|
414
|
+ secV = np.arange(0,len(r.rmsDbV)) / r.rms_srate
|
282
|
415
|
|
283
|
|
- fig,ax = plt.subplots()
|
284
|
|
- fig.set_size_inches(18.5, 10.5, forward=True)
|
|
416
|
+ ax.plot( secV, r.rmsDbV )
|
|
417
|
+ ax.plot( np.arange(0,len(r.tdRmsDbV)) / r.rms_srate, r.tdRmsDbV, color="black" )
|
285
|
418
|
|
286
|
|
- secV = np.arange(0,len(rmsDbV)) / rms_srate
|
287
|
419
|
|
288
|
|
- ax.plot( secV, rmsDbV )
|
289
|
|
- ax.plot( np.arange(0,len(rms0DbV)) / rms0_srate, rms0DbV, color="black" )
|
290
|
|
-
|
291
|
420
|
# print beg/end boundaries
|
292
|
|
- for i,(begMs, endMs) in enumerate(r['eventTimeL']):
|
|
421
|
+ for i,(begMs, endMs) in enumerate(r.eventTimeL):
|
293
|
422
|
ax.axvline( x=begMs/1000.0, color="green")
|
294
|
423
|
ax.axvline( x=endMs/1000.0, color="red")
|
295
|
424
|
ax.text(begMs/1000.0, 20.0, str(i) )
|
296
|
425
|
|
|
426
|
+ return
|
297
|
427
|
# plot peak markers
|
298
|
|
- for i,pki in enumerate(pkIdxL):
|
299
|
|
- marker = "o" if i==min_pk_idx or i==max_pk_idx else "."
|
|
428
|
+ for i,pki in enumerate(r.pkIdxL):
|
|
429
|
+ marker = 4 if i==min_pk_idx or i==max_pk_idx else 5
|
300
|
430
|
color = "red" if i in skipPkIdxL else "black"
|
301
|
|
- ax.plot( [pki / rms_srate], [ rmsDbV[pki] ], marker=marker, color=color)
|
|
431
|
+ ax.plot( [pki / r.rms_srate], [ r.rmsDbV[pki] ], marker=marker, color=color)
|
|
432
|
+
|
|
433
|
+ if i in jmpPkIdxL:
|
|
434
|
+ ax.plot( [pki / r.rms_srate], [ r.rmsDbV[pki] ], marker=6, color="blue")
|
302
|
435
|
|
303
|
|
- plt.show()
|
304
|
436
|
|
305
|
437
|
|
306
|
438
|
|
|
439
|
+def do_td_plot( inDir, analysisArgs ):
|
|
440
|
+
|
|
441
|
+ fig,ax = plt.subplots()
|
|
442
|
+ fig.set_size_inches(18.5, 10.5, forward=True)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+ id = int(inDir.split("/")[-1])
|
|
446
|
+ midi_pitch = int(inDir.split("/")[-2])
|
|
447
|
+
|
|
448
|
+ td_plot(ax,inDir,midi_pitch,id,analysisArgs)
|
|
449
|
+
|
|
450
|
+ plt.show()
|
|
451
|
+
|
|
452
|
+def do_td_multi_plot( inDir, analysisArgs ):
|
|
453
|
+
|
|
454
|
+ midi_pitch = int(inDir.split("/")[-1])
|
|
455
|
+
|
|
456
|
+ dirL = os.listdir(inDir)
|
307
|
457
|
|
|
458
|
+ fig,axL = plt.subplots(len(dirL),1)
|
308
|
459
|
|
|
460
|
+
|
|
461
|
+ for id,(idir,ax) in enumerate(zip(dirL,axL)):
|
309
|
462
|
|
|
463
|
+ td_plot(ax, os.path.join(inDir,str(id)), midi_pitch, id, analysisArgs )
|
|
464
|
+
|
|
465
|
+ plt.show()
|
|
466
|
+
|
|
467
|
+
|
310
|
468
|
if __name__ == "__main__":
|
311
|
469
|
|
312
|
470
|
inDir = sys.argv[1]
|
|
471
|
+ cfgFn = sys.argv[2]
|
313
|
472
|
|
314
|
|
- do_td_plot(inDir)
|
|
473
|
+ cfg = parse_yaml_cfg( cfgFn )
|
|
474
|
+
|
|
475
|
+ #do_td_plot(inDir,cfg.analysisArgs)
|
|
476
|
+
|
|
477
|
+ #o_td_multi_plot(inDir,cfg.analysisArgs)
|
315
|
478
|
|
316
|
479
|
#plot_spectral_ranges( inDir, [ 24, 36, 48, 60, 72, 84, 96, 104] )
|
|
480
|
+
|
|
481
|
+ plot_resample_pulse_times( inDir, cfg.analysisArgs )
|
|
482
|
+
|