Browse Source

README.md,doc/*.png : Updates to verify plotting and add example plots to README.md

master
kevin 3 years ago
parent
commit
ee3c41fe70
14 changed files with 487 additions and 6219 deletions
  1. 43
    4
      README.md
  2. BIN
      do_td_plot.png
  3. BIN
      doc/do_td_plot.png
  4. BIN
      doc/manual_db.png
  5. BIN
      doc/min_max_db_2.png
  6. BIN
      doc/multi_plot.png
  7. 0
    6060
      doc/multi_plot.svg
  8. BIN
      doc/plot_seq_0.png
  9. BIN
      doc/plot_spectral_ranges.png
  10. BIN
      doc/us_db_map.png
  11. 142
    16
      p_ac.yml
  12. 137
    63
      plot_seq.py
  13. 103
    59
      plot_seq_1.py
  14. 62
    17
      rms_analysis.py

+ 43
- 4
README.md View File

@@ -34,12 +34,51 @@ python calibrate_plot.py ~/temp/p_ac_3_oa/60/2 p_ac.yml 60
34 34
 
35 35
 
36 36
 
37
-![Plot Seq 1](doc/plot_seq_0.png)
37
+![Plot Seq 1](doc/do_td_plot.png)
38
+`python plot_seq.py ~/temp/p_ac_3_od p_ac.yml 60 10`
39
+`do_td_plot(inDir,cfg.analysisArgs, pitch, take_id )`
38 40
 
39
-`python plot_seq.py ~/temp/p_ac_3_od/60 p_ac.yml 12`
41
+![Multi Plot 1](doc/multi_plot.png)
42
+`python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_multi_plot 60 3 60 4 60 5`
43
+plot_seq.py `do_td_multi_plot(inDir,cfg.analysisArgs,[(36,4), (48,2)] )`
40 44
 
45
+![Spectral Ranges](doc/plot_spectral_ranges.png)
46
+`python plot_seq.py p_ac.yml ~/temp/p_ac_3_od plot_spectral_ranges 60 3 60 4`
41 47
 
42
-![Multi Plot 1](doc/multi_plot.png)
43 48
 
44
-plot_seq.py `do_td_multi_plot(inDir,cfg.analysisArgs,[(36,4), (48,2)] )`
49
+![Multi Usec dB](doc/us_db.png)
50
+`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od us_db 84`
51
+
52
+![Usec dB Spread](doc/us_db_map.png)
53
+`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od us_db_map 84 72`
54
+
55
+![Min Max](doc/min_max_db.png)
56
+`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od min_max 36 48 60 72 84`
57
+
58
+
59
+![Min Max 2](doc/min_max_db_2.png)
60
+The last number in the list is the 'takeId'
61
+`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od min_max_2 36 48 60 72 84 2`
62
+
63
+`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od manual_db`
64
+
65
+Interpolate across the min and max db values to form the min/max curves for the complete
66
+set of keys.  The anchor points for the curves are taken from cfg record
67
+manuMinD,manualAnchorPitchMinDbL, and manualMaxDbL.
68
+
69
+````
70
+      # select the event (takeId, eventIdx) to use to represent the min value for each pitch
71
+      manualMinD: {
72
+        36: [2, 10],
73
+        48: [2, 10],
74
+        60: [2, 10],
75
+        72: [2, 10],
76
+        84: [2, 10]
77
+        },
78
+
79
+      # leave 60 out of the min anchor point list
80
+      manualAnchorPitchMinDbL: [ 36,48,72,84 ],
81
+      
82
+      manualAnchorPitchMaxDbL: [ 36,48,60,72,84 ],
45 83
 
84
+````

BIN
do_td_plot.png View File


BIN
doc/do_td_plot.png View File


BIN
doc/manual_db.png View File


BIN
doc/min_max_db_2.png View File


BIN
doc/multi_plot.png View File


+ 0
- 6060
doc/multi_plot.svg
File diff suppressed because it is too large
View File


BIN
doc/plot_seq_0.png View File


BIN
doc/plot_spectral_ranges.png View File


BIN
doc/us_db_map.png View File


+ 142
- 16
p_ac.yml View File

@@ -3,8 +3,8 @@
3 3
 
4 4
 
5 5
     # Audio device setup
6
-    audio_off: {
7
-      inPortLabel: "5 USB Audio CODEC:", #"HDA Intel PCH: CS4208", # "5 USB Audio CODEC:", #"5 USB Sound Device",
6
+    audio: {
7
+      inPortLabel: "8 USB Audio CODEC:", #"HDA Intel PCH: CS4208", # "5 USB Audio CODEC:", #"5 USB Sound Device",
8 8
       outPortLabel: ,
9 9
     },
10 10
 
@@ -12,10 +12,10 @@
12 12
         inMonitorFl: False,
13 13
         outMonitorFl: False,
14 14
         throughFl: False,
15
-        #inPortLabel: "Fastlane:Fastlane MIDI A",
16
-        #outPortLabel: "Fastlane:Fastlane MIDI A"
17
-        inPortLabel: "picadae:picadae MIDI 1",
18
-        outPortLabel: "picadae:picadae MIDI 1"
15
+        inPortLabel: "Fastlane:Fastlane MIDI A",
16
+        outPortLabel: "Fastlane:Fastlane MIDI A"
17
+        #inPortLabel: "picadae:picadae MIDI 1",
18
+        #outPortLabel: "picadae:picadae MIDI 1"
19 19
     },
20 20
     
21 21
     # Picadae API args
@@ -23,18 +23,19 @@
23 23
     serial_baud: 38400,
24 24
     i2c_base_addr: 21,
25 25
     prescaler_usec: 16,
26
+    pwm_div: 5,
26 27
     serial_sync_timeout_ms: 10000,
27 28
 
28 29
 
29 30
     # MeasureSeq args
30
-    outDir: "~/temp/p_ac_3g",
31
+    outDir: "~/temp/p_ac_3_oe",
31 32
     noteDurMs: 500,
32 33
     pauseDurMs: 500,
33 34
     reversePulseListFl: True,
34 35
     useFullPulseListFl: True,
35 36
     maxSilentNoteCount: 4,
36 37
     silentNoteMaxPulseUs: 15000,
37
-    silentNoteMinDurMs: 250,
38
+    silentNoteMinDurMs: 180, #250,
38 39
 
39 40
     # Midi file player
40 41
     midiFileFn: "/home/kevin/media/audio/midi/txt/round4.txt",
@@ -57,15 +58,44 @@
57 58
 
58 59
     full_pulse8L: [  10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000, 15250, 15375, 15500, 15750, 16000, 16250, 16500, 16750, 17000, 17250, 17500, 17750, 18000, 18250, 18500, 18750, 19000, 19500, 20000, 20500, 21000, 21500, 22000, 22500, 23000, 23500, 24000, 24500, 25000, 25500, 26000, 26500, 27000, 27500, 28000, 28500, 29000, 30000, 31000, 32000, 33000, 34000, 35000, 36000, 37000, 38000, 39000, 40000 ],    
59 60
     
61
+    # full_pulse9L was the last pulse list used on Matt's piano
62
+    full_pulse9L: [11000, 11075, 11150, 11225, 11300, 11375, 11450, 11525, 11600,11675, 11750, 11825, 11900, 11975, 12050, 12125, 12200, 12275,12350, 12425, 12500, 12575, 12650, 12725, 12800, 12875, 12950, 13025, 13100, 13175, 13250, 13325, 13400, 13475, 13550, 13625, 13700, 13775, 13850, 13925, 14000, 14075, 14150, 14225, 14300, 14375, 14450, 14525, 14600, 14675, 14750, 14825, 14900, 14975],
60 63
 
61
-    full_pulseL: [11000, 11075, 11150, 11225, 11300, 11375, 11450, 11525, 11600,11675, 11750, 11825, 11900, 11975, 12050, 12125, 12200, 12275,12350, 12425, 12500, 12575, 12650, 12725, 12800, 12875, 12950, 13025, 13100, 13175, 13250, 13325, 13400, 13475, 13550, 13625, 13700, 13775, 13850, 13925, 14000, 14075, 14150, 14225, 14300, 14375, 14450, 14525, 14600, 14675, 14750, 14825, 14900, 14975],
62
-    
63 64
     full_pulse10L: [  8750, 8800, 8850, 8900, 8950, 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450,9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000, 15250, 15375, 15500, 15750, 16000, 16250, 16500, 16750, 17000, 17250, 17500, 17750, 18000, 18250, 18500, 18750, 19000, 19500, 20000, 20500, 21000, 21500, 22000, 22500, 23000, 23500, 24000, 24500, 25000, 25500, 26000, 26500, 27000, 27500, 28000, 28500, 29000, 30000, 31000, 32000, 33000, 34000, 35000, 36000, 37000, 38000, 39000, 40000 ],    
64 65
 
65 66
     full_pulse11L: [   9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450,9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000 ],
66 67
     
67 68
     full_pulse12L: [  8750, 8800, 8850, 8900, 8950, 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450,9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000 ],
69
+
70
+    # pulse lists below this line were developed on Steinway D (11/14/20)
71
+    # 60
72
+    full_pulse13L: [8000, 8250, 8500, 8750, 9000, 9250, 9500, 9750, 10000, 10250, 10500, 10750, 11000, 11250, 11500, 11750, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500, 16000, 16500, 17000, 17500, 18000, 18500, 19000, 19500, 20000 ],
73
+
74
+    full_pulse14L: [ 9000, 9125, 9250, 9375, 9500, 9625, 9750, 9875, 10000, 10125, 10250, 10375, 10500, 10625, 10750, 10875, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500, 16000, 16500, 17000, 17500, 18000, 18500, 19000, 19500, 20000, 20500, 21000, 21500, 22000 ],
68 75
     
76
+    full_pulse15L: [ 8000, 9000,10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000 ],
77
+
78
+    full_pulse16L: [ 8000, 8125, 8250, 8375, 8500, 8625, 8750, 8875, 9000, 9125, 9250, 9375, 9500, 9625, 9750, 9875, 10000, 10125, 10250, 10375, 10500, 10625, 10750, 10875, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500, 16000, 16500, 17000, 17500, 18000, 18500, 19000, 19500, 20000, 20500, 21000, 21500, 22000 ],
79
+
80
+    full_pulse17L: [ 8100, 8150, 8200, 8250, 8300, 8350, 8400, 8450, 8500, 8550, 8600, 8650, 8700, 8750, 8800, 8850, 8900, 8950, 9000, 9125, 9250, 9375, 9500, 9625, 9750, 9875, 10000, 10125, 10250, 10375, 10500, 10625, 10750, 10875, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500, 16000, 16500, 17000, 17500, 18000, 18500, 19000, 19500, 20000, 20500, 21000, 21500, 22000 ],
81
+
82
+    full_pulse18L: [  11500, 11625, 11750, 11875, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500 ],
83
+
84
+    full_pulse19L: [  11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13250, 13500, 13750, 14000, 14500, 15000, 15500, 16000, 16500 ],
85
+
86
+    # pulse lists below this line were developed on Steinway D w/ new firmware (11/21/20)
87
+
88
+    full_pulse20L: [  1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, 3250, 3500, 3750, 4000, 4250, 4500, 4750, 5000, 5250, 5500, 5750, 6000, 6250, 6500, 6750, 7000, 7250, 7500, 7750, 8000, 8250, 8500, 8750, 9000, 9250, 9500, 9750, 10000, 10250, 10500, 10750, 11000, 11250, 11500, 11750, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500 ],
89
+
90
+    # 60,72
91
+    full_pulse21L: [  1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, 3250, 3500, 3750, 4000, 4500, 5000, 5500, 6000, 6500, 7000, 7500, 8000, 8500, 9000, 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000 ],
92
+
93
+    # 48,36,85
94
+    full_pulse22L: [  3500, 3750, 4000, 4250, 4500, 4750, 5000, 5250, 5500, 5750, 6000, 6250, 6500, 6750, 7000, 7500, 8000, 8500, 9000, 9500, 10000, 10500, 11000, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000 ],
95
+
96
+    # 84
97
+    full_pulseL: [  2000, 2250, 2500,2750, 3000, 3250, 3500, 3750, 4000, 4250, 4500, 4750, 5000, 5250, 5500, 5750, 6000, 6250, 6500, 6750, 7000, 7500, 8000, 8500, 9000, 9500, 10000, 10500, 11000, 12000, 12500, 13000, 14000, 15000, 16000  ],
98
+
69 99
     # RMS analysis args
70 100
     analysisArgs: {
71 101
       rmsAnalysisArgs: {
@@ -77,11 +107,12 @@
77 107
         durDecayPct: 40,  # percent drop in RMS to indicate the end of a note
78 108
       },
79 109
 
80
-      resampleMinDb: 7.0,            # note's less than this will be skipped
110
+      resampleMinDb: -5.0,           # note's less than this will be skipped
81 111
       resampleNoiseLimitPct: 5.0,    # 
82
-      resampleMinDurMs: 800,         # notes's whose duration is less than this will be skipped
83
-      
84
-      minAttkDb: 7.0,   # threshold of silence level 
112
+      resampleMinDurMs: 150,         # notes's whose duration is less than this will be skipped
113
+
114
+      useLastTakeOnlyFl: True,
115
+      minAttkDb: -5.0,   # threshold of silence level 
85 116
       maxDbOffset: 0.25, # travel down the from the max. note level by at most this amount to locate the max. peak
86 117
       maxDeltaDb: 1.5,  # maximum db change between volume samples (changes greater than this will trigger resampling)
87 118
       samplesPerDb: 4,   # count of samples per dB to resample ranges whose range is less than maxDeltaDb
@@ -93,6 +124,18 @@
93 124
       },
94 125
 
95 126
       manualMinD: {
127
+        36: [2, 10],
128
+        48: [2, 10],
129
+        60: [2, 10],
130
+        72: [2, 10],
131
+        84: [2, 10]
132
+        },
133
+
134
+      manualAnchorPitchMinDbL: [ 36,48,72,84 ],
135
+      manualAnchorPitchMaxDbL: [ 36,48,60,72,84 ],
136
+        
137
+        
138
+      manualMinD_0: {
96 139
         23: [2, 24],
97 140
         24: [2, 18],
98 141
         25: [2, 41],
@@ -175,8 +218,8 @@
175 218
         
176 219
         },
177 220
 
178
-      manualAnchorPitchMinDbL: [ 23, 27, 31, 34, 44, 51, 61, 70, 74, 81, 87, 93, 96, 101 ],
179
-      manualAnchorPitchMaxDbL: [ 23, 32, 49, 57, 67, 76, 83, 93, 99, 101 ],
221
+      manualAnchorPitchMinDbL_0: [ 23, 27, 31, 34, 44, 51, 61, 70, 74, 81, 87, 93, 96, 101 ],
222
+      manualAnchorPitchMaxDbL_0: [ 23, 32, 49, 57, 67, 76, 83, 93, 99, 101 ],
180 223
 
181 224
       calibrateArgs: {
182 225
 
@@ -213,6 +256,89 @@
213 256
           dbSrcLabel: 'hm',              # source of the db measurement 'td' (time-domain) or 'hm' (harmonic)
214 257
 
215 258
           holdDutyPctD:  {
259
+          23: [[0, 40]],
260
+          24: [[0, 40]],
261
+          25: [[0, 40]],
262
+          26: [[0, 40]],
263
+          27: [[0, 40]],
264
+          28: [[0, 40]],
265
+          29: [[0, 40]],
266
+          30: [[0, 40]],
267
+          31: [[0, 40]],
268
+          32: [[0, 40]],
269
+          33: [[0, 40]],
270
+          34: [[0, 40]],
271
+          35: [[0, 40]],
272
+          36: [[0, 45]],
273
+          37: [[0, 40]],
274
+          38: [[0, 40]],
275
+          39: [[0, 40]],
276
+          40: [[0, 40]],
277
+          41: [[0, 40]],
278
+          42: [[0, 40]],
279
+          43: [[0, 40]],
280
+          44: [[0, 40]],
281
+          45: [[0, 40]],
282
+          46: [[0, 40]],
283
+          47: [[0, 40]],
284
+          48: [[0, 40]],
285
+          49: [[0, 40]],
286
+          50: [[0, 40]],
287
+          51: [[0, 40]],
288
+          52: [[0, 40]],
289
+          53: [[0, 40]],
290
+          54: [[0, 40]],
291
+          55: [[0, 40]],
292
+          56: [[0, 40]],
293
+          57: [[0, 40]],
294
+          58: [[0, 40]],
295
+          59: [[0, 40]],
296
+          60: [[0, 50]],
297
+          61: [[0, 43]],
298
+          62: [[0, 43]],
299
+          63: [[0, 43]],
300
+          64: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
301
+          65: [[0, 99]],              
302
+          66: [[0, 40]],
303
+          67: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
304
+          68: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
305
+          69: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
306
+          70: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
307
+          71: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
308
+          72: [[0, 45],[11000, 65] ],
309
+          73: [[0, 45],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
310
+          74: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
311
+          75: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
312
+          76: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
313
+          77: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
314
+          78: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
315
+          79: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
316
+          80: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
317
+          81: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
318
+          82: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
319
+          83: [[0, 42]],
320
+          84: [[0, 42],[10000,50],[11000,60]], 
321
+          85: [[0, 42],[9000,45],[10000,50],[11000,60]],
322
+          86: [[0, 40]],
323
+          87: [[0, 40]],
324
+          88: [[0, 40]],
325
+          89: [[0, 40]],
326
+          91: [[0, 40]],
327
+          92: [[0, 40]],
328
+          93: [[0, 40]],
329
+          94: [[0, 40]],
330
+          95: [[0, 40]],
331
+          96: [[0, 40]],
332
+          97: [[0, 40]],
333
+          98: [[0, 40]],
334
+          99: [[0, 40]],
335
+          100: [[0, 40]],
336
+          101: [[0, 40]],
337
+          106: [[0, 40]]
338
+          },
339
+
340
+          # Final for Matt's piano
341
+          holdDutyPct0D:  {
216 342
           23: [[0, 70]],
217 343
           24: [[0, 75]],
218 344
           25: [[0, 70]],

+ 137
- 63
plot_seq.py View File

@@ -1,14 +1,22 @@
1 1
 ##| Copyright: (C) 2019-2020 Kevin Larke <contact AT larke DOT org> 
2 2
 ##| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
3
-import os, sys
3
+import os, sys,json
4 4
 import matplotlib.pyplot as plt
5 5
 import numpy as np
6
+from scipy.io import wavfile
6 7
 from common import parse_yaml_cfg
7 8
 from rms_analysis import rms_analysis_main
8 9
 from rms_analysis import select_first_stable_note_by_delta_db
9 10
 from rms_analysis import select_first_stable_note_by_dur
10 11
 from rms_analysis import samples_to_linear_residual
11 12
 
13
+import rms_analysis as ra
14
+
15
+#from rms_analysis import audio_rms
16
+#from rms_analysis import locate_peak_indexes
17
+#from rms_analysis import audio_stft_rms
18
+#from rms_analysis import calc_harm_bins
19
+
12 20
 def is_nanV( xV ):
13 21
     
14 22
     for i in range(xV.shape[0]):
@@ -31,13 +39,16 @@ def _find_max_take_id( inDir ):
31 39
 
32 40
 def form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None ):
33 41
 
42
+    
34 43
     # append the midi pitch to the input directory
35
-    #inDir = os.path.join( inDir, "%i" % (midi_pitch))
44
+    inDir = os.path.join( inDir, str(midi_pitch))
36 45
 
37 46
     dirL =  os.listdir(inDir)
38 47
 
39 48
     pkL = []
40 49
 
50
+    maxTakeNumber = 0
51
+
41 52
     # for each take in this directory
42 53
     for idir in dirL:
43 54
 
@@ -46,10 +57,18 @@ def form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None ):
46 57
 
47 58
         if not os.path.isfile(os.path.join( inDir,idir, "seq.json")):
48 59
             continue
60
+        
61
+        if analysisArgsD['useLastTakeOnlyFl']:
62
+            if take_number > maxTakeNumber:
63
+                pkL.clear()
64
+                maxTakeNumber = take_number
65
+            else:
66
+                continue
67
+
49 68
 
50 69
         # analyze this takes audio and locate the note peaks
51 70
         r = rms_analysis_main( os.path.join(inDir,idir), midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
52
-
71
+            
53 72
         # store the peaks in pkL[ (db,us) ]
54 73
         for db,us in zip(r.pkDbL,r.pkUsL):
55 74
             pkL.append( (db,us) )
@@ -68,6 +87,8 @@ def form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None ):
68 87
     # locate the first and last note 
69 88
     min_pk_idx, max_pk_idx = find_min_max_peak_index( pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
70 89
 
90
+    print("MIN MAX:",min_pk_idx,pkUsL[min_pk_idx],max_pk_idx,pkUsL[max_pk_idx])
91
+
71 92
     db1 = pkDbL[ max_pk_idx ]
72 93
     db0 = pkDbL[ min_pk_idx ]
73 94
 
@@ -195,14 +216,12 @@ def calc_resample_ranges( pkDbL, pkUsL, min_pk_idx, max_pk_idx, maxDeltaDb, samp
195 216
             
196 217
 
197 218
 
198
-def form_resample_pulse_time_list( inDir, analysisArgsD ):
219
+def form_resample_pulse_time_list( inDir, midi_pitch, analysisArgsD ):
199 220
     """" This function merges all available data from previous takes to form
200 221
     a new list of pulse times to sample.
201 222
     """
202 223
 
203
-    # the last folder is always the midi pitch of the note under analysis
204
-    midi_pitch = int( inDir.split("/")[-1] )
205
-
224
+    inDir = os.path.join( inDir, str(midi_pitch) )
206 225
     dirL =  os.listdir(inDir)
207 226
 
208 227
     pkL = []
@@ -248,11 +267,10 @@ def plot_curve( ax, pulseUsL, rmsDbV ):
248 267
 
249 268
     ax.plot( pulseUsL, func(pulseUsL), color='red')
250 269
 
251
-def plot_resample_pulse_times_0( inDir, analysisArgsD ):
270
+def plot_resample_pulse_times_0( inDir, analysisArgsD, midi_pitch, printDir="" ):
252 271
 
253
-    newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, analysisArgsD )
272
+    newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, midi_pitch, analysisArgsD )
254 273
 
255
-    midi_pitch = int( inDir.split("/")[-1] )
256 274
     velTblUsL,velTblDbL,_ = form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None )
257 275
     
258 276
     fig,ax = plt.subplots()
@@ -262,16 +280,18 @@ def plot_resample_pulse_times_0( inDir, analysisArgsD ):
262 280
     for us in newPulseUsL:
263 281
         ax.axvline( x = us )
264 282
 
265
-    ax.plot(velTblUsL,velTblDbL,marker='.',linestyle='None')
283
+    print(len(velTblUsL))
284
+    ax.plot(velTblUsL,velTblDbL,marker='.',linestyle='None',color='red')
266 285
 
286
+    if printDir:
287
+        plt.savefig(os.path.join(printDir,"plot_resample_times_0.png"),format="png")
267 288
     
268 289
     plt.show()
269 290
 
270
-def plot_resample_pulse_times( inDir, analysisArgsD ):
291
+def plot_resample_pulse_times( inDir, analysisArgsD, midi_pitch, printDir="" ):
271 292
 
272
-    newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, analysisArgsD )
293
+    newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, midi_pitch, analysisArgsD )
273 294
 
274
-    midi_pitch = int( inDir.split("/")[-1] )
275 295
     velTblUsL,velTblDbL,_ = form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None )
276 296
     
277 297
     fig,axL = plt.subplots(2,1,gridspec_kw={'height_ratios': [2, 1]})
@@ -290,6 +310,10 @@ def plot_resample_pulse_times( inDir, analysisArgsD ):
290 310
     axL[1].axhline(1.0,color='black')
291 311
     axL[1].plot(pulseUsL,np.abs(scoreV * 100.0 / rmsDbV))
292 312
     axL[1].set_ylim((0.0,50))
313
+
314
+    if printDir:
315
+        plt.savefig(os.path.join(printDir,"plot_resample_times.png"),format="png")
316
+
293 317
     plt.show()
294 318
 
295 319
 
@@ -369,7 +393,7 @@ def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
369 393
     """ Plot a single spectrum, 'specV' and the harmonic peak location boundaries."""
370 394
     
371 395
     binN      = specV.shape[0]
372
-    harmLBinL,harmMBinL,harmUBinL = calc_harm_bins( srate, binHz, midiPitch, harmN )
396
+    harmLBinL,harmMBinL,harmUBinL = ra.calc_harm_bins( srate, binHz, midiPitch, harmN )
373 397
 
374 398
     fundHz      = harmMBinL[0] * binHz
375 399
     maxPlotHz   = fundHz * (harmN+1)
@@ -386,33 +410,40 @@ def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
386 410
         ax.axvline( x=h1 * binHz, color="black")
387 411
         ax.axvline( x=h2 * binHz, color="blue")
388 412
 
389
-    ax.set_ylabel(str(midiPitch))
413
+    ax.set_ylabel("dB : %i " % (midiPitch))
390 414
         
391
-def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbLinRef=0.001 ):
392
-    """ Plot the spectrum from one note (7th from last) in each attack pulse length sequence referred to by pitchL."""
415
+def plot_spectral_ranges( inDir, pitchTakeL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbLinRef=0.001, printDir="" ):
416
+    """ Plot the spectrum from one note (7th from last) in each attack pulse length sequence referred to by pitchTakeL."""
393 417
     
394
-    plotN = len(pitchL)
418
+    plotN = len(pitchTakeL)
395 419
     fig,axL = plt.subplots(plotN,1)
396 420
 
397
-    for plot_idx,midiPitch in enumerate(pitchL):
421
+    for plot_idx,(midiPitch,takeId) in enumerate(pitchTakeL):
398 422
 
399 423
         # get the audio and meta-data file names
400
-        seqFn   = os.path.join( inDir, str(midiPitch), "seq.json")
401
-        audioFn = os.path.join( inDir, str(midiPitch), "audio.wav")
424
+        seqFn   = os.path.join( inDir, str(midiPitch), str(takeId), "seq.json")
425
+        audioFn = os.path.join( inDir, str(midiPitch), str(takeId), "audio.wav")
402 426
 
403 427
         # read the meta data object
404 428
         with open( seqFn, "rb") as f:
405 429
             r = json.load(f)
406
-
430
+            
407 431
         # read the audio file
408 432
         srate, signalM  = wavfile.read(audioFn)
433
+
434
+
435
+        # convert the audio signal vector to contain only the first (left) channel
436
+        if len(signalM.shape)>1:
437
+            signalM = signalM[:,0].squeeze()
438
+        
409 439
         sigV  = signalM / float(0x7fff)
440
+        
410 441
 
411 442
         # calc. the RMS envelope in the time domain
412
-        rms0DbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef )
443
+        rms0DbV, rms0_srate = ra.audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef )
413 444
 
414 445
         # locate the sample index of the peak of each note attack 
415
-        pkIdx0L = locate_peak_indexes( rms0DbV, rms0_srate, r['eventTimeL'] )
446
+        pkIdx0L = ra.locate_peak_indexes( rms0DbV, rms0_srate, r['eventTimeL'] )
416 447
 
417 448
         # select the 7th to last note for spectrum measurement
418 449
 
@@ -423,33 +454,47 @@ def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbL
423 454
 
424 455
 
425 456
         # calc. the RMS envelope by taking the max spectral peak in each STFT window 
426
-        rmsDbV, rms_srate, specV, specHopIdx, binHz = audio_stft_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, spectrumSmpIdx)
457
+        rmsDbV, rms_srate, specV, specHopIdx, binHz = ra.audio_stft_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, spectrumSmpIdx)
427 458
 
428 459
         # specV[] is the spectrum of the note at spectrumSmpIdx
429 460
 
430 461
         # plot the spectrum and the harmonic selection ranges
431 462
         plot_spectrum( axL[plot_idx], srate, binHz, specV, midiPitch, harmN )
463
+
464
+    axL[-1].set_xlabel("Hertz")
465
+
466
+    if printDir:
467
+        plt.savefig(os.path.join(printDir,"plot_spectral_ranges.png"),format="png")
468
+    
432 469
     plt.show()
433 470
 
434 471
         
435 472
         
436 473
 def td_plot( ax, inDir, midi_pitch, id, analysisArgsD ):
437 474
 
475
+    # 
438 476
     r = rms_analysis_main( inDir, midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
439 477
 
478
+    # find min/max peak in the sequence
440 479
     min_pk_idx, max_pk_idx = find_min_max_peak_index( r.pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )    
441
-    
480
+
481
+    # find ranges of the sequence which should be skipped because they are noisy or unreliable
442 482
     skipPkIdxL = find_skip_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx )
443 483
 
484
+    # find peaks whose difference to surrounding peaks is greater than 'maxDeltaDb'.
444 485
     jmpPkIdxL = find_out_of_range_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx, analysisArgsD['maxDeltaDb'] )
445 486
 
446
-    secV = np.arange(0,len(r.rmsDbV)) / r.rms_srate
447 487
     
448
-    ax.plot( secV, r.rmsDbV )
449
-    ax.plot( np.arange(0,len(r.tdRmsDbV)) / r.rms_srate, r.tdRmsDbV, color="black" )
488
+    secV = np.arange(0,len(r.rmsDbV)) / r.rms_srate
489
+
490
+    # plot the harmonic RMS signal
491
+    ax.plot( secV, r.rmsDbV, color='blue', label="Harmonic" )
492
+
493
+    # plot the time-domain RMS signal
494
+    ax.plot( np.arange(0,len(r.tdRmsDbV)) / r.rms_srate, r.tdRmsDbV, color="black", label="TD" )
450 495
 
451 496
     
452
-    # print beg/end boundaries
497
+    # print note beg/end/peak boundaries
453 498
     for i,(begMs, endMs) in enumerate(r.eventTimeL):
454 499
 
455 500
         pkSec = r.pkIdxL[i] / r.rms_srate
@@ -472,38 +517,45 @@ def td_plot( ax, inDir, midi_pitch, id, analysisArgsD ):
472 517
             ax.plot( [pki / r.rms_srate], [ r.rmsDbV[pki] ], marker=6, color="blue")
473 518
 
474 519
 
520
+    ax.legend();
521
+    ax.set_ylabel("dB");
522
+            
475 523
     return r
476 524
 
477 525
 
478 526
 
479
-def do_td_plot( inDir, analysisArgs ):
527
+def do_td_plot( inDir, analysisArgs, midi_pitch, takeId, printDir="" ):
480 528
     
481 529
     fig,axL = plt.subplots(3,1)
482 530
     fig.set_size_inches(18.5, 10.5, forward=True)
483 531
 
484
-    id         = int(inDir.split("/")[-1])
485
-    midi_pitch = int(inDir.split("/")[-2])
532
+    # parse the file name
533
+    inDir = os.path.join(inDir,str(midi_pitch),str(takeId));
486 534
 
487
-    r = td_plot(axL[0],inDir,midi_pitch,id,analysisArgs)
535
+    # plot the time domain signal
536
+    r = td_plot(axL[0],inDir,midi_pitch,takeId,analysisArgs)
488 537
 
489 538
     qualityV = np.array([ x.quality  for x in r.statsL ]) * np.max(r.pkDbL)
490 539
     durMsV   = np.array([ x.durMs    for x in r.statsL ])
491 540
     avgV     = np.array([ x.durAvgDb for x in r.statsL ])
492 541
     
493
-    #durMsV[ durMsV < 400 ] = 0
494
-    #durMsV = durMsV * np.max(r.pkDbL)/np.max(durMsV)
495
-    #durMsV  = durMsV / 100.0
496 542
 
497 543
     dV = np.diff(r.pkDbL) / r.pkDbL[1:] 
498 544
     
499
-    axL[1].plot( r.pkUsL, r.pkDbL, marker='.',label="pkDb" )
500
-    axL[1].plot( r.pkUsL, qualityV, marker='.',label="quality" )
501
-    axL[1].plot( r.pkUsL, avgV,     marker='.',label="avgDb" )
545
+    axL[1].plot( r.pkUsL, r.pkDbL,  marker='.',  label="harmonic" )
546
+    #axL[1].plot( r.pkUsL, qualityV, marker='.', label="quality" )
547
+    axL[1].plot( r.pkUsL, avgV,     marker='.', label="harm-td avg" )
548
+    axL[1].legend()
549
+    axL[1].set_ylabel("dB");
550
+    
502 551
     #axL[2].plot( r.pkUsL, durMsV,   marker='.' )
503
-    axL[2].plot( r.pkUsL[1:], dV,       marker='.',label='d')
552
+    axL[2].plot( r.pkUsL[1:], dV,       marker='.',label='delta')
504 553
     axL[2].set_ylim([-1,1])
505
-    axL[1].legend()
554
+    axL[2].legend()
555
+    axL[2].set_ylabel("dB");
506 556
 
557
+    axL[2].set_xlabel("Microseconds")
558
+    
507 559
 
508 560
     sni = select_first_stable_note_by_dur( durMsV )
509 561
     if sni is not None:
@@ -519,41 +571,63 @@ def do_td_plot( inDir, analysisArgs ):
519 571
 
520 572
     for i in range(1,len(r.pkUsL)):
521 573
         axL[2].text( r.pkUsL[i],  dV[i-1], "%i" % (i))
522
-        
523
-    
524
-    plt.show()
525 574
 
526
-def do_td_multi_plot( inDir, analysisArgs ):
575
+    if printDir:
576
+        plt.savefig(os.path.join(printDir,"do_td_plot.png"),format="png")
577
+    plt.show()
527 578
 
528
-    midi_pitch = int(inDir.split("/")[-1])
579
+def do_td_multi_plot( inDir, analysisArgs, pitchTakeL, printPlotFl=False ):
529 580
 
530
-    dirL =  os.listdir(inDir)
581
+    #midi_pitch = int(inDir.split("/")[-1])
531 582
 
532
-    fig,axL = plt.subplots(len(dirL),1)
583
+    fig,axL = plt.subplots(len(pitchTakeL),1)
533 584
 
534 585
     
535
-    for id,(idir,ax) in enumerate(zip(dirL,axL)):
586
+    for id,((pitch,takeId),ax) in enumerate(zip(pitchTakeL,axL)):
536 587
 
537
-        td_plot(ax, os.path.join(inDir,str(id)), midi_pitch, id, analysisArgs )
538
-    
588
+        td_plot(ax, os.path.join(inDir,str(pitch),str(takeId)), pitch, takeId, analysisArgs )
589
+
590
+    ax.set_xlabel("Seconds")
591
+
592
+    if printDir:
593
+        plt.savefig(os.path.join(printDir,"multi_plot.png"),format="png")
539 594
     plt.show()
540 595
 
541 596
     
542 597
 if __name__ == "__main__":
543 598
 
544
-    inDir = sys.argv[1]
545
-    cfgFn = sys.argv[2]
546
-    take_id = None if len(sys.argv)<4 else sys.argv[3]
599
+    printDir = os.path.expanduser("~/src/picadae_ac_3/doc")
600
+    cfgFn = sys.argv[1]
601
+    inDir = sys.argv[2]
602
+    mode  = sys.argv[3]
547 603
 
548 604
     cfg = parse_yaml_cfg( cfgFn )
605
+    
606
+    if mode == "td_plot":
607
+        
608
+        # python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_plot 60 10
609
+        pitch   = int(sys.argv[4])
610
+        take_id = int(sys.argv[5])
611
+        do_td_plot(inDir,cfg.analysisArgs, pitch, take_id, printDir )
549 612
 
550
-    if take_id is not None:
551
-        inDir = os.path.join(inDir,take_id)
552
-        do_td_plot(inDir,cfg.analysisArgs)
553
-    else:    
554
-        #do_td_multi_plot(inDir,cfg.analysisArgs)
613
+    elif mode == "td_multi_plot" or mode == 'plot_spectral_ranges':
555 614
 
556
-        #plot_spectral_ranges( inDir, [ 24, 36, 48, 60, 72, 84, 96, 104] )
615
+        pitchTakeIdL = []
616
+        for i in range(4,len(sys.argv),2):
617
+            pitchTakeIdL.append( (int(sys.argv[i]), int(sys.argv[i+1])) )
557 618
 
558
-        plot_resample_pulse_times( inDir, cfg.analysisArgs )
619
+        if mode == "td_multi_plot":
620
+            # python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_multi_plot 36 2 48 3 60 4
621
+            do_td_multi_plot(inDir, cfg.analysisArgs, pitchTakeIdL, printDir )
622
+        else:
623
+            # python plot_seq.py p_ac.yml ~/temp/p_ac_3_od plot_spectral_ranges 36 2 48 3 60 4
624
+            plot_spectral_ranges( inDir, pitchTakeIdL, printDir=printDir )
625
+        
626
+    elif mode == "resample_pulse_times":
559 627
 
628
+        # python plot_seq.py p_ac.yml ~/temp/p_ac_3_od  resample_pulse_times 60
629
+        pitch   = int(sys.argv[4])        
630
+        plot_resample_pulse_times( inDir, cfg.analysisArgs, pitch, printDir )
631
+        
632
+    else:
633
+        print("Unknown plot mode:%s" % (mode))

+ 103
- 59
plot_seq_1.py View File

@@ -14,8 +14,9 @@ def fit_to_reference( pkL, refTakeId ):
14 14
     dur_outL = []
15 15
     tid_outL = []
16 16
 
17
+
17 18
     dbL,usL,durMsL,takeIdL = tuple(zip(*pkL))
18
-    
19
+
19 20
     us_refL,db_refL,dur_refL = zip(*[(usL[i],dbL[i],durMsL[i]) for i in range(len(usL)) if takeIdL[i]==refTakeId])
20 21
 
21 22
     
@@ -26,8 +27,10 @@ def fit_to_reference( pkL, refTakeId ):
26 27
         if takeId == refTakeId:
27 28
             db_outL += db0L
28 29
         else:
29
-            db1V = elbow.fit_points_to_reference(us0L,db0L,us_refL,db_refL)
30
-            db_outL += db1V.tolist()
30
+            db1V     = elbow.fit_points_to_reference(us0L,db0L,us_refL,db_refL)
31
+
32
+            if db1V is not None:
33
+                db_outL += db1V.tolist()
31 34
 
32 35
         us_outL += us0L
33 36
         dur_outL+= dur0L
@@ -37,18 +40,24 @@ def fit_to_reference( pkL, refTakeId ):
37 40
 
38 41
 
39 42
 def get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD ):
40
-    
43
+
41 44
     inDir = os.path.join(inDir,"%i" % (midi_pitch))
42 45
 
43 46
     takeDirL =  os.listdir(inDir)
44 47
 
45 48
     pkL = []
46 49
 
50
+    refTakeId = None
47 51
     usRefL = None
48 52
     dbRefL = None
49 53
 
50 54
     # for each take in this directory
51
-    for take_number in range(len(takeDirL)):
55
+    for take_folder in takeDirL:
56
+
57
+        take_number = int(take_folder)
58
+
59
+        if refTakeId is None:
60
+            refTakeId = take_number
52 61
 
53 62
         # analyze this takes audio and locate the note peaks
54 63
         r = rms_analysis.rms_analysis_main( os.path.join(inDir,str(take_number)), midi_pitch, **analysisArgsD )
@@ -57,16 +66,27 @@ def get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD ):
57 66
         for db,us,stats in zip(r.pkDbL,r.pkUsL,r.statsL):
58 67
             pkL.append( (db,us,stats.durMs,take_number) )
59 68
 
60
-    pkL = fit_to_reference( pkL, 0 )
69
+
70
+    pkUsL        = []
71
+    pkDbL        = []
72
+    durMsL       = []
73
+    takeIdL      = []
74
+    holdDutyPctL = []
75
+    
76
+    if refTakeId is None:
77
+        print("No valid data files at %s pitch:%i" % (inDir,midi_pitch))
78
+    else:
79
+        
80
+        pkL = fit_to_reference( pkL, refTakeId )
61 81
     
62
-    # sort the peaks on increasing attack pulse microseconds
63
-    pkL = sorted( pkL, key= lambda x: x[1] )
82
+        # sort the peaks on increasing attack pulse microseconds
83
+        pkL = sorted( pkL, key= lambda x: x[1] )
64 84
 
65
-    # merge sample points that separated by less than 'minSampleDistUs' milliseconds
66
-    #pkL = merge_close_sample_points( pkL, analysisArgsD['minSampleDistUs'] )
85
+        # merge sample points that separated by less than 'minSampleDistUs' milliseconds
86
+        #pkL = merge_close_sample_points( pkL, analysisArgsD['minSampleDistUs'] )
67 87
     
68
-    # split pkL 
69
-    pkDbL,pkUsL,durMsL,takeIdL = tuple(zip(*pkL))
88
+        # split pkL 
89
+        pkDbL,pkUsL,durMsL,takeIdL = tuple(zip(*pkL))
70 90
 
71 91
     return pkUsL,pkDbL,durMsL,takeIdL,r.holdDutyPctL
72 92
 
@@ -208,7 +228,7 @@ def get_resample_points( usL, dbL, durMsL, takeIdL, minDurMs, minDb, noiseLimitP
208 228
     return reUsL, reDbL, noiseL, resampleL, skipL, firstAudibleIdx, firstNonSkipIdx
209 229
 
210 230
 def get_resample_points_wrap( inDir, midi_pitch, analysisArgsD ):
211
-
231
+    
212 232
     usL, dbL, durMsL,_,_ = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'] )
213 233
 
214 234
     reUsL,_,_,_,_,_,_ = get_resample_points( usL, dbL, durMsL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
@@ -220,6 +240,7 @@ def get_resample_points_wrap( inDir, midi_pitch, analysisArgsD ):
220 240
 def plot_us_db_curves( ax, inDir, keyMapD, midi_pitch, analysisArgsD, plotResamplePointsFl=False, plotTakesFl=True, usMax=None ):
221 241
 
222 242
     usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'] )
243
+
223 244
     reUsL, reDbL, noiseL, resampleL, skipL, firstAudibleIdx, firstNonSkipIdx  = get_resample_points( usL, dbL, durMsL, takeIdL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
224 245
 
225 246
     # plot first audible and non-skip position
@@ -303,14 +324,15 @@ def plot_us_db_curves( ax, inDir, keyMapD, midi_pitch, analysisArgsD, plotResamp
303 324
     
304 325
     ax.set_ylabel( "%i %s %s" % (midi_pitch, keyMapD[midi_pitch]['type'],keyMapD[midi_pitch]['class']))
305 326
     
306
-def plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True, usMax=None ):
327
+def plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True, usMax=None, printDir="" ):
307 328
 
308 329
     analysisArgsD = cfg.analysisArgs
309
-    keyMapD = { d['midi']:d for d in cfg.key_mapL }
310
-    axN = len(pitchL)
311
-    fig,axL = plt.subplots(axN,1,sharex=True)
330
+    keyMapD       = { d['midi']:d for d in cfg.key_mapL }
331
+    axN           = len(pitchL)
332
+    fig,axL       = plt.subplots(axN,1,sharex=True)
312 333
     if axN == 1:
313 334
         axL = [axL]
335
+        
314 336
     fig.set_size_inches(18.5, 10.5*axN)
315 337
 
316 338
     for ax,midi_pitch in zip(axL,pitchL):
@@ -318,6 +340,9 @@ def plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True, usMax=None ):
318 340
 
319 341
     if plotTakesFl:
320 342
         plt.legend()
343
+
344
+    if printDir:
345
+        plt.savefig(os.path.join(printDir,"us_db.png"),format="png")
321 346
         
322 347
     plt.show()
323 348
 
@@ -332,16 +357,16 @@ def plot_all_noise_curves( inDir, cfg, pitchL=None ):
332 357
 
333 358
     for midi_pitch in pitchL:
334 359
 
335
-        print(midi_pitch)
336
-        
337 360
         usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
338 361
 
339 362
         scoreV       = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
340 363
         
341 364
         minDurMs      = cfg.analysisArgs['resampleMinDurMs']
342
-        minDb         = cfg.analysisArgs['resampleMinDb'],
365
+        minDb         = cfg.analysisArgs['resampleMinDb']
343 366
         noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
344 367
 
368
+        
369
+
345 370
         skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, scoreV.tolist(), takeIdL, minDurMs, minDb, noiseLimitPct )
346 371
     
347 372
 
@@ -364,18 +389,22 @@ def plot_all_noise_curves( inDir, cfg, pitchL=None ):
364 389
     plt.legend()
365 390
     plt.show()
366 391
 
367
-def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2 ):
392
+def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2, printDir=None ):
368 393
 
369 394
     pitchFolderL = os.listdir(inDir)
370
-    
395
+
396
+    print(pitchL)
397
+
371 398
     if pitchL is None:
372 399
         pitchL = [ int( int(pitchFolder) ) for pitchFolder in pitchFolderL ]
373 400
 
374
-
375
-    okL   = []
401
+    print(pitchL)
402
+        
403
+    okL       = []
376 404
     outPitchL = []
377
-    minDbL = []
378
-    maxDbL = []
405
+    minDbL    = []
406
+    maxDbL    = []
407
+    
379 408
     for midi_pitch in pitchL:
380 409
 
381 410
         print(midi_pitch)
@@ -384,11 +413,8 @@ def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2 ):
384 413
 
385 414
         okL.append(False)
386 415
 
387
-        takeId = len(set(takeIdL))-1
388
-
389 416
         db_maxL = sorted(dbL)
390 417
         maxDbL.append( np.mean(db_maxL[-5:]) )
391
-        
392 418
 
393 419
         usL,dbL = zip(*[(usL[i],dbL[i]) for i in range(len(usL)) if takeIdL[i]==takeId  ])
394 420
         
@@ -414,10 +440,14 @@ def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2 ):
414 440
         ax.text( pitch, min_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
415 441
         ax.text( pitch, max_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
416 442
 
417
-
443
+ 
444
+    if printDir:
445
+        plt.savefig(os.path.join(printDir,"min_max_db_2.png"),format="png")
446
+    
447
+       
418 448
     plt.show()
419 449
 
420
-def plot_min_db_manual( inDir, cfg ):
450
+def plot_min_db_manual( inDir, cfg, printDir=None ):
421 451
     
422 452
     pitchL     = list(cfg.manualMinD.keys())
423 453
     
@@ -463,8 +493,10 @@ def plot_min_db_manual( inDir, cfg ):
463 493
 
464 494
         if midi_pitch in cfg.manualAnchorPitchMaxDbL:
465 495
             anchorMaxDbL.append( max_db )
466
-            
496
+
467 497
         
498
+            
499
+    
468 500
 
469 501
     # Form the complete set of min/max db levels for each pitch by interpolating the
470 502
     # db values between the manually selected anchor points.
@@ -491,15 +523,14 @@ def plot_min_db_manual( inDir, cfg ):
491 523
     with open("minInterpDb.json",'w') as f:
492 524
         json.dump( { "pitchL":pitchL, "minDbL":list(interpMinDbL), "maxDbL":list(interpMaxDbL) }, f )
493 525
 
494
-            
495
-            
496
-            
526
+                      
497 527
               
498
-
528
+    if printDir:
529
+        plt.savefig(os.path.join(printDir,"manual_db.png"),format="png")
499 530
 
500 531
     plt.show()
501 532
     
502
-def plot_min_max_db( inDir, cfg, pitchL=None ):
533
+def plot_min_max_db( inDir, cfg, pitchL=None, printDir=None ):
503 534
 
504 535
     pitchFolderL = os.listdir(inDir)
505 536
     
@@ -518,7 +549,7 @@ def plot_min_max_db( inDir, cfg, pitchL=None ):
518 549
         scoreV       = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
519 550
 
520 551
         minDurMs      = cfg.analysisArgs['resampleMinDurMs']
521
-        minDb         = cfg.analysisArgs['resampleMinDb'],
552
+        minDb         = cfg.analysisArgs['resampleMinDb']
522 553
         noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
523 554
 
524 555
         skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, takeIdL, scoreV.tolist(), minDurMs, minDb, noiseLimitPct )        
@@ -548,6 +579,9 @@ def plot_min_max_db( inDir, cfg, pitchL=None ):
548 579
 
549 580
         ax.text( pitch, db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']))
550 581
 
582
+    if printDir:
583
+        plt.savefig(os.path.join(printDir,"min_max_db.png"),format="png")
584
+    
551 585
 
552 586
     plt.show()
553 587
     
@@ -575,7 +609,7 @@ def estimate_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=0
575 609
         scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
576 610
         
577 611
         minDurMs      = cfg.analysisArgs['resampleMinDurMs']
578
-        minDb         = cfg.analysisArgs['resampleMinDb'],
612
+        minDb         = cfg.analysisArgs['resampleMinDb']
579 613
         noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
580 614
 
581 615
         # get the set of samples that are not valid (too short, too quiet, too noisy)
@@ -633,7 +667,7 @@ def estimate_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=0
633 667
             
634 668
     return mapD, list(dbS)
635 669
 
636
-def plot_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=1.0, pitchL=None ):
670
+def plot_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=1.0, pitchL=None, printDir=None ):
637 671
 
638 672
     fig,ax = plt.subplots()
639 673
     
@@ -644,22 +678,28 @@ def plot_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=1.0,
644 678
 
645 679
         u_dL = [ (d['us_avg'],d['us_cls'],d['db_avg'],d['us_std'],d['us_min'],d['us_max'],d['db_std']) for loDb, d in  dbD.items() if d['us_avg'] != 0 ]
646 680
 
647
-        # get the us/db lists for this pitch
648
-        usL,uscL,dbL,ussL,usnL,usxL,dbsL = zip(*u_dL)
681
+        if u_dL:
682
+        
683
+            # get the us/db lists for this pitch
684
+            usL,uscL,dbL,ussL,usnL,usxL,dbsL = zip(*u_dL)
649 685
 
650
-        # plot central curve and std dev's
651
-        p = ax.plot(usL,dbL, marker='.', label=str(pitch))
652
-        ax.plot(uscL,dbL, marker='x', label=str(pitch), color=p[0].get_color(), linestyle='None')
653
-        ax.plot(usL,np.array(dbL)+dbsL, color=p[0].get_color(), alpha=0.3)
654
-        ax.plot(usL,np.array(dbL)-dbsL, color=p[0].get_color(), alpha=0.3)
686
+            # plot central curve and std dev's
687
+            p = ax.plot(usL,dbL, marker='.', label=str(pitch))
688
+            ax.plot(uscL,dbL, marker='x', label=str(pitch), color=p[0].get_color(), linestyle='None')
689
+            ax.plot(usL,np.array(dbL)+dbsL, color=p[0].get_color(), alpha=0.3)
690
+            ax.plot(usL,np.array(dbL)-dbsL, color=p[0].get_color(), alpha=0.3)
655 691
 
656
-        # plot us error bars
657
-        for db,us,uss,us_min,us_max in zip(dbL,usL,ussL,usnL,usxL):
658
-            ax.plot([us_min,us_max],[db,db], color=p[0].get_color(), alpha=0.3 )
659
-            ax.plot([us-uss,us+uss],[db,db], color=p[0].get_color(), alpha=0.3, marker='.', linestyle='None' )
692
+            # plot us error bars
693
+            for db,us,uss,us_min,us_max in zip(dbL,usL,ussL,usnL,usxL):
694
+                ax.plot([us_min,us_max],[db,db], color=p[0].get_color(), alpha=0.3 )
695
+                ax.plot([us-uss,us+uss],[db,db], color=p[0].get_color(), alpha=0.3, marker='.', linestyle='None' )
660 696
 
661 697
                     
662 698
     plt.legend()
699
+
700
+    if printDir:
701
+        plt.savefig(os.path.join(printDir,"us_db_map.png"),format="png")
702
+    
663 703
     plt.show()
664 704
 
665 705
 def report_take_ids( inDir ):
@@ -760,9 +800,11 @@ def gen_vel_map( inDir, cfg, minMaxDbFn, dynLevelN, cacheFn ):
760 800
     
761 801
 if __name__ == "__main__":
762 802
 
763
-    inDir = sys.argv[1]
764
-    cfgFn = sys.argv[2]
765
-    mode  = sys.argv[3]
803
+    printDir =os.path.expanduser( "~/src/picadae_ac_3/doc")
804
+    cfgFn   = sys.argv[1]
805
+    inDir   = sys.argv[2]
806
+    mode    = sys.argv[3]
807
+    
766 808
     if len(sys.argv) <= 4:
767 809
         pitchL = None
768 810
     else:
@@ -771,21 +813,23 @@ if __name__ == "__main__":
771 813
     cfg = parse_yaml_cfg( cfgFn )
772 814
 
773 815
     if mode == 'us_db':
774
-        plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True,usMax=None )
816
+        plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True,usMax=None, printDir=printDir )
775 817
     elif mode == 'noise':
776 818
         plot_all_noise_curves( inDir, cfg, pitchL )
777 819
     elif mode == 'min_max':
778
-        plot_min_max_db( inDir, cfg, pitchL )
820
+        plot_min_max_db( inDir, cfg, pitchL, printDir=printDir )
779 821
     elif mode == 'min_max_2':
780
-        plot_min_max_2_db( inDir, cfg, pitchL )
822
+        takeId = pitchL[-1]
823
+        del pitchL[-1]
824
+        plot_min_max_2_db( inDir, cfg, pitchL, takeId=takeId, printDir=printDir )
781 825
     elif mode == 'us_db_map':
782
-        plot_us_to_db_map( inDir, cfg, pitchL=pitchL )
826
+        plot_us_to_db_map( inDir, cfg, pitchL=pitchL, printDir=printDir )
783 827
     elif mode == 'audacity':
784 828
         rms_analysis.write_audacity_label_files( inDir, cfg.analysisArgs['rmsAnalysisArgs'] )
785 829
     elif mode == 'rpt_take_ids':
786 830
         report_take_ids( inDir )
787 831
     elif mode == 'manual_db':
788
-        plot_min_db_manual( inDir, cfg )
832
+        plot_min_db_manual( inDir, cfg, printDir=printDir )
789 833
     elif mode == 'gen_vel_map':
790 834
         gen_vel_map( inDir, cfg, "minInterpDb.json", 9, "cache_us_db.json" )
791 835
     elif mode == 'cache_us_db':

+ 62
- 17
rms_analysis.py View File

@@ -201,12 +201,14 @@ def select_first_stable_note_by_delta_db( pkDbL, pkUsL=None, maxPulseUs=0.1 ):
201 201
     return None
202 202
 
203 203
 def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
204
-    
204
+    """ Collect some statistics and markers for each note in the sequence. """
205 205
     statsL = []
206 206
 
207 207
     srate = r.rms_srate
208 208
 
209 209
     qmax = 0
210
+
211
+    
210 212
     
211 213
     for i,(begSmpMs, endSmpMs) in enumerate(r.eventTimeL):
212 214
 
@@ -214,6 +216,7 @@ def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
214 216
         endSmpIdx = int(round(srate * (endSmpMs + extraDurSearchMs) / 1000.0))
215 217
         pkSmpIdx  = r.pkIdxL[i]
216 218
 
219
+        
217 220
         durMs = measure_duration_ms( r.rmsDbV, srate, pkSmpIdx, endSmpIdx, decay_pct )
218 221
 
219 222
         bi = pkSmpIdx
@@ -232,7 +235,15 @@ def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
232 235
             hmRmsDb_u = 0.0 if ei >= len(r.rmsDbV)   else np.mean(r.rmsDbV[bi:ei])
233 236
             durAvgDb = (hmRmsDb_u + tdRmsDb_u)/2.0
234 237
 
235
-        statsL.append( types.SimpleNamespace(**{'begSmpSec':begSmpIdx/srate,'endSmpSec':endSmpIdx/srate,'pkSmpSec':pkSmpIdx/srate,'durMs':durMs, 'pkDb':r.pkDbL[i], 'pulse_us':r.pkUsL[i], 'quality':qualityCoeff, 'durAvgDb':durAvgDb }))
238
+        statsL.append( types.SimpleNamespace(**{
239
+            'begSmpSec':begSmpIdx/srate,
240
+            'endSmpSec':endSmpIdx/srate,
241
+            'pkSmpSec':pkSmpIdx/srate,
242
+            'durMs':durMs,
243
+            'pkDb':r.pkDbL[i],
244
+            'pulse_us':r.pkUsL[i],
245
+            'quality':qualityCoeff,
246
+            'durAvgDb':durAvgDb }))
236 247
 
237 248
     for i,r in enumerate(statsL):
238 249
         statsL[i].quality = 0 if qmax <= 0 else statsL[i].quality / qmax
@@ -243,15 +254,19 @@ def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
243 254
         
244 255
         
245 256
                
246
-def locate_peak_indexes( xV, xV_srate, eventMsL ):
257
+def locate_peak_indexes( xV, xV_srate, eventMsL, audioFn ):
247 258
 
248 259
     pkIdxL = []
249
-    for begMs, endMs in eventMsL:
260
+    for i, (begMs, endMs) in enumerate(eventMsL):
250 261
 
251 262
         begSmpIdx = int(begMs * xV_srate / 1000.0)
252 263
         endSmpIdx = int(endMs * xV_srate / 1000.0)
253 264
 
254
-        pkIdxL.append( begSmpIdx + np.argmax( xV[begSmpIdx:endSmpIdx] ) )
265
+        if endSmpIdx != begSmpIdx:
266
+            pkIdxL.append( begSmpIdx + np.argmax( xV[begSmpIdx:endSmpIdx] ) )
267
+        else:
268
+            print("Missing peak %i : begween beg:%i ms end:%i ms : %s" % (i, begMs, endMs, audioFn ))
269
+            pkIdxL.append( None )
255 270
 
256 271
     return pkIdxL
257 272
 
@@ -304,6 +319,11 @@ def rms_analyze_one_rt_note_wrap( audioDev, annBegMs, annEndMs, midi_pitch, note
304 319
 
305 320
         sigV = buf_result.value
306 321
 
322
+        if len(sigV.shape) > 1:
323
+            sigV = sigV[:,0].squeeze()
324
+        
325
+        
326
+
307 327
         # get the annotated begin and end of the note as sample indexes into sigV
308 328
         bi = int(round(annBegMs * audioDev.srate / 1000))
309 329
         ei = int(round(annEndMs * audioDev.srate / 1000))
@@ -399,47 +419,72 @@ def calibrate_recording_analysis( inDir ):
399 419
 
400 420
 def rms_analysis_main( inDir, midi_pitch, rmsWndMs=300, rmsHopMs=30, dbLinRef=0.001, harmCandN=5, harmN=3, durDecayPct=40 ):
401 421
 
422
+    # form the audio and meta data file names
402 423
     seqFn     = os.path.join( inDir, "seq.json")
403 424
     audioFn   = os.path.join( inDir, "audio.wav")
404 425
     
405
-
426
+    # read the meta data file
406 427
     with open( seqFn, "rb") as f:
407 428
         r = json.load(f)
408 429
     
409
-    
430
+    # rad the auido file
410 431
     srate, signalM  = wavfile.read(audioFn)
432
+
433
+    # convert the audio signal vector to contain only the first (left) channel
434
+    if len(signalM.shape)>1:
435
+        signalM = signalM[:,0].squeeze()
436
+
437
+    # convert the auido file to floats in range [-1.0 to 1.0]
411 438
     sigV  = signalM / float(0x7fff)
412 439
 
440
+    # calc. the RMS signal
413 441
     tdRmsDbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef )
414 442
 
415
-    tdPkIdxL = locate_peak_indexes( tdRmsDbV, rms0_srate,  r['eventTimeL'])
416
-    
443
+    # locate the peaks in the RMS signal
444
+    tdPkIdxL = locate_peak_indexes( tdRmsDbV, rms0_srate,  r['eventTimeL'], audioFn )
445
+
446
+    # sum the first harmN harmonics to form a envelope of the audio signal (this is an alternative to the RMS signal)
417 447
     rmsDbV, rms_srate, binHz = audio_harm_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, midi_pitch, harmCandN, harmN  )
418 448
 
419
-    pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'] )    
449
+    # locate the peaks in the harmonic sum signal
450
+    pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'], audioFn )    
420 451
 
452
+    # form the 'holdDutyPctlL' hold duty cycle transition point list
421 453
     holdDutyPctL = None
422 454
     if 'holdDutyPct' in r:
423 455
         holdDutyPctL = [ (0, r['holdDutyPct']) ]
424 456
     else:
425 457
         holdDutyPctL = r['holdDutyPctL']
426 458
 
427
-
459
+    eventN = len(r['eventTimeL'])
460
+    assert( len(tdPkIdxL) == eventN and len(pkIdxL) == eventN )
461
+        
462
+    # filter out all missing events that have no peak
463
+    flL        = [ (tdPkIdxL[i] is not None) and  (pkIdxL[i] is not None)  for i in range(eventN) ]
464
+    eventTimeL = [ r['eventTimeL'][i]       for i in range(eventN) if  flL[i] ]
465
+    tdPkDbL    = [ tdRmsDbV[tdPkIdxL[i]]    for i in range(eventN) if  flL[i] ]
466
+    pkDbL      = [ rmsDbV[ pkIdxL[i]]       for i in range(eventN) if  flL[i] ]
467
+    #pkUsL      = [ r['pulseUsL'][pkIdxL[i]] for i in range(eventN) if  flL[i] ]
468
+    tdPkIdxL   = [ i                        for i in tdPkIdxL  if i is not None ]
469
+    pkIdxL     = [ i                        for i in pkIdxL    if i is not None ]
470
+    
471
+    # form the result record
428 472
     r = types.SimpleNamespace(**{
429 473
         "audio_srate":srate,
430
-        "eventTimeMsL":r['eventTimeL'],
474
+        "eventTimeMsL": eventTimeL, #r['eventTimeL'],
431 475
         "tdRmsDbV": tdRmsDbV,
432 476
         "tdPkIdxL": tdPkIdxL,
433
-        "tdPkDbL": [ tdRmsDbV[i] for i in tdPkIdxL ],
477
+        "tdPkDbL": tdPkDbL,         # [ tdRmsDbV[i] for i in tdPkIdxL ],
434 478
         "binHz": binHz,
435
-        "rmsDbV":rmsDbV,
479
+        "rmsDbV": rmsDbV,
436 480
         "rms_srate":rms_srate,
437 481
         "pkIdxL":pkIdxL,            # pkIdxL[ len(pulsUsL) ] - indexes into rmsDbV[] of peaks
438
-        "eventTimeL":r['eventTimeL'],
482
+        "eventTimeL": eventTimeL, #r['eventTimeL'],
439 483
         "holdDutyPctL":holdDutyPctL,
440
-        'pkDbL': [ rmsDbV[ i ] for i in pkIdxL ],
441
-        'pkUsL':r['pulseUsL'] })
484
+        'pkDbL': pkDbL, # [ rmsDbV[ i ] for i in pkIdxL ],
485
+        'pkUsL': r['pulseUsL'] })  # r['pulseUsL']
442 486
 
487
+    # 
443 488
     statsL = note_stats(r,durDecayPct)
444 489
 
445 490
     setattr(r,"statsL",   statsL )

Loading…
Cancel
Save