9 Commitit

Tekijä SHA1 Viesti Päivämäärä
  kevin 7119fe471b rms_analysis.py : Don't crash in write_audacity_label_files() when seq.json does not exist. 3 vuotta sitten
  kevin f36e7c8a64 plot_seq_1.py : Many changes and updates. 3 vuotta sitten
  kevin e32f224d12 elbow.py : In find_elbow() change default 'pointsPerLine' to 5. 3 vuotta sitten
  kevin 109944f130 README.md : Update formatting. 3 vuotta sitten
  kevin 6b845bd1b3 MidiDevice.py : get_input() now loops until all available messages are received. 3 vuotta sitten
  kevin 55695164b5 p_ac.py, p_ac.yml : Added use of VelTablePlayer,NoteTester,PolyNoteTester,ChordTester 3 vuotta sitten
  kevin ca030abcf5 ChordTester.py,NoteTester.py,PolyNoteTester.py,VelTablePlayer.py : iniital commit. 3 vuotta sitten
  kevin d96bc2fded .gitignore : ignore *.ipynb files. 3 vuotta sitten
  kevin 9374bcee0c Delete accidentallly tracked *.ipynb files 3 vuotta sitten
15 muutettua tiedostoa jossa 1065 lisäystä ja 203 poistoa
  1. 2
    1
      .gitignore
  2. 68
    0
      ChordTester.py
  3. 5
    3
      MidiDevice.py
  4. 119
    0
      NoteTester.py
  5. 148
    0
      PolyNoteTester.py
  6. 23
    16
      README.md
  7. 148
    0
      VelTablePlayer.py
  8. 2
    2
      elbow.py
  9. 138
    33
      p_ac.py
  10. 261
    98
      p_ac.yml
  11. 1
    1
      plot_all_note_durations.ipynb
  12. 1
    1
      plot_calibrate.ipynb
  13. 144
    46
      plot_seq_1.py
  14. 1
    1
      plot_us_db_range.ipynb
  15. 4
    1
      rms_analysis.py

+ 2
- 1
.gitignore Näytä tiedosto

1
+*.ipynb
1
 *.pyc
2
 *.pyc
2
-.ipynb_checkpoints
3
+.ipynb_checkpoints

+ 68
- 0
ChordTester.py Näytä tiedosto

1
+import types
2
+import time
3
+
4
+class ChordTester:
5
+    def __init__( self, cfg, api ):
6
+        self.api = api
7
+        self.cfg = types.SimpleNamespace(**cfg.ChordTester)
8
+
9
+        self.nextMs       = 0
10
+        self.isStartedFl  = False
11
+        self.curNoteCnt   = 0
12
+        self.curRepeatCnt = 0
13
+        self.isPlayingFl  = False
14
+
15
+
16
+    def start( self ):
17
+        self.api.set_hold_duty_all( self.cfg.holdDuty )
18
+        if self.cfg.useVelTableFl:
19
+            self.api.set_vel_table_all( self.cfg.pitchL )
20
+        self.curNoteCnt = 0
21
+        self.curRepeatCnt = 0
22
+        self.isStartedFl = True
23
+
24
+    def stop( self ):
25
+        self.isStartedFl = False
26
+        self.api.all_notes_off()
27
+        
28
+
29
+
30
+    def tick( self, ms ):
31
+
32
+        if self.isStartedFl and ms >= self.nextMs:
33
+
34
+            if self.isPlayingFl:
35
+
36
+                # turn notes off
37
+                for i in range(0,self.curNoteCnt+1):
38
+                    self.api.note_off( self.cfg.pitchL[i])
39
+                    time.sleep( 0.01 )
40
+
41
+                # repeat or advance the chord note count
42
+                self.curRepeatCnt += 1
43
+                if self.curRepeatCnt >= self.cfg.repeatCnt:
44
+                    self.curRepeatCnt = 0
45
+                    self.curNoteCnt  += 1
46
+                    if self.curNoteCnt >= len(self.cfg.pitchL):
47
+                        self.isStartedFl = False
48
+                        self.curNoteCnt = 0
49
+                        
50
+                self.isPlayingFl = False
51
+                self.nextMs      = ms + self.cfg.pauseMs
52
+                
53
+            else:
54
+                for i in range(0,self.curNoteCnt+1):
55
+                    if self.cfg.useVelTableFl:
56
+                        self.api.note_on_vel(self.cfg.pitchL[i], 45 )
57
+                    else:
58
+                        self.api.note_on_us(self.cfg.pitchL[i], self.cfg.atkUsec)
59
+                    time.sleep( 0.02 )
60
+                    
61
+                self.nextMs = ms + self.cfg.durMs
62
+                self.isPlayingFl = True
63
+                    
64
+                    
65
+                    
66
+
67
+        
68
+            

+ 5
- 3
MidiDevice.py Näytä tiedosto

136
         o_msgL = []
136
         o_msgL = []
137
         
137
         
138
         if self.mip is not None:
138
         if self.mip is not None:
139
-            midi_msg = self.mip.get_message()
140
-            if midi_msg and midi_msg[0]:
139
+            while True:
140
+                midi_msg = self.mip.get_message()
141
+                if not midi_msg or not midi_msg[0]:
142
+                    break;
141
                 
143
                 
142
-                if self.monitorInFl:
144
+                if self.inMonitorFl:
143
                     o_msgL.append( self._midi_data_to_text_msg(True,midi_msg[0]) )
145
                     o_msgL.append( self._midi_data_to_text_msg(True,midi_msg[0]) )
144
                     
146
                     
145
                 if self.throughFl and self.mop is not None:
147
                 if self.throughFl and self.mop is not None:

+ 119
- 0
NoteTester.py Näytä tiedosto

1
+import sys,os,types,json
2
+from random import randrange
3
+
4
+class NoteTester:
5
+    def __init__( self, cfg, api  ):
6
+        self.cfg = cfg
7
+        self.api = api
8
+
9
+        r = types.SimpleNamespace(**cfg.NoteTester)
10
+        
11
+        self.durMsL   = [ randrange(r.minNoteDurMs,  r.maxNoteDurMs)  for _ in range(r.noteCount) ]
12
+        self.pauseMsL = [ randrange(r.minPauseDurMs, r.maxPauseDurMs) for _ in range(r.noteCount) ]
13
+        self.eventL   = []
14
+        self.nextMs   = 0        # next transition time
15
+        self.eventIdx = 0        # next event to play
16
+        self.noteOnFl = False    # True if note is currently sounding
17
+        self.pitch    = r.pitch  #
18
+        self.filename    = r.filename
19
+        self.isStartedFl = False
20
+        self.minAttackUsec = r.minAttackUsec
21
+        self.maxAttackUsec = r.maxAttackUsec
22
+    
23
+    def start( self ):
24
+        self.eventIdx = 0
25
+        self.noteOnFl = False
26
+        self.nextMs   = 0
27
+        self.isStartedFl = True
28
+
29
+    def stop( self ):
30
+        self.isStartedFl = False
31
+        self.write()
32
+
33
+    def tick( self, ms ):
34
+
35
+        if self.isStartedFl and ms > self.nextMs:
36
+
37
+            offsMs = 0
38
+            
39
+            if self.noteOnFl:
40
+                self.noteOnFl = False
41
+                self.api.note_off( self.pitch )                
42
+                offsMs = self.pauseMsL[ self.eventIdx ]
43
+                self.eventIdx += 1
44
+                print("off:%i ms" % (offsMs))
45
+                    
46
+
47
+            else:
48
+                usec        = self.minAttackUsec + (int(self.eventIdx * 250) % int(self.maxAttackUsec - self.minAttackUsec))
49
+                decay_level = self.api.calc_decay_level( usec )
50
+
51
+                self.api.note_on_us( self.pitch, usec, decay_level )
52
+                offsMs = self.durMsL[ self.eventIdx ]
53
+                print("usec:%i %i dcy:%i" % (usec,offsMs, decay_level) )
54
+                self.noteOnFl = True
55
+                
56
+
57
+            self.eventL.append( (ms, self.noteOnFl) )
58
+            self.nextMs = ms + offsMs
59
+    
60
+            if self.eventIdx >= len(self.durMsL):
61
+                self.write();
62
+                self.isStartedFl = False
63
+                print("done % i" % (len(self.eventL)))
64
+
65
+        
66
+        
67
+    def write( self ):
68
+
69
+        with open(self.filename,"w") as f:
70
+            json.dump({ "eventL":self.eventL },f )
71
+
72
+
73
+
74
+def note_tester_compare( nt_fn, logica_fn ):
75
+
76
+    eventL  = []
77
+    logicaL = []
78
+
79
+    with open(nt_fn,"r") as f:
80
+        r = json.load(f)
81
+        eventL =  r['eventL']
82
+        eventL = [ (ms-eventL[0][0], level ) for ms,level in eventL  ]
83
+
84
+    with open(logica_fn,"r") as f:
85
+        logicaL = [ ( d['count']/16e3,d['level'])  for d in json.load(f) if d['signal'] == 0 ]
86
+        logicaL = [ (ms-logicaL[0][0], level!=0 ) for ms,level in logicaL ]
87
+
88
+
89
+    print(len(eventL))
90
+    print(len(logicaL))
91
+
92
+    #edL = [ eventL[i][0]  - eventL[i-1][0]  for i in range(2,len(eventL)) ]
93
+    #ldL = [ logicaL[i][0] - logicaL[i-1][0] for i in range(2,len(logicaL)) ]
94
+
95
+    #print(edL[:10])
96
+    #print(ldL[:10])
97
+
98
+
99
+    durMs = 0
100
+    ms = 0
101
+    for i,(t0,t1) in enumerate(zip(eventL,logicaL)):
102
+        t = t0[0]                              # eventL[] time
103
+        dt = int(t - t1[0])                    # diff between eventL[] and logicaL[] time
104
+        fl = ' ' if t0[1] == t1[1] else '*'    # mark level mismatch with '*'
105
+        print("%5i %7i %4i %i %s" % (i,durMs,dt,t0[1],fl))
106
+        durMs = t-ms                             
107
+        ms = t
108
+        
109
+    
110
+if __name__ == "__main__":
111
+
112
+    nt_fn = "note_tester.json"
113
+    logica_fn = sys.argv[1]
114
+    if len(sys.argv) > 2:
115
+        nt_fn     = sys.argv[2]
116
+
117
+    note_tester_compare( nt_fn, logica_fn)
118
+        
119
+    

+ 148
- 0
PolyNoteTester.py Näytä tiedosto

1
+import types
2
+import time
3
+from random import randrange
4
+
5
+class PolyNoteTester:
6
+    def __init__( self, cfg, api  ):
7
+        self.api = api
8
+
9
+        r = types.SimpleNamespace(**cfg.PolyNoteTester)
10
+        self.cfg = r
11
+
12
+        if r.mode == "simple":
13
+            print("mode:simple")
14
+            self.schedL = self._gen_simple_sched(r)
15
+        else:
16
+            print("mode:poly")
17
+            self.schedL = self._gen_sched(r)
18
+
19
+            
20
+        self.schedL = sorted( self.schedL, key=lambda x: x[0] )    
21
+        self.nextMs   = 0        # next transition time
22
+        self.schedIdx = 0        # next event to play
23
+        self.isStartedFl   = False
24
+
25
+        #self._report()
26
+
27
+    def _report( self ):
28
+        for t,cmd,pitch,atkUs in self.schedL:
29
+            print("%s %6i %3i %5i" % (cmd,t,pitch,atkUs))
30
+
31
+    def _gen_simple_sched( self, r ):
32
+        """ Play each note sequentially from lowest to highest. """
33
+        durMs  = int(r.minNoteDurMs + (r.maxNoteDurMs - r.minNoteDurMs)/2)
34
+        ioMs   = int(r.minInterOnsetMs + (r.maxInterOnsetMs - r.minInterOnsetMs)/2)
35
+        atkUs  = int(r.minAttackUsec + (r.maxAttackUsec - r.minAttackUsec)/2)
36
+        schedL = []
37
+
38
+        t0 = 0
39
+        for pitch in range(r.minPitch,r.maxPitch+1):
40
+            schedL.append((t0,'on',pitch,atkUs))
41
+            schedL.append((t0+durMs,'off',pitch,0))
42
+            t0 += durMs + ioMs
43
+
44
+        return schedL
45
+
46
+        
47
+    def _does_note_overlap( self, beg0Ms, end0Ms, beg1Ms, end1Ms ):
48
+        """ if note 0 is entirely before or after note 1 """
49
+        return not (beg0Ms > end1Ms or end0Ms < beg1Ms)
50
+
51
+    def _do_any_notes_overlap( self, begMs, endMs, begEndL ):
52
+        for beg1,end1 in begEndL:
53
+            if self._does_note_overlap(begMs,endMs,beg1,end1):
54
+                return True
55
+        return False
56
+
57
+    def _get_last_end_time( self, begEndL ):
58
+        end0 = 0
59
+        for beg,end in begEndL:
60
+            if end > end0:
61
+                end0 = end
62
+
63
+        return end0
64
+            
65
+                    
66
+    def _gen_sched( self, r ):
67
+
68
+        pitchL = [ randrange(r.minPitch,r.maxPitch) for _ in range(r.noteCount) ]
69
+        durMsL = [ randrange(r.minNoteDurMs, r.maxNoteDurMs) for _ in range(r.noteCount) ]
70
+        ioMsL  = [ randrange(r.minInterOnsetMs, r.maxInterOnsetMs) for _ in range(r.noteCount) ]
71
+        atkUsL = [ randrange(r.minAttackUsec,   r.maxAttackUsec)   for _ in range(r.noteCount) ]
72
+        schedL = []
73
+        pitchD = {} # pitch: [ (begMs,endMs) ]
74
+
75
+        t0 = 0
76
+        # for each pitch,dur,ioi,atkUs tuple
77
+        for pitch,durMs,interOnsetMs,atkUs in zip(pitchL,durMsL,ioMsL,atkUsL):
78
+
79
+            # calc note begin and end time
80
+            begMs = t0
81
+            endMs = t0 + durMs
82
+
83
+            # if this pitch hasn't yet been added to pitchD
84
+            if pitch not in pitchD:
85
+                pitchD[ pitch ] = [ (begMs,endMs) ]
86
+            else:
87
+
88
+                # if the proposed note overlaps with other notes for this pitch
89
+                if self._do_any_notes_overlap( begMs, endMs, pitchD[pitch] ):
90
+                    # move this pitch past the last note
91
+                    begMs = self._get_last_end_time( pitchD[pitch] ) + interOnsetMs
92
+                    endMs = begMs + durMs
93
+
94
+                # add the new note to pitchD
95
+                pitchD[ pitch ].append( (begMs,endMs) )
96
+
97
+            
98
+            # update the schedule
99
+            schedL.append( (begMs, 'on', pitch, atkUs)) 
100
+            schedL.append( (endMs, 'off', pitch, 0 ))
101
+            
102
+            t0 += interOnsetMs
103
+
104
+        
105
+        return schedL
106
+        
107
+        
108
+    def start( self ):
109
+        self.schedIdx = 0
110
+        self.nextMs   = 0
111
+        self.api.set_hold_duty_all(self.cfg.holdDutyPct)
112
+        self.isStartedFl = True
113
+        
114
+
115
+    def stop( self ):
116
+        self.isStartedFl = False
117
+        self.api.all_notes_off()
118
+        
119
+
120
+    def tick( self, ms ):
121
+
122
+        while self.isStartedFl and ms >= self.nextMs and self.schedIdx < len(self.schedL):
123
+
124
+            t0,cmd,pitch,usec = self.schedL[self.schedIdx]
125
+
126
+            if cmd == 'on':
127
+                if pitch not in self.cfg.skipPitchL:
128
+                    decay_level = self.api.calc_decay_level( usec )
129
+                    self.api.note_on_us( pitch, usec, decay_level )
130
+                    print("on  %i %i %i" % (pitch,usec,decay_level))
131
+                
132
+            elif cmd == 'off':
133
+                if pitch not in self.cfg.skipPitchL:
134
+                    self.api.note_off( pitch )
135
+                    print("off %i" % pitch)
136
+            
137
+            self.schedIdx += 1
138
+            if self.schedIdx < len(self.schedL):
139
+                self.nextMs = ms + (self.schedL[self.schedIdx][0] - t0)
140
+            else:
141
+                self.isStartedFl = False
142
+                print("Done.")
143
+        
144
+        
145
+
146
+
147
+
148
+    

+ 23
- 16
README.md Näytä tiedosto

27
 ## Plot Cheat Sheet
27
 ## Plot Cheat Sheet
28
 
28
 
29
 
29
 
30
-![Plot Seq 1](doc/do_td_plot.png)
30
+---
31
 
31
 
32
 Print a specific pitch and take.
32
 Print a specific pitch and take.
33
 
33
 
34
+![Plot Seq 1](doc/do_td_plot.png)
35
+
36
+
34
 ````
37
 ````
35
     python plot_seq.py p_ac.yml ~/temp/p_ac_3_of td_plot 60 2
38
     python plot_seq.py p_ac.yml ~/temp/p_ac_3_of td_plot 60 2
36
     
39
     
39
 
42
 
40
 ---
43
 ---
41
 
44
 
42
-![Multi Usec dB](doc/us_db.png)
43
-
44
 Plot all the takes for a given pitch
45
 Plot all the takes for a given pitch
45
 
46
 
47
+![Multi Usec dB](doc/us_db.png)
48
+
46
 ````
49
 ````
47
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od us_db 84
50
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od us_db 84
48
 ````
51
 ````
49
 
52
 
50
 ---
53
 ---
51
 
54
 
55
+Plot a specific set of pitches and takes.
56
+
52
 ![Overlapping USec dB](doc/us_db_takes.png)
57
 ![Overlapping USec dB](doc/us_db_takes.png)
53
 
58
 
54
-Plot a specific set of pitches and takes.
55
 
59
 
56
 ````
60
 ````
57
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_of us_db_pitch_take 75 0 76 0 77 0 78 0 72 10 73 1 74 1
61
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_of us_db_pitch_take 75 0 76 0 77 0 78 0 72 10 73 1 74 1
59
 
63
 
60
 ---
64
 ---
61
 
65
 
66
+Plot the last take from a list of pitches.
67
+
62
 ![Overlapping USec dB](doc/us_db_takes_last.png)
68
 ![Overlapping USec dB](doc/us_db_takes_last.png)
63
 
69
 
64
-Plot the last take from a list of pitches.
65
 
70
 
66
 ````
71
 ````
67
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_of us_db_pitch_last 77 78 79 80
72
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_of us_db_pitch_last 77 78 79 80
70
 
75
 
71
 ---
76
 ---
72
 
77
 
78
+Plot the time domain envelope for a specific set of pitches and takes.
79
+
73
 ![Multi Plot 1](doc/multi_plot.png)
80
 ![Multi Plot 1](doc/multi_plot.png)
74
 
81
 
75
-Plot the time domain envelope for a specific set of pitches and takes.
76
 
82
 
77
 ````
83
 ````
78
 python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_multi_plot 60 3 60 4 60 5
84
 python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_multi_plot 60 3 60 4 60 5
81
 ````
87
 ````
82
 ---
88
 ---
83
 
89
 
90
+Plot the spectrum with harmonic location markers of a specific set of pitches and takes.
91
+
84
 ![Spectral Ranges](doc/plot_spectral_ranges.png)
92
 ![Spectral Ranges](doc/plot_spectral_ranges.png)
85
 
93
 
86
-Plot the spectrum with harmonic location markers of a specific set of pitches and takes.
87
 
94
 
88
 ````
95
 ````
89
 #                                                                 pitch0 takeId0 pitch1 takeId1
96
 #                                                                 pitch0 takeId0 pitch1 takeId1
93
 
100
 
94
 ---
101
 ---
95
 
102
 
96
-![Usec dB Spread](doc/us_db_map.png)
97
-
98
 Plot the microsecond variance to achieve a given decibel value.
103
 Plot the microsecond variance to achieve a given decibel value.
99
 
104
 
105
+![Usec dB Spread](doc/us_db_map.png)
106
+
100
 ````
107
 ````
101
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od us_db_map 84 72
108
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od us_db_map 84 72
102
 ````
109
 ````
103
 
110
 
104
 ---
111
 ---
105
 
112
 
106
-![Resample](doc/resample_pulse_times.png)
107
-
108
 Analyze all takes for given pitch and show the mean us/db curve and
113
 Analyze all takes for given pitch and show the mean us/db curve and
109
 places where resampling may be necessary.
114
 places where resampling may be necessary.
110
 
115
 
116
+![Resample](doc/resample_pulse_times.png)
117
+
111
 ````
118
 ````
112
 python plot_seq.py p_ac.yml ~/temp/p_ac_3_of resample_pulse_times 84
119
 python plot_seq.py p_ac.yml ~/temp/p_ac_3_of resample_pulse_times 84
113
 ````
120
 ````
115
 
122
 
116
 ---
123
 ---
117
 
124
 
118
-![Min Max](doc/min_max_db.png)
119
-
120
 Plot the min and max decibel values for specified pitches.
125
 Plot the min and max decibel values for specified pitches.
121
 
126
 
127
+![Min Max](doc/min_max_db.png)
128
+
122
 ````
129
 ````
123
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od min_max 36 48 60 72 84
130
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od min_max 36 48 60 72 84
124
 ````
131
 ````
125
 
132
 
126
 ---
133
 ---
127
 
134
 
128
-![Min Max 2](doc/min_max_db_2.png)
129
-
130
 Plot the min and max decibel values for specified pitches.
135
 Plot the min and max decibel values for specified pitches.
131
 
136
 
137
+![Min Max 2](doc/min_max_db_2.png)
138
+
132
 ````
139
 ````
133
 #                                                        pitch0 pitch1 pitch2 pitch3 pitch4 takeId
140
 #                                                        pitch0 pitch1 pitch2 pitch3 pitch4 takeId
134
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od min_max_2   36     48      60     72     84     2
141
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od min_max_2   36     48      60     72     84     2
135
 ````
142
 ````
136
 
143
 
137
 ---
144
 ---
138
-![Manual dB](doc/manual_db.png)
139
 
145
 
140
 Plot the min and max decibel values for specified set of manually corrected pitches.
146
 Plot the min and max decibel values for specified set of manually corrected pitches.
141
 
147
 
148
+![Manual dB](doc/manual_db.png)
142
 
149
 
143
 ````
150
 ````
144
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od manual_db
151
 python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od manual_db

+ 148
- 0
VelTablePlayer.py Näytä tiedosto

1
+import json
2
+from rt_note_analysis import RT_Analyzer
3
+
4
+class VelTablePlayer:
5
+    
6
+    def __init__( self, cfg, api, audio, holdDutyPctD, fn  ):
7
+        self.cfg          = cfg
8
+        self.api          = api
9
+        self.audio        = audio
10
+        self.rtAnalyzer   = RT_Analyzer()
11
+        self.holdDutyPctD = holdDutyPctD
12
+        self.durMs        = 500
13
+        self.mode         = "across"
14
+        self.state        = "off"
15
+        self.minPitch     = 21
16
+        self.maxPitch     = 108
17
+        self.velMapD      = {}
18
+
19
+        self.curMaxPitch  = self.maxPitch
20
+        self.curMinPitch  = self.minPitch
21
+        self.curPitch     = 21
22
+        self.curVelocity  = 0
23
+        self.curEndMs     = 0
24
+        self.curBegNoteMs = 0
25
+        self.curEndNoteMs = 0
26
+        
27
+        with open(fn,"r") as f:
28
+            d = json.load(f)
29
+
30
+            for pitch,value in d.items():
31
+                self.velMapD[ int(pitch) ] = [ int(x[0]) for x in d[pitch] ]
32
+
33
+        assert self.minPitch in self.velMapD
34
+        assert self.maxPitch in self.velMapD
35
+
36
+        
37
+    def start( self, minPitch, maxPitch, mode  ):
38
+        self.curMaxPitch = maxPitch
39
+        self.curMinPitch = minPitch
40
+        self.curPitch    = minPitch
41
+        self.curVelocity = 0
42
+        self.state       = "note_on"
43
+        self.mode        = mode 
44
+        self.audio.record_enable(True)   # start recording audio
45
+           
46
+    def stop( self ):
47
+        self.curPitch = self.minPitch
48
+        self._all_notes_off()
49
+        self.audio.record_enable(False)
50
+
51
+    def tick( self, ms ):
52
+        if self.state == "off":
53
+            pass
54
+        
55
+        elif self.state == "note_on":
56
+            self.state = self._note_on(ms)
57
+            
58
+        elif self.state == "playing":
59
+            if ms >= self.curEndMs:
60
+                self.state = "note_off"
61
+        
62
+        elif self.state == "note_off":
63
+            self.state = self._note_off(ms)
64
+
65
+
66
+    def _get_duty_cycle( self, pitch, usec ):
67
+        usDutyL = self.holdDutyPctD[pitch]
68
+
69
+        for i in range(len(usDutyL)):
70
+            if usDutyL[i][0] >= usec:
71
+                return usDutyL[i][1]
72
+
73
+        return usDutyL[-1][1]
74
+
75
+    def _calc_next_pitch( self ):
76
+
77
+        self.curPitch += 1
78
+        while self.curPitch not in self.velMapD and self.curPitch <= self.curMaxPitch:
79
+            self.curPitch+1
80
+
81
+        return self.curPitch <= self.curMaxPitch
82
+    
83
+    def _get_next_note_params( self ):
84
+
85
+        usec    = None
86
+        dutyPct = None
87
+        doneFl  = False
88
+
89
+        if self.mode == "updown":
90
+            if self.curVelocity + 1 < len(self.velMapD[ self.curPitch ]):
91
+                self.curVelocity += 1
92
+            else:
93
+
94
+                if self._calc_next_pitch():
95
+                    self.curVelocity = 0
96
+                else:
97
+                    doneFl = True
98
+
99
+        else:
100
+            
101
+            if self._calc_next_pitch():
102
+                self.curPitch += 1
103
+            else:
104
+                if self.curVelocity + 1 < len(self.velMapD[ self.curPitch ]):
105
+                    self.curVelocity += 1
106
+                    self.curPitch = self.curMinPitch
107
+                else:
108
+                    doneFl = True
109
+
110
+        if doneFl:
111
+            self.audio.record_enable(False)
112
+        else:
113
+            usec    = self.velMapD[self.curPitch][self.curVelocity]
114
+        
115
+            dutyPct = self._get_duty_cycle( self.curPitch, usec )
116
+            
117
+
118
+        return self.curPitch, usec, dutyPct
119
+            
120
+    def _note_on( self, ms ):
121
+
122
+        pitch,usec,dutyPct = self._get_next_note_params()
123
+
124
+        if not usec:
125
+            return "off"
126
+        else:
127
+            print(self.curPitch,self.curVelocity,usec,dutyPct)
128
+            self.curBegNoteMs = self.audio.buffer_sample_ms().value
129
+            self.api.set_pwm_duty( pitch, dutyPct )
130
+            self.api.note_on_us( pitch, usec )
131
+            self.curEndMs = ms + self.durMs
132
+            return "playing"
133
+        
134
+    def _note_off( self, ms ):
135
+
136
+        self.curEndNoteMs = self.audio.buffer_sample_ms().value
137
+        self.rtAnalyzer.analyze_note( self.audio, self.curPitch, self.curBegNoteMs, self.curEndNoteMs, self.cfg.analysisArgs['rmsAnalysisArgs'] )
138
+        self.api.note_off( self.curPitch )
139
+        return "note_on"
140
+
141
+
142
+    def _all_notes_off( self ):
143
+        if self.curPitch == 109:
144
+            self.state = 'off'
145
+            print('done')
146
+        else:
147
+            self.api.note_off( self.curPitch )
148
+            self.curPitch += 1

+ 2
- 2
elbow.py Näytä tiedosto

29
 
29
 
30
     return dbV
30
     return dbV
31
 
31
 
32
-def find_elbow( usL, dbL, pointsPerLine=10 ):
32
+def find_elbow( usL, dbL, pointsPerLine=5 ):
33
     
33
     
34
     ppl_2         = int(pointsPerLine/2)
34
     ppl_2         = int(pointsPerLine/2)
35
     dL = []
35
     dL = []
58
         i += 1
58
         i += 1
59
 
59
 
60
     # find the max angle
60
     # find the max angle
61
-    i = np.argmax( dL )    
61
+    i = np.argmax( dL )
62
 
62
 
63
     # return the x,y coordinate of the first data point of the second line
63
     # return the x,y coordinate of the first data point of the second line
64
     return (usL[i+ppl_2],dbL[i+ppl_2])
64
     return (usL[i+ppl_2],dbL[i+ppl_2])

+ 138
- 33
p_ac.py Näytä tiedosto

19
 from calibrate        import Calibrate
19
 from calibrate        import Calibrate
20
 from rms_analysis import rms_analyze_one_rt_note_wrap
20
 from rms_analysis import rms_analyze_one_rt_note_wrap
21
 from MidiFilePlayer import MidiFilePlayer
21
 from MidiFilePlayer import MidiFilePlayer
22
+from VelTablePlayer import VelTablePlayer
23
+from NoteTester     import NoteTester
24
+from PolyNoteTester import PolyNoteTester
25
+from ChordTester    import ChordTester
22
 
26
 
23
 class AttackPulseSeq:
27
 class AttackPulseSeq:
24
     """ Sequence a fixed pitch over a list of attack pulse lengths."""
28
     """ Sequence a fixed pitch over a list of attack pulse lengths."""
33
         self.noteDurMs   = noteDurMs      # duration of each chord in milliseconds
37
         self.noteDurMs   = noteDurMs      # duration of each chord in milliseconds
34
         self.pauseDurMs  = pauseDurMs     # duration between end of previous note and start of next
38
         self.pauseDurMs  = pauseDurMs     # duration between end of previous note and start of next
35
         self.holdDutyPctL= None           # hold voltage duty cycle table [ (minPulseSeqUsec,dutyCyclePct) ]
39
         self.holdDutyPctL= None           # hold voltage duty cycle table [ (minPulseSeqUsec,dutyCyclePct) ]
36
-        self.holdDutyPctD= None          # { us:dutyPct } for each us in self.pulseUsL
40
+        self.holdDutyPctD= None           # { us:dutyPct } for each us in self.pulseUsL
37
         self.silentNoteN = None 
41
         self.silentNoteN = None 
38
         self.pulse_idx            = 0     # Index of next pulse 
42
         self.pulse_idx            = 0     # Index of next pulse 
39
         self.state                = None  # 'note_on','note_off'
43
         self.state                = None  # 'note_on','note_off'
45
         self.rtAnalyzer           = RT_Analyzer()
49
         self.rtAnalyzer           = RT_Analyzer()
46
 
50
 
47
     def start( self, ms, outDir, pitch, pulseUsL, holdDutyPctL, holdDutyPctD, playOnlyFl=False ):
51
     def start( self, ms, outDir, pitch, pulseUsL, holdDutyPctL, holdDutyPctD, playOnlyFl=False ):
48
-        self.outDir     = outDir         # directory to write audio file and results
49
-        self.pitch     = pitch           # note to play
50
-        self.pulseUsL   = pulseUsL       # one onset pulse length in microseconds per sequence element
51
-        self.holdDutyPctL = holdDutyPctL
52
-        self.holdDutyPctD = holdDutyPctD
53
-        self.silentNoteN = 0 
54
-        self.pulse_idx  = 0
55
-        self.state      = 'note_on'
52
+        self.outDir          = outDir         # directory to write audio file and results
53
+        self.pitch           = pitch          # note to play
54
+        self.pulseUsL        = pulseUsL       # one onset pulse length in microseconds per sequence element
55
+        self.holdDutyPctL    = holdDutyPctL
56
+        self.holdDutyPctD    = holdDutyPctD
57
+        self.silentNoteN     = 0 
58
+        self.pulse_idx       = 0
59
+        self.state           = 'note_on'
56
         self.prevHoldDutyPct = None
60
         self.prevHoldDutyPct = None
57
-        self.next_ms    = ms + 500       # wait for 500ms to play the first note (this will guarantee that there is some empty space in the audio file before the first note)
58
-        self.eventTimeL = [[0,0]  for _ in range(len(pulseUsL))] # initialize the event time         
59
-        self.beginMs    = ms
60
-        self.playOnlyFl = playOnlyFl
61
+        self.next_ms         = ms + 500       # wait for 500ms to play the first note (this will guarantee that there is some empty space in the audio file before the first note)
62
+        self.eventTimeL      = [[0,0]  for _ in range(len(pulseUsL))] # initialize the event time         
63
+        self.beginMs         = ms
64
+        self.playOnlyFl      = playOnlyFl
61
                     
65
                     
62
         # kpl if not playOnlyFl:
66
         # kpl if not playOnlyFl:
63
         self.audio.record_enable(True)   # start recording audio
67
         self.audio.record_enable(True)   # start recording audio
64
 
68
 
65
-        print("Hold Delay from end of attack:",cfg.defaultHoldDelayUsecs)                    
66
-        self.api.set_hold_delay(pitch,cfg.defaultHoldDelayUsecs)
69
+        #print("Hold Delay from end of attack:",cfg.defaultHoldDelayUsecs)                    
70
+        # self.api.set_hold_delay(pitch,cfg.defaultHoldDelayUsecs)
71
+
72
+        self.api.set_hold_duty(pitch,35)
73
+        print("Hold Duty Cycle=35.")
67
         
74
         
68
         self.tick(ms)                    # play the first note
75
         self.tick(ms)                    # play the first note
69
         
76
         
128
             
135
             
129
     def _set_duty_cycle( self, pitch, pulseUsec ):
136
     def _set_duty_cycle( self, pitch, pulseUsec ):
130
 
137
 
131
-        
132
-        dutyPct = self._get_duty_cycle( pulseUsec )
138
+        if False:
139
+            dutyPct = self._get_duty_cycle( pulseUsec )
133
 
140
 
134
-        if dutyPct != self.prevHoldDutyPct:
135
-            self.api.set_pwm_duty( pitch, dutyPct )
136
-            print("Hold Duty:",dutyPct)
137
-            
138
-        self.prevHoldDutyPct = dutyPct
139
-    
141
+            if dutyPct != self.prevHoldDutyPct:
142
+                # self.api.set_pwm_duty( pitch, dutyPct )
143
+                # print("Hold Duty:",dutyPct)
144
+                pass
145
+
146
+            self.prevHoldDutyPct = dutyPct
147
+
148
+        if False:
149
+            maxLevel   =  64 # 225  # 64
150
+            minLevel   =  24 #  89 #  24
151
+            maxUsec    = 10000
152
+            minUsec    =  2500
153
+            decayLevel = maxLevel
154
+
155
+            if pulseUsec < maxUsec and pulseUsec >= minUsec:
156
+                decayLevel = minLevel + ((pulseUsec-minUsec)/(maxUsec-minUsec)) * (maxLevel-minLevel)
157
+            else:
158
+                if pulseUsec < minUsec:
159
+                    decayLevel = minLevel
160
+
161
+            decayLevel = int(decayLevel)
162
+
163
+            decayLevel = self.api.calc_decay_level( pulseUsec )
164
+            self.api.set_decay_level( pitch, decayLevel )
165
+            print("Decay Level:", decayLevel)
166
+
167
+        
140
     def _note_on( self, ms ):
168
     def _note_on( self, ms ):
141
 
169
 
142
         self.eventTimeL[ self.pulse_idx ][0] = self.audio.buffer_sample_ms().value
170
         self.eventTimeL[ self.pulse_idx ][0] = self.audio.buffer_sample_ms().value
144
         self.state = 'note_off'
172
         self.state = 'note_off'
145
 
173
 
146
         pulse_usec = int(self.pulseUsL[ self.pulse_idx ])
174
         pulse_usec = int(self.pulseUsL[ self.pulse_idx ])
147
-        self._set_duty_cycle( self.pitch, pulse_usec )
175
+        
176
+        # decay_level = self.api.calc_decay_level( pulse_usec )
177
+
178
+        #self._set_duty_cycle( self.pitch, pulse_usec )
179
+        
148
         self.api.note_on_us( self.pitch, pulse_usec )
180
         self.api.note_on_us( self.pitch, pulse_usec )
149
-        print("note-on:",self.pitch, self.pulse_idx, pulse_usec)
181
+        print("note-on:",self.pitch, self.pulse_idx, pulse_usec, "dcy:", decay_level)
150
 
182
 
151
     def _note_off( self, ms ):
183
     def _note_off( self, ms ):
152
         self.eventTimeL[ self.pulse_idx ][1] = self.audio.buffer_sample_ms().value
184
         self.eventTimeL[ self.pulse_idx ][1] = self.audio.buffer_sample_ms().value
209
             self.pulseUsL  = pulseUsL
241
             self.pulseUsL  = pulseUsL
210
             self.pitchL   = pitchL
242
             self.pitchL   = pitchL
211
             self.pitch_idx = -1
243
             self.pitch_idx = -1
212
-            self._start_next_note( ms, playOnlyFl )
244
+            self._start_next_seq( ms, playOnlyFl )
213
         
245
         
214
         
246
         
215
     def stop( self, ms ):
247
     def stop( self, ms ):
225
 
257
 
226
             # if the sequencer is done playing 
258
             # if the sequencer is done playing 
227
             if not self.seq.is_enabled():
259
             if not self.seq.is_enabled():
228
-                self._start_next_note( ms, self.seq.playOnlyFl ) # ... else start the next sequence
260
+                self._start_next_seq( ms, self.seq.playOnlyFl ) # ... else start the next sequence
229
 
261
 
230
         return None
262
         return None
231
 
263
 
232
-    def _start_next_note( self, ms, playOnlyFl ):
264
+    def _start_next_seq( self, ms, playOnlyFl ):
233
         
265
         
234
         self.pitch_idx += 1
266
         self.pitch_idx += 1
235
 
267
 
253
             # get the next available output directory id
285
             # get the next available output directory id
254
             outDir_id = self._calc_next_out_dir_id( outDir )
286
             outDir_id = self._calc_next_out_dir_id( outDir )
255
 
287
 
256
-            print(outDir_id,outDir)
257
-            
258
             # if this is not the first time this note has been sampled then get the resample locations
288
             # if this is not the first time this note has been sampled then get the resample locations
259
             if (outDir_id == 0) or self.cfg.useFullPulseListFl:
289
             if (outDir_id == 0) or self.cfg.useFullPulseListFl:
260
                 self.pulseUsL = self.cfg.full_pulseL
290
                 self.pulseUsL = self.cfg.full_pulseL
265
 
295
 
266
             holdDutyPctL = self.cfg.calibrateArgs['holdDutyPctD'][pitch]
296
             holdDutyPctL = self.cfg.calibrateArgs['holdDutyPctD'][pitch]
267
             
297
             
268
-            
298
+            # if playback of the current calibration was requested
269
             if playOnlyFl:
299
             if playOnlyFl:
270
 
300
 
301
+                # form the calibrated pulse list 
271
                 self.pulseUsL,_,holdDutyPctL = form_final_pulse_list( baseDir,  pitch,  self.cfg.analysisArgs, take_id=None )
302
                 self.pulseUsL,_,holdDutyPctL = form_final_pulse_list( baseDir,  pitch,  self.cfg.analysisArgs, take_id=None )
272
 
303
 
273
                 noteN = cfg.analysisArgs['auditionNoteN']
304
                 noteN = cfg.analysisArgs['auditionNoteN']
318
         self.keyboard       = None
349
         self.keyboard       = None
319
         self.calibrate      = None
350
         self.calibrate      = None
320
         self.midiFilePlayer = None
351
         self.midiFilePlayer = None
352
+        self.velTablePlayer = None
353
+        self.noteTester     = None
354
+        self.polyNoteTester = None
355
+        self.chordTester    = None
321
         
356
         
322
     def setup( self, cfg ):
357
     def setup( self, cfg ):
323
         self.cfg = cfg
358
         self.cfg = cfg
369
 
404
 
370
                 self.midiFilePlayer = MidiFilePlayer( cfg, self.api, self.midiDev, cfg.midiFileFn )
405
                 self.midiFilePlayer = MidiFilePlayer( cfg, self.api, self.midiDev, cfg.midiFileFn )
371
 
406
 
407
+                self.velTablePlayer = VelTablePlayer( cfg, self.api, self.audioDev, self.cfg.calibrateArgs['holdDutyPctD'], "velMapD.json")
408
+
409
+                self.noteTester = NoteTester(cfg,self.api)
410
+
411
+                self.polyNoteTester = PolyNoteTester(cfg,self.api)
412
+
413
+                self.chordTester    = ChordTester(cfg,self.api)
414
+
415
+
372
         return res
416
         return res
373
 
417
 
374
     def tick( self, ms ):
418
     def tick( self, ms ):
388
         if self.midiFilePlayer:
432
         if self.midiFilePlayer:
389
             self.midiFilePlayer.tick(ms)
433
             self.midiFilePlayer.tick(ms)
390
 
434
 
435
+        if self.midiDev:
436
+            msgL = self.midiDev.get_input()
437
+            for msg in msgL:
438
+                print(msg['value']);
439
+
440
+        if self.velTablePlayer:
441
+            self.velTablePlayer.tick(ms)
442
+
443
+        if self.noteTester:
444
+            self.noteTester.tick(ms)
445
+
446
+        if self.polyNoteTester:
447
+            self.polyNoteTester.tick(ms)
448
+                
449
+        if self.chordTester:
450
+            self.chordTester.tick(ms)
451
+            
391
     def audio_dev_list( self, ms ):
452
     def audio_dev_list( self, ms ):
392
 
453
 
393
         if self.audioDev is not None:
454
         if self.audioDev is not None:
394
             portL = self.audioDev.get_port_list( True )
455
             portL = self.audioDev.get_port_list( True )
395
         
456
         
396
             for port in portL:
457
             for port in portL:
397
-                print("chs:%4i label:%s" % (port['chN'],port['label']))
458
+                print("chs:%4i label:'%s'" % (port['chN'],port['label']))
398
         
459
         
399
     def midi_dev_list( self, ms ):
460
     def midi_dev_list( self, ms ):
400
         d = self.midiDev.get_port_list( True )
461
         d = self.midiDev.get_port_list( True )
447
     def midi_file_player_stop( self, ms ):
508
     def midi_file_player_stop( self, ms ):
448
         self.midiFilePlayer.stop(ms)
509
         self.midiFilePlayer.stop(ms)
449
 
510
 
511
+    def vel_table_updown( self, ms, argL):
512
+        self.velTablePlayer.start(argL[0],argL[1],"updown")
513
+
514
+    def vel_table_across( self, ms, argL ):
515
+        self.velTablePlayer.start(argL[0],argL[1],"across")
516
+
517
+    def vel_table_stop( self, ms ):
518
+        self.velTablePlayer.stop()
519
+
520
+    def note_tester_start( self, ms ):
521
+        self.noteTester.start();
522
+
523
+    def note_tester_stop( self, ms ):
524
+        self.noteTester.stop();
525
+
526
+    def poly_note_tester_start( self, ms ):
527
+        self.polyNoteTester.start();
528
+
529
+    def poly_note_tester_stop( self, ms ):
530
+        self.polyNoteTester.stop();
531
+
532
+    def chord_tester_start( self, ms ):
533
+        self.chordTester.start()
534
+
535
+    def chord_tester_stop( self, ms ):
536
+        self.chordTester.stop()
537
+        
450
     def pedal_down( self, ms ):
538
     def pedal_down( self, ms ):
451
         print("pedal_down")
539
         print("pedal_down")
452
         self.midiDev.send_controller(64, 100 )
540
         self.midiDev.send_controller(64, 100 )
454
     def pedal_up( self, ms ):
542
     def pedal_up( self, ms ):
455
         print("pedal_up");
543
         print("pedal_up");
456
         self.midiDev.send_controller(64, 0 )
544
         self.midiDev.send_controller(64, 0 )
545
+
546
+    def all_notes_off(self, ms):
547
+        self.api.all_notes_off();
548
+
549
+    def check_for_errors(self,ms):
550
+        self.api.check_for_serial_errors()
457
         
551
         
458
     def quit( self, ms ):
552
     def quit( self, ms ):
459
         if self.api:
553
         if self.api:
587
             'R':{ "func":"keyboard_repeat_target_db", "minN":1,  "maxN":1, "help":"Repeat db across keyboard with new pulse_idx"},
681
             'R':{ "func":"keyboard_repeat_target_db", "minN":1,  "maxN":1, "help":"Repeat db across keyboard with new pulse_idx"},
588
             'F':{ "func":"midi_file_player_start",    "minN":0,  "maxN":0, "help":"Play the MIDI file."},
682
             'F':{ "func":"midi_file_player_start",    "minN":0,  "maxN":0, "help":"Play the MIDI file."},
589
             'f':{ "func":"midi_file_player_stop",     "minN":0,  "maxN":0, "help":"Stop the MIDI file."},
683
             'f':{ "func":"midi_file_player_stop",     "minN":0,  "maxN":0, "help":"Stop the MIDI file."},
684
+            'V':{ "func":"vel_table_updown",          "minN":2,  "maxN":2, "help":"Play Velocity Table up/down - across."},
685
+            'A':{ "func":"vel_table_across",          "minN":2,  "maxN":2, "help":"Play Velocity Table across - up/down."},
686
+            'v':{ "func":"vel_table_stop",            "minN":0,  "maxN":0, "help":"Stop the velocity table playback."},
687
+            'N':{ "func":"note_tester_start",         "minN":0,  "maxN":0, "help":"Play a note using NoteTester."},
688
+            'n':{ "func":"note_tester_stop",          "minN":0,  "maxN":0, "help":"Stop NoteTester."},
689
+            'T':{ "func":"poly_note_tester_start",    "minN":0,  "maxN":0, "help":"Play random notes using PolyNoteTester."},
690
+            't':{ "func":"poly_note_tester_stop",     "minN":0,  "maxN":0, "help":"Stop NoteTester."},
691
+            'H':{ "func":"chord_tester_start",        "minN":0,  "maxN":0, "help":"Chord tester start."},
692
+            'h':{ "func":"chord_tester_stop",         "minN":0,  "maxN":0, "help":"Chord tester stop."},
590
             'P':{ "func":"pedal_down",                "minN":0,  "maxN":0, "help":"Pedal down."},
693
             'P':{ "func":"pedal_down",                "minN":0,  "maxN":0, "help":"Pedal down."},
591
-            'U':{ "func":"pedal_up",                  "minN":0,  "maxN":0, "help":"Pedal up."},
694
+            'p':{ "func":"pedal_up",                  "minN":0,  "maxN":0, "help":"Pedal up."},
695
+            'O':{ "func":"all_notes_off",             "minN":0,  "maxN":0, "help":"All notes off."},
696
+            'E':{ "func":"check_for_errors",          "minN":0,  "maxN":0, "help":"Check for errors."}
592
             }
697
             }
593
 
698
 
594
     def _help( self, _=None ):
699
     def _help( self, _=None ):

+ 261
- 98
p_ac.yml Näytä tiedosto

5
     # Audio device setup
5
     # Audio device setup
6
     audio: {
6
     audio: {
7
       inPortLabel: "8 USB Audio CODEC:", #"HDA Intel PCH: CS4208", # "5 USB Audio CODEC:", #"5 USB Sound Device",
7
       inPortLabel: "8 USB Audio CODEC:", #"HDA Intel PCH: CS4208", # "5 USB Audio CODEC:", #"5 USB Sound Device",
8
+      #inPortLabel: "8 Scarlett 18i20 USB: Audio",
8
       outPortLabel: ,
9
       outPortLabel: ,
9
     },
10
     },
10
 
11
 
12
         inMonitorFl: False,
13
         inMonitorFl: False,
13
         outMonitorFl: False,
14
         outMonitorFl: False,
14
         throughFl: False,
15
         throughFl: False,
15
-        inPortLabel: "Fastlane:Fastlane MIDI A",
16
-        outPortLabel: "Fastlane:Fastlane MIDI A"
16
+        #inPortLabel: "MIDI9/QRS PNOScan:MIDI9/QRS PNOScan MIDI 1",
17
+        #outPortLabel: "MIDI9/QRS PNOScan:MIDI9/QRS PNOScan MIDI 1"
18
+        #inPortLabel: "Fastlane:Fastlane MIDI A",
19
+        #outPortLabel: "Fastlane:Fastlane MIDI A",
17
         #inPortLabel: "picadae:picadae MIDI 1",
20
         #inPortLabel: "picadae:picadae MIDI 1",
18
-        #outPortLabel: "picadae:picadae MIDI 1"
21
+        #outPortLabel: "picadae:picadae MIDI 1"        
22
+    },
23
+
24
+    NoteTester:
25
+    {
26
+      noteCount: 200,
27
+      minNoteDurMs: 100,
28
+      maxNoteDurMs: 1000,
29
+      minPauseDurMs: 5,
30
+      maxPauseDurMs: 500,
31
+      pitch: 60,
32
+      minAttackUsec: 4000,
33
+      maxAttackUsec: 20000,
34
+      filename: "note_tester.json"
35
+    },
36
+
37
+    PolyNoteTester:
38
+    {
39
+      mode: "simple",
40
+      noteCount: 11,
41
+      minPitch: 21,
42
+      maxPitch: 31,
43
+      skipPitchL: [22,68,102],
44
+      minNoteDurMs: 1000,
45
+      maxNoteDurMs: 2000,
46
+      minInterOnsetMs: 100,
47
+      maxInterOnsetMs: 1000,
48
+      minAttackUsec: 4000,
49
+      maxAttackUsec: 20000,
50
+      holdDutyPct: 35,
51
+      
52
+    },
53
+
54
+    ChordTester:
55
+    {
56
+       useVelTableFl: False,
57
+       #pitchL: [24,36,48,60,72,84,96],
58
+       #pitchL: [24,96,48,72,36,84,60],
59
+       #pitchL: [ 36,40,43,47, 48, 52, 55, 59],
60
+       #pitchL:  [ 36,39,41,33 ],
61
+       pitchL:   [ 21,23,24,25,26,27,28,29,30,31],
62
+       #pitchL:  [ 36,33,35,34,37,40,43,42,38,39,41],
63
+       #pitchL:   [ 43,44,45,46,47,48,49,50,51,52,53],
64
+       #pitchL:  [ 54,55,56,57,58,59,60,61,62,63,64 ],
65
+       repeatCnt: 3,
66
+       atkUsec: 10000,
67
+       durMs: 1000,
68
+       pauseMs: 1000,
69
+       holdDuty: 35,
19
     },
70
     },
20
     
71
     
21
     # Picadae API args
72
     # Picadae API args
28
     
79
     
29
 
80
 
30
     # MeasureSeq args
81
     # MeasureSeq args
31
-    outDir: "~/temp/p_ac_3_of",
82
+    outDir: "~/temp/p_ac_3_ok",
32
     noteDurMs: 500,
83
     noteDurMs: 500,
33
     pauseDurMs: 500,
84
     pauseDurMs: 500,
34
     reversePulseListFl: True,
85
     reversePulseListFl: True,
35
-    useFullPulseListFl: True,
86
+    useFullPulseListFl: True,     # don't select a new set of resample points based on the previous samples from this pitch
36
     maxSilentNoteCount: 4,
87
     maxSilentNoteCount: 4,
37
     silentNoteMaxPulseUs: 15000,
88
     silentNoteMaxPulseUs: 15000,
38
     silentNoteMinDurMs: 180, #250,
89
     silentNoteMinDurMs: 180, #250,
40
     defaultHoldDelayUsecs: 1000,
91
     defaultHoldDelayUsecs: 1000,
41
 
92
 
42
     # Midi file player
93
     # Midi file player
43
-    midiFileFn: "/home/kevin/media/audio/midi/txt/round4.txt",
94
+    midiFileFn: "/home/kevin/media/audio/midi/txt/988-v25.txt",
44
     
95
     
45
     
96
     
46
 
97
 
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 ],
145
     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
 
146
 
96
     # 60,72,84    
147
     # 60,72,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, 11500, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000, 22000  ],
148
+    full_pulse23L: [  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, 11500, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000, 22000  ],
149
+
150
+    full_pulseL24: [  5000, 5125, 5250, 5375, 5500, 5625, 5750, 5875, 6000, 6125, 6250, 6500, 6750, 7000, 7500, 8000, 8500, 9000, 9500, 10000, 10500, 11000, 11500, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000, 22000  ],
151
+
152
+    # 23-40
153
+    full_pulseL25: [  3500, 3625, 3750, 3875, 4000, 4125, 4250, 4375, 4500, 4625, 4750, 4875, 5000, 5125, 5250, 5375, 5500, 5625, 5750, 5875, 6000, 6125, 6250, 6375, 6500, 6625, 6750, 6875, 7000, 7250, 7500, 7750, 8000, 8250, 8500, 9000, 9500, 10000, 10500, 11000, 11500, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000  ],
154
+
155
+    full_pulse25aL: [  2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, 2400, 2450, 2500, 2550, 2600, 2650, 2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050, 3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450, 3500, 3550, 3600, 3650, 3700, 3750, 3800, 3850, 3900, 3950, 4000, 4050, 4100, 4150, 4200, 4250, 4300, 4350, 4400, 4450, 4500, 4550, 4600, 4650, 4700, 4750, 4800, 4850, 4900, 4950, 5000, 5050, 5100, 5150, 5200, 5250, 5300, 5350, 5400, 5450, 5500, 5550, 5600, 5650, 5700, 5750, 5800, 5850, 5900, 5950, 6000, 6125, 6250, 6375, 6500, 6625, 6750, 6875, 7000, 7250, 7500, 7750, 8000, 8250, 8500, 9000, 9500, 10000, 10500, 11000, 11500, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000  ],
156
+
157
+    # 21-    
158
+    full_pulse26L: [  1500, 1625, 1750, 1875, 2000, 2125, 2250, 2375, 2500, 2625, 2750, 2875, 3000, 3125, 3250, 3375,3500, 3625, 3750, 3875, 4000, 4125, 4250, 4375, 4500, 4625, 4750, 4875, 5000, 5125, 5250, 5375, 5500, 5625, 5750, 5875, 6000, 6125, 6250, 6375, 6500, 6625, 6750, 6875, 7000, 7250, 7500, 7750, 8000, 8250, 8500, 9000, 9500, 10000, 10500, 11000, 11500, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000, 22000  ],
159
+
160
+    full_pulseL: [  1500, 1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100, 3200, 3300, 3400, 3500, 3625, 3750, 3875, 4000, 4125, 4250, 4375, 4500, 4625, 4750, 4875, 5000, 5125, 5250, 5375, 5500, 5625, 5750, 5875, 6000, 6125, 6250, 6375, 6500, 6625, 6750, 6875, 7000, 7250, 7500, 7750, 8000, 8250, 8500, 9000, 9500, 10000, 10500, 11000, 11500, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000, 22000  ],
161
+    
162
+    # 92
163
+    full_pulse27L: [  1500, 1625, 1750, 1875, 2000, 2125, 2250, 2375, 2500, 2625, 2750, 2875, 3000, 3125, 3250, 3375, 3500, 3625, 3750, 3875, 4000, 4125, 4250, 4375, 4500, 4625, 4750, 4875, 5000, 5125, 5250, 5375, 5500, 5625, 5750, 5875, 6000, 6125, 6250, 6375, 6500, 6625, 6750, 6875, 7000, 7250, 7500, 7750, 8000, 8250, 8500, 9000, 9500, 10000, 10500, 11000, 11500, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000  ],
164
+
165
+    full_pulse28L: [ 4000, 8000, 12000, 16000 ],
166
+
167
+    full_pulse29L: [ 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500, 7000, 7500, 8000, 8500, 9000, 9500, 10000, 10500, 11000, 11500, 12000, 12500, 13000, 13500, 14000, 14500, 15000, 15500, 16000, 16500, 17000, 17500, 18000, 18500, 19000, 19500, 20000 ],
98
 
168
 
99
     # RMS analysis args
169
     # RMS analysis args
100
     analysisArgs: {
170
     analysisArgs: {
112
       resampleMinDurMs: 150,         # notes's whose duration is less than this will be skipped
182
       resampleMinDurMs: 150,         # notes's whose duration is less than this will be skipped
113
 
183
 
114
       useLastTakeOnlyFl: True,
184
       useLastTakeOnlyFl: True,
115
-      minAttkDb: -5.0,   # threshold of silence level 
116
-      maxDbOffset: 0.25, # travel down the from the max. note level by at most this amount to locate the max. peak
117
-      maxDeltaDb: 1.5,  # maximum db change between volume samples (changes greater than this will trigger resampling)
118
-      samplesPerDb: 4,   # count of samples per dB to resample ranges whose range is less than maxDeltaDb
119
-      minSampleDistUs: 50, # minimum distance between sample points in microseconds
185
+      minAttkDb: -5.0,      # threshold of silence level 
186
+      maxDbOffset: 0.25,    # travel down the from the max. note level by at most this amount to locate the max. peak
187
+      maxDeltaDb: 1.5,      # maximum db change between volume samples (changes greater than this will trigger resampling)
188
+      samplesPerDb: 4,      # count of samples per dB to resample ranges whose range is less than maxDeltaDb
189
+      minSampleDistUs: 50,  # minimum distance between sample points in microseconds
120
       auditionNoteN: 19,    # count of notes to play for audition
190
       auditionNoteN: 19,    # count of notes to play for audition
121
 
191
 
122
       finalPulseListCacheFn: "/home/kevin/temp/final_pulse_list_cache.pickle",
192
       finalPulseListCacheFn: "/home/kevin/temp/final_pulse_list_cache.pickle",
123
       rmsAnalysisCacheFn: "/home/kevin/temp/rms_analysis_cache.pickle"
193
       rmsAnalysisCacheFn: "/home/kevin/temp/rms_analysis_cache.pickle"
124
       },
194
       },
125
 
195
 
196
+      # used by plot_seq_1.gen_vel_map()
197
+      velTableDbL: [ 2, 7, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 27, 29  ],
198
+
126
       manualMinD: {
199
       manualMinD: {
127
-        36: [2, 10],
128
-        48: [2, 10],
129
-        60: [2, 10],
130
-        72: [2, 10],
131
-        84: [2, 10]
132
-        },
200
+      21:[-1,6], 
201
+      23:[-1,1], # 10
202
+      24:[-1,11], # 11 
203
+      25:[-1,8], 
204
+      26:[-1,9], # 13 
205
+      27:[-1,11], 
206
+      28:[-1,2], 
207
+      29:[-1,16], 
208
+      30:[-1,4], 
209
+      31:[-1,8], 
210
+      32:[-1,9], 
211
+      33:[-1,10], 
212
+      34:[-1,9], # 15 
213
+      35:[-1,9], 
214
+      36:[-1,4], 
215
+      37:[-1,7], 
216
+      38:[-1,5], 
217
+      39:[-1,5], 
218
+      40:[-1,6],  # 2  lwr
219
+      41:[-1,10],  # 24 lwr
220
+      42:[-1,2], 
221
+      43:[-1,4], 
222
+      44:[-1,5], 
223
+      45:[-1,10], 
224
+      46:[-1,4], 
225
+      47:[-1,7], 
226
+      48:[-1,9], 
227
+      49:[-1,2],
228
+      50:[-1,5],
229
+      51:[-1,4], 
230
+      52:[-1,8], 
231
+      53:[-1,0], # 16
232
+      54:[-1,8], 
233
+      55:[-1,14], 
234
+      56:[-1,8], 
235
+      57:[-1,9], 
236
+      58:[-1,10], 
237
+      59:[-1,7], 
238
+      60:[-1,7], # 14
239
+      61:[-1,10], # 14
240
+      62:[-1,1], # 7
241
+      63:[-1,5], 
242
+      64:[-1,2], 
243
+      65:[-1,8], 
244
+      66:[-1,3], 
245
+      67:[-1,3], # 17 lwr
246
+      68:[-1,3], 
247
+      69:[-1,9], 
248
+      70:[-1,15], # 15 lwr 
249
+      71:[-1,0], # 7 lwr
250
+      72:[-1,3],# 12 lwr
251
+      73:[-1,0],# 21 lwr
252
+      74:[-1,0],# 24 lwr
253
+      75:[-1,0],# 18 lwr
254
+      76:[-1,0],# 13 lwr
255
+      77:[-1,0],# 22 lwr
256
+      78:[-1,0],# 14 lwr
257
+      79:[-1,8], 
258
+      80:[-1,3], 
259
+      81:[-1,2],  # 14 lwr 
260
+      82:[-1,5],  # 11
261
+      83:[-1,7], 
262
+      84:[-1,3], # 13
263
+      85:[-1,5], 
264
+      86:[-1,4], 
265
+      87:[-1,7], 
266
+      88:[-1,5], # 22
267
+      89:[-1,19], 
268
+      90:[-1,2], # 10
269
+      91:[-1,10], 
270
+      92:[-1,8], 
271
+      93:[-1,7], 
272
+      94:[-1,5], # 10
273
+      95:[-1,9], 
274
+      96:[-1,13], 
275
+      97:[-1,12], 
276
+      98:[-1,14], 
277
+      99:[-1,14], 
278
+      100:[-1,18], 
279
+      101:[-1,18], 
280
+      103:[-1,25], 
281
+      104:[-1,26], 
282
+      105:[-1,18], 
283
+      106:[-1,26], 
284
+      107:[-1,29], 
285
+      108:[-1,28], 
286
+      },
133
 
287
 
134
-      manualAnchorPitchMinDbL: [ 36,48,72,84 ],
135
-      manualAnchorPitchMaxDbL: [ 36,48,60,72,84 ],
136
-        
288
+      manualAnchorPitchMinDbL: [ 24, 36,48,60,72,84,96 ],
289
+      manualAnchorPitchMaxDbL: [ 24, 36,48,60,72,84,96 ],
290
+
291
+      manualLastFl: true,
137
         
292
         
138
       manualMinD_0: {
293
       manualMinD_0: {
139
         23: [2, 24],
294
         23: [2, 24],
256
           dbSrcLabel: 'hm',              # source of the db measurement 'td' (time-domain) or 'hm' (harmonic)
411
           dbSrcLabel: 'hm',              # source of the db measurement 'td' (time-domain) or 'hm' (harmonic)
257
 
412
 
258
           holdDutyPctD:  {
413
           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, 40]],
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, 45]],
296
-          60: [[0, 40],[10000,50]],
297
-          61: [[0, 40],[10000,50]],
298
-          62: [[0, 40],[10000,50]],
299
-          63: [[0, 40],[10000,50]],
300
-          64: [[0, 40],[10000,50]],
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, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
309
-          73: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
310
-          74: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
311
-          75: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
312
-          76: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
313
-          77: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
314
-          78: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
315
-          79: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
316
-          80: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
317
-          81: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
318
-          82: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
319
-          83: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
320
-          84: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,60] ],
321
-          85: [[0, 42],[8000,44],[9000,46],[10000, 48],[11000,50],[12000,52],[13000,54],[14000,56],[15000,58],[16000,60],[17000,60],[18000,60],[19000,60],[20000,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]],
414
+          21: [[0, 50]],
415
+          23: [[0, 50]],
416
+          24: [[0, 50]],
417
+          25: [[0, 50]],
418
+          26: [[0, 50]],
419
+          27: [[0, 50]],
420
+          28: [[0, 50]],
421
+          29: [[0, 50]],
422
+          30: [[0, 50]],
423
+          31: [[0, 50]],
424
+          32: [[0, 50]],
425
+          33: [[0, 50]],
426
+          34: [[0, 50]],
427
+          35: [[0, 50]],
428
+          36: [[0, 50]],
429
+          37: [[0, 50]],
430
+          38: [[0, 50]],
431
+          39: [[0, 50]],
432
+          40: [[0, 50]],
433
+          41: [[0, 45]],
434
+          42: [[0, 45]],
435
+          43: [[0, 45]],
436
+          44: [[0, 45]],
437
+          45: [[0, 45]],
438
+          46: [[0, 40],[10000,45]],
439
+          47: [[0, 40],[10000,45]],
440
+          48: [[0, 40],[10000,45]],
441
+          49: [[0, 40],[10000,45]],
442
+          50: [[0, 40],[10000,45]],
443
+          51: [[0, 40],[10000,45]],
444
+          52: [[0, 40],[10000,45]],
445
+          53: [[0, 40],[10000,45]],
446
+          54: [[0, 40],[10000,45]],
447
+          55: [[0, 40],[10000,45]],
448
+          56: [[0, 40],[10000,45]],
449
+          57: [[0, 40],[10000,45]],
450
+          58: [[0, 40],[10000,45]],
451
+          59: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],       
452
+          60: [[0, 40],[10000,43],[11000,45],[12000,45], [13000,50],[14000,55],[15000,60],[16000,65]],
453
+          61: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
454
+          62: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
455
+          63: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
456
+          64: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
457
+          65: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],              
458
+          66: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
459
+          67: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
460
+          68: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
461
+          69: [[0, 32],[5000,64] ],
462
+          70: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
463
+          71: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
464
+          72: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
465
+          73: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
466
+          74: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
467
+          75: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
468
+          76: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
469
+          77: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55],[15000,60]],
470
+          78: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55]],
471
+          79: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55]],
472
+          80: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55]],
473
+          81: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55]],
474
+          82: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55]],
475
+          83: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55]],
476
+          84: [[0, 35],[8000,40],[10000,45],[12000,48],[13000,50],[14000,55]],
477
+          85: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55]],
478
+          86: [[0, 40],[10000,45],[12000,48],[13000,50],[14000,55]],
479
+          87: [[0, 40],[10000,45],[12000,48],[13000,50]],
480
+          88: [[0, 40],[10000,45],[12000,48],[13000,50]],
481
+          89: [[0, 40],[10000,45],[12000,48],[13000,50]],
482
+          90: [[0, 40],[10000,45],[12000,48],[13000,50]],
483
+          91: [[0, 40],[10000,45],[12000,48],[13000,50]],
484
+          92: [[0, 37]],
485
+          93: [[0, 37]],
486
+          94: [[0, 37]],
487
+          95: [[0, 37]],
488
+          96: [[0, 37]],
332
           97: [[0, 40]],
489
           97: [[0, 40]],
333
           98: [[0, 40]],
490
           98: [[0, 40]],
334
-          99: [[0, 40]],
335
-          100: [[0, 40]],
336
-          101: [[0, 40]],
337
-          106: [[0, 40]]
491
+          99: [[0, 37]],
492
+          100: [[0, 37]],
493
+          101: [[0, 37]],
494
+          102: [[0, 37]],
495
+          103: [[0, 40],[10000,45],[15000,50]],
496
+          104: [[0, 40],[7500,45],[15000,50]],
497
+          105: [[0, 40],[10000,45],[15000,50]],
498
+          106: [[0, 37]],
499
+          107: [[0, 37]],
500
+          108: [[0, 37]],
338
           },
501
           },
339
 
502
 
340
           # Final for Matt's piano
503
           # Final for Matt's piano

+ 1
- 1
plot_all_note_durations.ipynb Näytä tiedosto

71
    "name": "python",
71
    "name": "python",
72
    "nbconvert_exporter": "python",
72
    "nbconvert_exporter": "python",
73
    "pygments_lexer": "ipython3",
73
    "pygments_lexer": "ipython3",
74
-   "version": "3.7.5"
74
+   "version": "3.8.6"
75
   }
75
   }
76
  },
76
  },
77
  "nbformat": 4,
77
  "nbformat": 4,

+ 1
- 1
plot_calibrate.ipynb Näytä tiedosto

67
    "name": "python",
67
    "name": "python",
68
    "nbconvert_exporter": "python",
68
    "nbconvert_exporter": "python",
69
    "pygments_lexer": "ipython3",
69
    "pygments_lexer": "ipython3",
70
-   "version": "3.7.5"
70
+   "version": "3.8.6"
71
   }
71
   }
72
  },
72
  },
73
  "nbformat": 4,
73
  "nbformat": 4,

+ 144
- 46
plot_seq_1.py Näytä tiedosto

1
 ##| Copyright: (C) 2019-2020 Kevin Larke <contact AT larke DOT org> 
1
 ##| Copyright: (C) 2019-2020 Kevin Larke <contact AT larke DOT org> 
2
 ##| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
2
 ##| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
3
-import os, sys,json
3
+import os, sys,json,math
4
 import matplotlib.pyplot as plt
4
 import matplotlib.pyplot as plt
5
 import numpy as np
5
 import numpy as np
6
 from common import parse_yaml_cfg
6
 from common import parse_yaml_cfg
458
 
458
 
459
         usL, dbL, durMsL, _, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'], takeId=takeId )
459
         usL, dbL, durMsL, _, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'], takeId=takeId )
460
 
460
 
461
+
461
         ax.plot(usL,dbL, marker='.',label="%i:%i %s %s" % (midi_pitch,takeId,keyMapD[midi_pitch]['class'],keyMapD[midi_pitch]['type']))
462
         ax.plot(usL,dbL, marker='.',label="%i:%i %s %s" % (midi_pitch,takeId,keyMapD[midi_pitch]['class'],keyMapD[midi_pitch]['type']))
462
 
463
 
463
-        # for i,(x,y) in enumerate(zip(usL,dbL)):
464
-        #    ax.text(x,y,str(i))
464
+        for i,(x,y) in enumerate(zip(usL,dbL)):
465
+            ax.text(x,y,str(i))
465
 
466
 
467
+        f_usL,f_dbL = filter_us_db(usL,dbL)
468
+            
469
+        ax.plot(f_usL,f_dbL, marker='.')
470
+            
471
+
472
+        elbow_us,elbow_db = elbow.find_elbow(usL,dbL)
473
+        ax.plot([elbow_us],[elbow_db],marker='*',markersize=12,color='red',linestyle='None')
474
+
475
+        elb_idx = nearest_sample_point( dbL, usL, elbow_db, elbow_us )
466
 
476
 
467
     if printDir:
477
     if printDir:
468
         plt.savefig(os.path.join(printDir,printFn),format="png")
478
         plt.savefig(os.path.join(printDir,printFn),format="png")
469
         
479
         
470
     plt.legend()
480
     plt.legend()
471
     plt.show()
481
     plt.show()
482
+
483
+def get_pitches_and_takes( inDir ):
484
+
485
+    pitchD = {}
486
+    inDirL = os.listdir( inDir )
487
+
488
+    for pitch in inDirL:
489
+        path    = os.path.join( inDir, pitch )
490
+        takeIdL = os.listdir( path )
491
+        
492
+        takeIdL = sorted([ int(takeId) for takeId in takeIdL ])
493
+        takeIdL = [ str(x) for x in takeIdL ] 
494
+        pitchD[int(pitch)] = takeIdL
495
+
496
+
497
+    return pitchD
472
     
498
     
473
 def plot_us_db_takes( inDir, cfg, pitchL, printDir=""):
499
 def plot_us_db_takes( inDir, cfg, pitchL, printDir=""):
474
 
500
 
480
 
506
 
481
 def plot_us_db_takes_last( inDir, cfg, pitchL, printDir ):
507
 def plot_us_db_takes_last( inDir, cfg, pitchL, printDir ):
482
 
508
 
509
+    pitchD = get_pitches_and_takes(inDir)
510
+    
483
     takeIdL = []
511
     takeIdL = []
484
     for pitch in pitchL:
512
     for pitch in pitchL:
485
-
486
-        inDirL = os.listdir( os.path.join(inDir,str(pitch)))
487
-
488
-        inDirL = sorted(inDirL)
489
-        
490
-        takeIdL.append( int(inDirL[-1]) )
513
+        takeIdL.append( int(pitchD[pitch][-1]) )
491
 
514
 
492
     return _plot_us_db_takes( inDir, cfg, pitchL, takeIdL, printDir, "us_db_takes_last.png")
515
     return _plot_us_db_takes( inDir, cfg, pitchL, takeIdL, printDir, "us_db_takes_last.png")
493
     
516
     
535
     plt.legend()
558
     plt.legend()
536
     plt.show()
559
     plt.show()
537
 
560
 
538
-def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2, printDir=None ):
561
+def nearest_sample_point( dbL, usL, db0, us0 ):
562
+    xL = np.array([ abs(us-us0)  for db,us in zip(dbL,usL) ])
539
 
563
 
540
-    pitchFolderL = os.listdir(inDir)
564
+    return np.argmin(xL)
541
 
565
 
542
-    print(pitchL)
566
+    
567
+def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2, printDir=None ):
543
 
568
 
569
+    takeIdArg  = takeId
570
+    pitchTakeD = get_pitches_and_takes(inDir)
571
+    
572
+    pitchFolderL = os.listdir(inDir)
544
     if pitchL is None:
573
     if pitchL is None:
545
         pitchL = [ int( int(pitchFolder) ) for pitchFolder in pitchFolderL ]
574
         pitchL = [ int( int(pitchFolder) ) for pitchFolder in pitchFolderL ]
546
 
575
 
547
-    print(pitchL)
548
-        
549
     okL       = []
576
     okL       = []
550
     outPitchL = []
577
     outPitchL = []
551
     minDbL    = []
578
     minDbL    = []
553
     
580
     
554
     for midi_pitch in pitchL:
581
     for midi_pitch in pitchL:
555
 
582
 
556
-        print(midi_pitch)
583
+        takeId = None
584
+        if takeIdArg == -1:
585
+            takeId = pitchTakeD[midi_pitch][-1]
557
 
586
 
558
-        usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
587
+        usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'], takeId )
559
 
588
 
560
         okL.append(False)
589
         okL.append(False)
561
 
590
 
571
         minDbL.append(elbow_db)
600
         minDbL.append(elbow_db)
572
         outPitchL.append(midi_pitch)
601
         outPitchL.append(midi_pitch)
573
 
602
 
603
+        smp_idx = nearest_sample_point( dbL, usL, elbow_db, elbow_us )
604
+
605
+        print(" %i:[-1,%i], " % (midi_pitch,smp_idx))
574
 
606
 
575
 
607
 
576
     p_dL = sorted( zip(outPitchL,minDbL,maxDbL,okL), key=lambda x: x[0] )
608
     p_dL = sorted( zip(outPitchL,minDbL,maxDbL,okL), key=lambda x: x[0] )
593
        
625
        
594
     plt.show()
626
     plt.show()
595
 
627
 
596
-def plot_min_db_manual( inDir, cfg, printDir=None ):
628
+def plot_min_db_manual( inDir, cfg, printDir=None, absMaxDb=27, absMinDb=3 ):
629
+
630
+    pitchTakeD = get_pitches_and_takes(inDir)
597
     
631
     
598
     pitchL     = list(cfg.manualMinD.keys())
632
     pitchL     = list(cfg.manualMinD.keys())
599
     
633
     
606
 
640
 
607
     for midi_pitch in pitchL:
641
     for midi_pitch in pitchL:
608
 
642
 
609
-        manual_take_id    = cfg.manualMinD[midi_pitch][0]
643
+        if cfg.manualLastFl:
644
+            manual_take_id = pitchTakeD[midi_pitch][-1]
645
+            takeId         = manual_take_id
646
+        else:
647
+            manual_take_id = cfg.manualMinD[midi_pitch][0]
648
+            takeId         = None
649
+        
610
         manual_sample_idx = cfg.manualMinD[midi_pitch][1]
650
         manual_sample_idx = cfg.manualMinD[midi_pitch][1]
611
         
651
         
612
-        usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
652
+        usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'], takeId )
613
 
653
 
614
         okL.append(False)
654
         okL.append(False)
615
 
655
 
616
-        takeId = len(set(takeIdL))-1
656
+        if takeId is None:
657
+            takeId = len(set(takeIdL))-1
617
 
658
 
659
+            # most pitches have 3 sample takes that do not
660
+            if len(set(takeIdL)) == 3 and manual_take_id == takeId:
661
+                okL[-1] = True
662
+        else:
663
+            okL[-1] = True
664
+                
618
         # maxDb is computed on all takes (not just the specified take)
665
         # maxDb is computed on all takes (not just the specified take)
619
         db_maxL = sorted(dbL)
666
         db_maxL = sorted(dbL)
620
-        max_db = np.mean(db_maxL[-4:])
667
+        max_db = min(absMaxDb,np.mean(db_maxL[-4:]))
621
         maxDbL.append( max_db )
668
         maxDbL.append( max_db )
622
 
669
 
623
         # get the us,db values for the specified take
670
         # get the us,db values for the specified take
624
         usL,dbL = zip(*[(usL[i],dbL[i]) for i in range(len(usL)) if takeIdL[i]==manual_take_id  ])
671
         usL,dbL = zip(*[(usL[i],dbL[i]) for i in range(len(usL)) if takeIdL[i]==manual_take_id  ])
625
 
672
 
626
-        # most pitches have 3 sample takes that do not
627
-        if len(set(takeIdL)) == 3 and manual_take_id == takeId:
628
-            okL[-1] = True
629
 
673
 
630
         # min db from the sample index manually specified in cfg
674
         # min db from the sample index manually specified in cfg
631
-        manualMinDb = dbL[ manual_sample_idx ]
632
-        
675
+        manualMinDb = max(absMinDb,dbL[ manual_sample_idx ])
676
+
633
         minDbL.append( manualMinDb )
677
         minDbL.append( manualMinDb )
634
         outPitchL.append(midi_pitch)
678
         outPitchL.append(midi_pitch)
635
 
679
 
642
 
686
 
643
         
687
         
644
             
688
             
645
-    
646
-
647
     # Form the complete set of min/max db levels for each pitch by interpolating the
689
     # Form the complete set of min/max db levels for each pitch by interpolating the
648
     # db values between the manually selected anchor points.
690
     # db values between the manually selected anchor points.
649
     interpMinDbL = np.interp( pitchL, cfg.manualAnchorPitchMinDbL, anchorMinDbL )
691
     interpMinDbL = np.interp( pitchL, cfg.manualAnchorPitchMinDbL, anchorMinDbL )
861
         if len(takeDirL) == 0:
903
         if len(takeDirL) == 0:
862
             print(pitch," directory empty")
904
             print(pitch," directory empty")
863
         else:
905
         else:
864
-            with open( os.path.join(pitchDir,'0','seq.json'), "rb") as f:
865
-                r = json.load(f)
906
+            
907
+            fn = os.path.join(pitchDir,'0','seq.json')
908
+            
909
+            if not os.path.isfile(fn):
910
+                print("Missing sequence file:",fn)
911
+            else:
912
+                with open( fn, "rb") as f:
913
+                    r = json.load(f)
866
 
914
 
867
-            if len(r['eventTimeL']) != 81:
868
-                print(pitch," ",len(r['eventTimeL']))
915
+                
916
+                if len(r['eventTimeL']) != 81:
917
+                    print(pitch," ",len(r['eventTimeL']))
918
+
919
+                if len(takeDirL) != 3:
920
+                    print("***",pitch,len(takeDirL))
921
+
922
+def filter_us_db( us0L, db0L ):
923
+
924
+    us1L = [us0L[-1]]
925
+    db1L = [db0L[-1]]
926
+    dDb = 0
927
+    lastIdx = 0
928
+    for i,(us,db) in enumerate(zip( us0L[::-1],db0L[::-1])):
929
+        db1 = db1L[-1]
930
+        if db < db1 and db1-db >= dDb/2:
931
+            dDb = db1 - db
932
+            us1L.append(us)
933
+            db1L.append(db)
934
+            lastIdx = i
869
 
935
 
870
-            if len(takeDirL) != 3:
871
-                print("***",pitch,len(takeDirL))
872
 
936
 
937
+    lastIdx = len(us0L) - lastIdx - 1
938
+
939
+    
940
+    usL = [ us0L[lastIdx] ]
941
+    dbL = [ db0L[lastIdx] ]
942
+    dDb = 0
943
+    for us,db in zip(us0L[lastIdx::],db0L[lastIdx::]):
944
+        db1 = dbL[-1]
945
+        if db > db1:
946
+            dDb = db-db1
947
+            usL.append(us)
948
+            dbL.append(db)
949
+        
950
+    return usL,dbL
951
+                                    
873
 def cache_us_db( inDir, cfg, outFn ):
952
 def cache_us_db( inDir, cfg, outFn ):
874
 
953
 
954
+    pitchTakeD = get_pitches_and_takes(inDir)
955
+    
875
     pitch_usDbD = {}
956
     pitch_usDbD = {}
876
     pitchDirL = os.listdir(inDir)
957
     pitchDirL = os.listdir(inDir)
877
 
958
 
878
-    for pitch in pitchDirL:
959
+    for pitch,takeIdL in pitchTakeD.items():
879
 
960
 
880
         pitch = int(pitch)
961
         pitch = int(pitch)
962
+        takeId = takeIdL[-1]
881
 
963
 
882
         print(pitch)
964
         print(pitch)
883
         
965
         
884
-        usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
966
+        usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, pitch, cfg.analysisArgs['rmsAnalysisArgs'], takeId )
885
 
967
 
886
         pitch_usDbD[pitch] = { 'usL':usL, 'dbL':dbL, 'durMsL':durMsL, 'takeIdL':takeIdL, 'holdDutyPctL': holdDutyPctL }
968
         pitch_usDbD[pitch] = { 'usL':usL, 'dbL':dbL, 'durMsL':durMsL, 'takeIdL':takeIdL, 'holdDutyPctL': holdDutyPctL }
887
 
969
 
889
     with open(outFn,"w") as f:
971
     with open(outFn,"w") as f:
890
         json.dump(pitch_usDbD,f)
972
         json.dump(pitch_usDbD,f)
891
 
973
 
892
-        
974
+    
893
             
975
             
894
 def gen_vel_map( inDir, cfg, minMaxDbFn, dynLevelN, cacheFn ):
976
 def gen_vel_map( inDir, cfg, minMaxDbFn, dynLevelN, cacheFn ):
895
 
977
 
897
     
979
     
898
     pitchDirL = os.listdir(inDir)
980
     pitchDirL = os.listdir(inDir)
899
 
981
 
982
+    # pitchUsDbD = { pitch: 
900
     with open(cacheFn,"r") as f:
983
     with open(cacheFn,"r") as f:
901
         pitchUsDbD = json.load(f)
984
         pitchUsDbD = json.load(f)
902
         
985
         
903
 
986
 
987
+    # form minMaxDb = { pitch:(minDb,maxDb) }
904
     with open("minInterpDb.json","r") as f:
988
     with open("minInterpDb.json","r") as f:
905
         r = json.load(f)
989
         r = json.load(f)
906
         minMaxDbD = { pitch:(minDb,maxDb) for pitch,minDb,maxDb in zip(r['pitchL'],r['minDbL'],r['maxDbL']) }
990
         minMaxDbD = { pitch:(minDb,maxDb) for pitch,minDb,maxDb in zip(r['pitchL'],r['minDbL'],r['maxDbL']) }
907
-    
908
 
991
 
992
+        
909
     pitchL = sorted( [ int(pitch) for pitch in pitchUsDbD.keys()] )
993
     pitchL = sorted( [ int(pitch) for pitch in pitchUsDbD.keys()] )
910
-    
994
+
995
+    # for each pitch
911
     for pitch in pitchL:
996
     for pitch in pitchL:
997
+
998
+        # get the us/db map for this 
912
         d = pitchUsDbD[str(pitch)]
999
         d = pitchUsDbD[str(pitch)]
1000
+
1001
+
1002
+        usL, dbL = filter_us_db( d['usL'], d['dbL'] )        
1003
+        #usL = d['usL']
1004
+        #dbL = np.array(d['dbL'])
1005
+        dbL = np.array(dbL)
913
         
1006
         
914
-        usL = d['usL']
915
-        dbL = np.array(d['dbL'])
916
 
1007
 
917
         velMapD[pitch] = []
1008
         velMapD[pitch] = []
918
-        
919
-        for i in range(dynLevelN+1):
920
 
1009
 
921
-            db = minMaxDbD[pitch][0] + (i * (minMaxDbD[pitch][1] - minMaxDbD[pitch][0])/ dynLevelN)
1010
+        maxDb = minMaxDbD[pitch][1]
1011
+        minDb = minMaxDbD[pitch][0]
1012
+
1013
+        dynLevelN = len(cfg.velTableDbL)
1014
+
1015
+        # for each dynamic level
1016
+        for i in range(dynLevelN):
1017
+
1018
+            #db = minDb + (i * (maxDb - minDb)/ dynLevelN)
1019
+            db = cfg.velTableDbL[i]
922
 
1020
 
923
             usIdx = np.argmin( np.abs(dbL - db) )
1021
             usIdx = np.argmin( np.abs(dbL - db) )
924
             
1022
             
946
     
1044
     
947
 if __name__ == "__main__":
1045
 if __name__ == "__main__":
948
 
1046
 
949
-    printDir = None #os.path.expanduser( "~/src/picadae_ac_3/doc")
1047
+    printDir = os.path.expanduser("~/temp") # os.path.expanduser( "~/src/picadae_ac_3/doc")
950
     cfgFn   = sys.argv[1]
1048
     cfgFn   = sys.argv[1]
951
     inDir   = sys.argv[2]
1049
     inDir   = sys.argv[2]
952
     mode    = sys.argv[3]
1050
     mode    = sys.argv[3]
981
     elif mode == 'manual_db':
1079
     elif mode == 'manual_db':
982
         plot_min_db_manual( inDir, cfg, printDir=printDir )
1080
         plot_min_db_manual( inDir, cfg, printDir=printDir )
983
     elif mode == 'gen_vel_map':
1081
     elif mode == 'gen_vel_map':
984
-        gen_vel_map( inDir, cfg, "minInterpDb.json", 9, "cache_us_db.json" )
1082
+        gen_vel_map( inDir, cfg, "minInterpDb.json", 12, "cache_us_db.json" )
985
     elif mode == 'cache_us_db':
1083
     elif mode == 'cache_us_db':
986
         cache_us_db( inDir, cfg, "cache_us_db.json")
1084
         cache_us_db( inDir, cfg, "cache_us_db.json")
987
     else:
1085
     else:

+ 1
- 1
plot_us_db_range.ipynb Näytä tiedosto

75
    "name": "python",
75
    "name": "python",
76
    "nbconvert_exporter": "python",
76
    "nbconvert_exporter": "python",
77
    "pygments_lexer": "ipython3",
77
    "pygments_lexer": "ipython3",
78
-   "version": "3.7.5"
78
+   "version": "3.8.6"
79
   }
79
   }
80
  },
80
  },
81
  "nbformat": 4,
81
  "nbformat": 4,

+ 4
- 1
rms_analysis.py Näytä tiedosto

254
         
254
         
255
         
255
         
256
                
256
                
257
-def locate_peak_indexes( xV, xV_srate, eventMsL, audioFn ):
257
+def locate_peak_indexes( xV, xV_srate, eventMsL, audioFn="" ):
258
 
258
 
259
     pkIdxL = []
259
     pkIdxL = []
260
     for i, (begMs, endMs) in enumerate(eventMsL):
260
     for i, (begMs, endMs) in enumerate(eventMsL):
601
 
601
 
602
             takeDir = os.path.join(pitchDir,takeFolder)
602
             takeDir = os.path.join(pitchDir,takeFolder)
603
 
603
 
604
+            if not os.path.isfile(os.path.join(takeDir,"seq.json")):
605
+                continue
606
+
604
             r = rms_analysis_main( takeDir, midi_pitch, **analysisArgsD )
607
             r = rms_analysis_main( takeDir, midi_pitch, **analysisArgsD )
605
 
608
 
606
             labelFn = os.path.join(takeDir,"audacity.txt")
609
             labelFn = os.path.join(takeDir,"audacity.txt")

Loading…
Peruuta
Tallenna