Many changes and additions.
This commit is contained in:
parent
ca9580cd50
commit
4d3044af67
147
MidiFilePlayer.py
Normal file
147
MidiFilePlayer.py
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
class MidiFilePlayer:
|
||||||
|
def __init__( self, cfg, api, midiDev, midiFn, velMapFn="velMapD.json" ):
|
||||||
|
self.cfg = cfg
|
||||||
|
self.api = api
|
||||||
|
self.midiDev = midiDev
|
||||||
|
self.midiL = [] # [ (us,status,d0,d1) ]
|
||||||
|
|
||||||
|
self._parse_midi_file(midiFn)
|
||||||
|
self.nextIdx = None
|
||||||
|
self.startMs = 0
|
||||||
|
self.curDutyPctD = {} # { pitch:duty } track the current hold duty cycle of each note
|
||||||
|
self.velMapD = {}
|
||||||
|
self.holdDutyPctD = cfg.calibrateArgs['holdDutyPctD']
|
||||||
|
|
||||||
|
with open(velMapFn,'r') as f:
|
||||||
|
velMapD = json.load(f)
|
||||||
|
|
||||||
|
for pitch,usDbL in velMapD.items():
|
||||||
|
self.velMapD[ int(pitch) ] = usDbL
|
||||||
|
|
||||||
|
def start(self, ms):
|
||||||
|
self.nextIdx = 0
|
||||||
|
self.startMs = ms
|
||||||
|
|
||||||
|
def stop( self, ms):
|
||||||
|
self.nextIdx = None
|
||||||
|
for pitch in self.velMapD.keys():
|
||||||
|
self.api.note_off( int(pitch) )
|
||||||
|
|
||||||
|
def tick( self, ms):
|
||||||
|
|
||||||
|
if self.nextIdx is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
curOffsMs = ms - self.startMs
|
||||||
|
|
||||||
|
while self.nextIdx < len(self.midiL):
|
||||||
|
|
||||||
|
if curOffsMs < self.midiL[ self.nextIdx ][0]:
|
||||||
|
break
|
||||||
|
|
||||||
|
cmd = self.midiL[ self.nextIdx ][1]
|
||||||
|
|
||||||
|
if cmd == 'non':
|
||||||
|
self._note_on(self.midiL[ self.nextIdx ][2],self.midiL[ self.nextIdx ][3])
|
||||||
|
elif cmd == 'nof':
|
||||||
|
self._note_off(self.midiL[ self.nextIdx ][2])
|
||||||
|
elif cmd == 'ctl' and self.midiL[ self.nextIdx ][2] == 64:
|
||||||
|
self.midiDev.send_controller(64,self.midiL[ self.nextIdx ][3])
|
||||||
|
|
||||||
|
self.nextIdx += 1
|
||||||
|
|
||||||
|
|
||||||
|
if self.nextIdx >= len(self.midiL):
|
||||||
|
self.nextIdx = None
|
||||||
|
|
||||||
|
def _get_duty_cycle( self, pitch, pulseUsec ):
|
||||||
|
|
||||||
|
dutyPct = 50
|
||||||
|
|
||||||
|
if pitch in self.holdDutyPctD:
|
||||||
|
|
||||||
|
dutyPct = self.holdDutyPctD[pitch][0][1]
|
||||||
|
for refUsec,refDuty in self.holdDutyPctD[pitch]:
|
||||||
|
print(pitch,refUsec,refDuty)
|
||||||
|
if pulseUsec < refUsec:
|
||||||
|
break
|
||||||
|
dutyPct = refDuty
|
||||||
|
|
||||||
|
return dutyPct
|
||||||
|
|
||||||
|
def _set_duty_cycle( self, pitch, pulseUsec ):
|
||||||
|
|
||||||
|
dutyPct = self._get_duty_cycle( pitch, pulseUsec )
|
||||||
|
|
||||||
|
if pitch not in self.curDutyPctD or self.curDutyPctD[pitch] != dutyPct:
|
||||||
|
self.curDutyPctD[pitch] = dutyPct
|
||||||
|
self.api.set_pwm_duty( pitch, dutyPct )
|
||||||
|
print("Hold Duty Set:",dutyPct)
|
||||||
|
|
||||||
|
return dutyPct
|
||||||
|
|
||||||
|
def _get_pulse_us( self, pitch, vel ):
|
||||||
|
|
||||||
|
usDbL = self.velMapD[pitch]
|
||||||
|
idx = round(vel * len(usDbL) / 127)
|
||||||
|
|
||||||
|
if idx > len(usDbL):
|
||||||
|
idx = len(usDbL)-1
|
||||||
|
|
||||||
|
us = usDbL[ idx ][0]
|
||||||
|
|
||||||
|
print('non',pitch,vel,idx,us)
|
||||||
|
|
||||||
|
return us
|
||||||
|
|
||||||
|
def _note_on( self, pitch, vel ):
|
||||||
|
|
||||||
|
if pitch not in self.velMapD:
|
||||||
|
print("Missing pitch:",pitch)
|
||||||
|
else:
|
||||||
|
pulseUs = self._get_pulse_us(pitch,vel)
|
||||||
|
self._set_duty_cycle( pitch, pulseUs )
|
||||||
|
self.api.note_on_us( pitch, pulseUs )
|
||||||
|
|
||||||
|
|
||||||
|
def _note_off( self, pitch ):
|
||||||
|
self.api.note_off( pitch )
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_midi_file( self,fn ):
|
||||||
|
|
||||||
|
with open(fn,"r") as f:
|
||||||
|
|
||||||
|
for lineNumb,line in enumerate(f):
|
||||||
|
if lineNumb >= 3:
|
||||||
|
tokenL = line.split()
|
||||||
|
|
||||||
|
if len(tokenL) > 5:
|
||||||
|
usec = int(tokenL[3])
|
||||||
|
status = None
|
||||||
|
d0 = None
|
||||||
|
d1 = None
|
||||||
|
if tokenL[5] == 'non' or tokenL[5]=='nof' or tokenL[5]=='ctl':
|
||||||
|
status = tokenL[5]
|
||||||
|
d0 = int(tokenL[7])
|
||||||
|
d1 = int(tokenL[8])
|
||||||
|
self.midiL.append( (usec/1000,status,d0,d1))
|
||||||
|
|
||||||
|
self.midiL = sorted( self.midiL, key=lambda x: x[0] )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
midiFn = "/home/kevin/media/audio/midi/txt/988-v25.txt"
|
||||||
|
|
||||||
|
mfp = MidiFilePlayer(None,None,midiFn)
|
||||||
|
|
||||||
|
print(mfp.midiL[0:10])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
138
calibrate.py
138
calibrate.py
@ -1,38 +1,41 @@
|
|||||||
import os,types,wave,json,array
|
import os,types,wave,json,array
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from rms_analysis import rms_analyze_one_rt_note
|
from rms_analysis import rms_analyze_one_rt_note
|
||||||
|
from plot_seq_1 import get_merged_pulse_db_measurements
|
||||||
|
|
||||||
class Calibrate:
|
class Calibrate:
|
||||||
def __init__( self, cfg, audio, midi, api ):
|
def __init__( self, cfg, audio, midi, api ):
|
||||||
self.cfg = types.SimpleNamespace(**cfg)
|
self.cfg = types.SimpleNamespace(**cfg)
|
||||||
self.audio = audio
|
self.audio = audio
|
||||||
self.midi = midi
|
self.midi = midi
|
||||||
self.api = api
|
self.api = api
|
||||||
self.state = "stopped" # stopped | started | note_on | note_off | analyzing
|
self.state = "stopped" # stopped | started | note_on | note_off | analyzing
|
||||||
self.playOnlyFl = False
|
self.playOnlyFl = False
|
||||||
self.startMs = None
|
self.startMs = None
|
||||||
self.nextStateChangeMs = None
|
self.nextStateChangeMs = None
|
||||||
self.curHoldDutyCyclePctD = None # { pitch:dutyPct}
|
self.curHoldDutyCyclePctD = None # { pitch:dutyPct}
|
||||||
self.noteAnnotationL = [] # (noteOnMs,noteOffMs,pitch,pulseUs)
|
self.noteAnnotationL = [] # (noteOnMs,noteOffMs,pitch,pulseUs)
|
||||||
|
|
||||||
self.measD = None # { midi_pitch: [ {pulseUs, db, durMs, targetDb } ] }
|
self.measD = None # { midi_pitch: [ {pulseUs, db, durMs, targetDb } ] }
|
||||||
|
|
||||||
self.curNoteStartMs = None
|
self.initPulseDbListD = self._get_init_pulseDbD()
|
||||||
self.curPitchIdx = None
|
|
||||||
self.curTargetDbIdx = None
|
|
||||||
self.successN = None
|
|
||||||
self.failN = None
|
|
||||||
|
|
||||||
self.curTargetDb = None
|
self.curNoteStartMs = None
|
||||||
self.curPulseUs = None
|
self.curPitchIdx = None
|
||||||
self.curMatchN = None
|
self.curTargetDbIdx = None
|
||||||
self.curAttemptN = None
|
self.successN = None
|
||||||
|
self.failN = None
|
||||||
|
|
||||||
|
self.curTargetDb = None
|
||||||
|
self.curPulseUs = None
|
||||||
|
self.curMatchN = None
|
||||||
|
self.curAttemptN = None
|
||||||
self.lastAudiblePulseUs = None
|
self.lastAudiblePulseUs = None
|
||||||
self.maxTooShortPulseUs = None
|
self.maxTooShortPulseUs = None
|
||||||
self.pulseDbL = None
|
self.pulseDbL = None
|
||||||
self.deltaUpMult = None
|
self.deltaUpMult = None
|
||||||
self.deltaDnMult = None
|
self.deltaDnMult = None
|
||||||
self.skipMeasFl = None
|
self.skipMeasFl = None
|
||||||
|
|
||||||
def start(self,ms):
|
def start(self,ms):
|
||||||
self.stop(ms)
|
self.stop(ms)
|
||||||
@ -42,22 +45,23 @@ class Calibrate:
|
|||||||
|
|
||||||
self.startMs = ms
|
self.startMs = ms
|
||||||
|
|
||||||
self.curPitchIdx = 0
|
self.curPitchIdx = 0
|
||||||
self.curPulseUs = self.cfg.initPulseUs
|
self.curPulseUs = self.cfg.initPulseUs
|
||||||
self.lastAudiblePulseUs = None
|
self.lastAudiblePulseUs = None
|
||||||
self.maxTooShortPulseUs = None
|
self.maxTooShortPulseUs = None
|
||||||
self.pulseDbL = []
|
self.pulseDbL = []
|
||||||
self.deltaUpMult = 1
|
self.pulseDbL = self.initPulseDbListD[ self.cfg.pitchL[ self.curPitchIdx ] ]
|
||||||
self.deltaDnMult = 1
|
self.deltaUpMult = 1
|
||||||
self.curTargetDbIdx = -1
|
self.deltaDnMult = 1
|
||||||
|
self.curTargetDbIdx = -1
|
||||||
self._start_new_db_target()
|
self._start_new_db_target()
|
||||||
|
|
||||||
self.curDutyPctD = {}
|
self.curDutyPctD = {}
|
||||||
self.skipMeasFl = False
|
self.skipMeasFl = False
|
||||||
self.measD = {}
|
self.measD = {}
|
||||||
|
|
||||||
self.successN = 0
|
self.successN = 0
|
||||||
self.failN = 0
|
self.failN = 0
|
||||||
self.audio.record_enable(True)
|
self.audio.record_enable(True)
|
||||||
|
|
||||||
def stop(self,ms):
|
def stop(self,ms):
|
||||||
@ -84,7 +88,7 @@ class Calibrate:
|
|||||||
|
|
||||||
self.audio.record_enable(True)
|
self.audio.record_enable(True)
|
||||||
|
|
||||||
self._do_play_update()
|
self._do_play_only_update()
|
||||||
|
|
||||||
def tick(self,ms):
|
def tick(self,ms):
|
||||||
|
|
||||||
@ -105,7 +109,7 @@ class Calibrate:
|
|||||||
|
|
||||||
elif self.state == 'note_off':
|
elif self.state == 'note_off':
|
||||||
if self.playOnlyFl:
|
if self.playOnlyFl:
|
||||||
if not self._do_play_update():
|
if not self._do_play_only_update():
|
||||||
self.stop(ms)
|
self.stop(ms)
|
||||||
self.state = 'stopped'
|
self.state = 'stopped'
|
||||||
else:
|
else:
|
||||||
@ -120,7 +124,7 @@ class Calibrate:
|
|||||||
self.state = 'started'
|
self.state = 'started'
|
||||||
|
|
||||||
|
|
||||||
def _calc_play_pulse_us( self, pitch, targetDb ):
|
def _calc_play_only_pulse_us( self, pitch, targetDb ):
|
||||||
|
|
||||||
pulseDbL = []
|
pulseDbL = []
|
||||||
for d in self.measD[ pitch ]:
|
for d in self.measD[ pitch ]:
|
||||||
@ -136,7 +140,7 @@ class Calibrate:
|
|||||||
|
|
||||||
return np.mean(pulseL)
|
return np.mean(pulseL)
|
||||||
|
|
||||||
def _do_play_update( self ):
|
def _do_play_only_update( self ):
|
||||||
|
|
||||||
if self.curPitchIdx >= 0:
|
if self.curPitchIdx >= 0:
|
||||||
self._meas_note( self.cfg.pitchL[self.curPitchIdx], self.curPulseUs )
|
self._meas_note( self.cfg.pitchL[self.curPitchIdx], self.curPulseUs )
|
||||||
@ -150,7 +154,7 @@ class Calibrate:
|
|||||||
|
|
||||||
pitch = self.cfg.pitchL[ self.curPitchIdx ]
|
pitch = self.cfg.pitchL[ self.curPitchIdx ]
|
||||||
targetDb = self.cfg.targetDbL[ self.curTargetDbIdx ]
|
targetDb = self.cfg.targetDbL[ self.curTargetDbIdx ]
|
||||||
self.curPulseUs = self._calc_play_pulse_us( pitch, targetDb )
|
self.curPulseUs = self._calc_play_only_pulse_us( pitch, targetDb )
|
||||||
self.curTargetDb = targetDb
|
self.curTargetDb = targetDb
|
||||||
|
|
||||||
if self.curPulseUs == -1:
|
if self.curPulseUs == -1:
|
||||||
@ -161,7 +165,24 @@ class Calibrate:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _get_init_pulseDbD( self ):
|
||||||
|
|
||||||
|
initPulseDbListD = {}
|
||||||
|
|
||||||
|
print("Calculating initial calibration search us/db lists ...")
|
||||||
|
if self.cfg.inDir is not None:
|
||||||
|
|
||||||
|
for pitch in self.cfg.pitchL:
|
||||||
|
|
||||||
|
print(pitch)
|
||||||
|
|
||||||
|
inDir = os.path.expanduser( self.cfg.inDir )
|
||||||
|
|
||||||
|
usL,dbL,_,_,_ = get_merged_pulse_db_measurements( inDir, pitch, self.cfg.analysisD )
|
||||||
|
|
||||||
|
initPulseDbListD[pitch] = [ (us,db) for us,db in zip(usL,dbL) ]
|
||||||
|
|
||||||
|
return initPulseDbListD
|
||||||
|
|
||||||
def _get_duty_cycle( self, pitch, pulseUsec ):
|
def _get_duty_cycle( self, pitch, pulseUsec ):
|
||||||
|
|
||||||
@ -232,8 +253,9 @@ class Calibrate:
|
|||||||
|
|
||||||
return int(round(curPulse + np.sign(targetDb - curDb) * delta_pulse))
|
return int(round(curPulse + np.sign(targetDb - curDb) * delta_pulse))
|
||||||
|
|
||||||
def _step( self, targetDb, dbL, pulseL ):
|
def _step( self, targetDb ):
|
||||||
|
|
||||||
|
# get the last two pulse/db samples
|
||||||
pulse0,db0 = self.pulseDbL[-2]
|
pulse0,db0 = self.pulseDbL[-2]
|
||||||
pulse1,db1 = self.pulseDbL[-1]
|
pulse1,db1 = self.pulseDbL[-1]
|
||||||
|
|
||||||
@ -255,19 +277,22 @@ class Calibrate:
|
|||||||
|
|
||||||
def _calc_next_pulse_us( self, targetDb ):
|
def _calc_next_pulse_us( self, targetDb ):
|
||||||
|
|
||||||
# sort pulseDb ascending on db
|
|
||||||
#self.pulseDbL = sorted( self.pulseDbL, key=lambda x: x[1] )
|
|
||||||
|
|
||||||
|
|
||||||
pulseL,dbL = zip(*self.pulseDbL)
|
# sort pulseDb ascending on db
|
||||||
|
pulseDbL = sorted( self.pulseDbL, key=lambda x: x[1] )
|
||||||
|
|
||||||
|
# get the set of us/db values tried so far
|
||||||
|
pulseL,dbL = zip(*pulseDbL)
|
||||||
|
|
||||||
max_i = np.argmax(dbL)
|
max_i = np.argmax(dbL)
|
||||||
min_i = np.argmin(dbL)
|
min_i = np.argmin(dbL)
|
||||||
|
|
||||||
|
# if the targetDb is greater than the max. db value achieved so far
|
||||||
if targetDb > dbL[max_i]:
|
if targetDb > dbL[max_i]:
|
||||||
pu = pulseL[max_i] + self.deltaUpMult * 500
|
pu = pulseL[max_i] + self.deltaUpMult * 500
|
||||||
self.deltaUpMult += 1
|
self.deltaUpMult += 1
|
||||||
|
|
||||||
|
# if the targetDb is less than the min. db value achieved so far
|
||||||
elif targetDb < dbL[min_i]:
|
elif targetDb < dbL[min_i]:
|
||||||
pu = pulseL[min_i] - self.deltaDnMult * 500
|
pu = pulseL[min_i] - self.deltaDnMult * 500
|
||||||
self.deltaDnMult += 1
|
self.deltaDnMult += 1
|
||||||
@ -277,12 +302,18 @@ class Calibrate:
|
|||||||
pu = self.maxTooShortPulseUs + (abs(pulseL[min_i] - self.maxTooShortPulseUs))/2
|
pu = self.maxTooShortPulseUs + (abs(pulseL[min_i] - self.maxTooShortPulseUs))/2
|
||||||
self.deltaDnMult = 1
|
self.deltaDnMult = 1
|
||||||
else:
|
else:
|
||||||
|
# the targetDb value is inside the min/max range of the db values acheived so far
|
||||||
self.deltaUpMult = 1
|
self.deltaUpMult = 1
|
||||||
self.deltaDnMult = 1
|
self.deltaDnMult = 1
|
||||||
|
|
||||||
|
# interpolate the new pulse value based on the values seen so far
|
||||||
|
|
||||||
|
# TODO: use only closest 5 values rather than all values
|
||||||
pu = np.interp([targetDb],dbL,pulseL)
|
pu = np.interp([targetDb],dbL,pulseL)
|
||||||
|
|
||||||
|
# the selected pulse has already been sampled
|
||||||
if int(pu) in pulseL:
|
if int(pu) in pulseL:
|
||||||
pu = self._step(targetDb, dbL, pulseL )
|
pu = self._step(targetDb )
|
||||||
|
|
||||||
|
|
||||||
return max(min(pu,self.cfg.maxPulseUs),self.cfg.minPulseUs)
|
return max(min(pu,self.cfg.maxPulseUs),self.cfg.minPulseUs)
|
||||||
@ -319,23 +350,23 @@ class Calibrate:
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# this is a valid measurement store it to the pulse-db table
|
# this is a valid measurement, store it to the pulse-db table
|
||||||
self.pulseDbL.append( (self.curPulseUs,db) )
|
self.pulseDbL.append( (self.curPulseUs,db) )
|
||||||
|
|
||||||
# track the most recent audible note - to return to if a successive note is too short
|
# track the most recent audible note (to return to if a successive note is too short)
|
||||||
self.lastAudiblePulseUs = self.curPulseUs
|
self.lastAudiblePulseUs = self.curPulseUs
|
||||||
|
|
||||||
# calc the upper and lower bounds db range
|
# calc the upper and lower bounds db range
|
||||||
lwr_db = self.curTargetDb * ((100.0 - self.cfg.tolDbPct)/100.0)
|
lwr_db = self.curTargetDb * ((100.0 - self.cfg.tolDbPct)/100.0)
|
||||||
upr_db = self.curTargetDb * ((100.0 + self.cfg.tolDbPct)/100.0)
|
upr_db = self.curTargetDb * ((100.0 + self.cfg.tolDbPct)/100.0)
|
||||||
|
|
||||||
# was this note is inside the db range then set the 'match' flag
|
# if this note was inside the db range then set the 'match' flag
|
||||||
if lwr_db <= db and db <= upr_db:
|
if lwr_db <= db and db <= upr_db:
|
||||||
self.curMatchN += 1
|
self.curMatchN += 1
|
||||||
measD['matchFl'] = True
|
measD['matchFl'] = True
|
||||||
print("MATCH!")
|
print("MATCH!")
|
||||||
|
|
||||||
#
|
# calculate the next pulse length
|
||||||
self.curPulseUs = int(self._calc_next_pulse_us(self.curTargetDb))
|
self.curPulseUs = int(self._calc_next_pulse_us(self.curTargetDb))
|
||||||
|
|
||||||
# if at least minMatchN matches have been made on this pitch/targetDb
|
# if at least minMatchN matches have been made on this pitch/targetDb
|
||||||
@ -371,8 +402,6 @@ class Calibrate:
|
|||||||
|
|
||||||
sigV = buf_result.value
|
sigV = buf_result.value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# get the annotated begin and end of the note as sample indexes into sigV
|
# get the annotated begin and end of the note as sample indexes into sigV
|
||||||
bi = int(round(annD['beg_ms'] * self.audio.srate / 1000))
|
bi = int(round(annD['beg_ms'] * self.audio.srate / 1000))
|
||||||
ei = int(round(annD['end_ms'] * self.audio.srate / 1000))
|
ei = int(round(annD['end_ms'] * self.audio.srate / 1000))
|
||||||
@ -384,7 +413,6 @@ class Calibrate:
|
|||||||
bi = max(0,bi - noteOffSmp_o_2)
|
bi = max(0,bi - noteOffSmp_o_2)
|
||||||
ei = min(ei+noteOffSmp_o_2,sigV.shape[0]-1)
|
ei = min(ei+noteOffSmp_o_2,sigV.shape[0]-1)
|
||||||
|
|
||||||
|
|
||||||
ar = types.SimpleNamespace(**self.cfg.analysisD)
|
ar = types.SimpleNamespace(**self.cfg.analysisD)
|
||||||
|
|
||||||
# shift the annotatd begin/end of the note to be relative to index bi
|
# shift the annotatd begin/end of the note to be relative to index bi
|
||||||
@ -393,9 +421,10 @@ class Calibrate:
|
|||||||
|
|
||||||
#print("MEAS:",begMs,endMs,bi,ei,sigV.shape,self.audio.is_recording_enabled(),ar)
|
#print("MEAS:",begMs,endMs,bi,ei,sigV.shape,self.audio.is_recording_enabled(),ar)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# analyze the note
|
# analyze the note
|
||||||
resD = rms_analyze_one_rt_note( sigV[bi:ei], self.audio.srate, begMs, endMs, midi_pitch, rmsWndMs=ar.rmsWndMs, rmsHopMs=ar.rmsHopMs, dbRefWndMs=ar.dbRefWndMs, harmCandN=ar.harmCandN, harmN=ar.harmN, durDecayPct=ar.durDecayPct )
|
resD = rms_analyze_one_rt_note( sigV[bi:ei], self.audio.srate, begMs, endMs, midi_pitch, rmsWndMs=ar.rmsWndMs, rmsHopMs=ar.rmsHopMs, dbLinRef=ar.dbLinRef, harmCandN=ar.harmCandN, harmN=ar.harmN, durDecayPct=ar.durDecayPct )
|
||||||
|
|
||||||
resD["pulse_us"] = pulse_us
|
resD["pulse_us"] = pulse_us
|
||||||
resD["midi_pitch"] = midi_pitch
|
resD["midi_pitch"] = midi_pitch
|
||||||
@ -405,9 +434,8 @@ class Calibrate:
|
|||||||
resD['matchFl'] = False
|
resD['matchFl'] = False
|
||||||
resD['targetDb'] = self.curTargetDb
|
resD['targetDb'] = self.curTargetDb
|
||||||
resD['annIdx'] = len(self.noteAnnotationL)-1
|
resD['annIdx'] = len(self.noteAnnotationL)-1
|
||||||
|
|
||||||
print( "%4.1f hm:%4.1f (%4.1f) %4i td:%4.1f (%4.1f) %4i" % (self.curTargetDb,resD['hm']['db'], resD['hm']['db']-self.curTargetDb, resD['hm']['durMs'], resD['td']['db'], resD['td']['db']-self.curTargetDb, resD['td']['durMs']))
|
|
||||||
|
|
||||||
|
print( "%4.1f hm:%4.1f (%4.1f) %4i td:%4.1f (%4.1f) %4i" % (self.curTargetDb,resD['hm']['db'], resD['hm']['db']-self.curTargetDb, resD['hm']['durMs'], resD['td']['db'], resD['td']['db']-self.curTargetDb, resD['td']['durMs']))
|
||||||
|
|
||||||
return resD
|
return resD
|
||||||
|
|
||||||
@ -427,13 +455,15 @@ class Calibrate:
|
|||||||
if self.curPitchIdx >= len(self.cfg.pitchL):
|
if self.curPitchIdx >= len(self.cfg.pitchL):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# reset the variables prior to begining the next target search
|
||||||
self.curTargetDb = self.cfg.targetDbL[ self.curTargetDbIdx ]
|
self.curTargetDb = self.cfg.targetDbL[ self.curTargetDbIdx ]
|
||||||
self.curMatchN = 0
|
self.curMatchN = 0
|
||||||
self.curAttemptN = 0
|
self.curAttemptN = 0
|
||||||
self.lastAudiblePulseUs = None
|
self.lastAudiblePulseUs = None
|
||||||
self.maxTooShortPulseUs = None
|
self.maxTooShortPulseUs = None
|
||||||
self.pulseDbL = []
|
self.pulseDbL = []
|
||||||
|
self.pulseDbL = self.initPulseDbListD[ self.cfg.pitchL[ self.curPitchIdx ] ]
|
||||||
self.deltaUpMult = 1
|
self.deltaUpMult = 1
|
||||||
self.deltaDnMult = 1
|
self.deltaDnMult = 1
|
||||||
return True
|
return True
|
||||||
|
45
calibrate_plot.py
Normal file
45
calibrate_plot.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import os,sys,json
|
||||||
|
import common
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import plot_seq_1
|
||||||
|
|
||||||
|
def plot_calibrate( cfg, pitch, dataD ):
|
||||||
|
|
||||||
|
dataL = [ (d['pulse_us'], d['hm']['db'], d['targetDb'], d['matchFl'],d['skipMeasFl'],d['annIdx']) for d in dataD['measD'][pitch] ]
|
||||||
|
|
||||||
|
udmL = [(t[0],t[1],t[3]) for t in dataL]
|
||||||
|
udmL = sorted( udmL, key=lambda x: x[0] )
|
||||||
|
usL,dbL,matchL = zip(*udmL)
|
||||||
|
|
||||||
|
fig,ax = plt.subplots()
|
||||||
|
|
||||||
|
musL = [us for us,db,m in udmL if m]
|
||||||
|
mdbL = [db for us,db,m in udmL if m]
|
||||||
|
|
||||||
|
ax.plot(musL,mdbL,marker='o',color='red',linestyle='None')
|
||||||
|
|
||||||
|
ax.plot(usL,dbL,marker='.')
|
||||||
|
|
||||||
|
initDataDir = os.path.expanduser(dataD['cfg']['inDir'])
|
||||||
|
usL,dbL,_,_,_ = plot_seq_1.get_merged_pulse_db_measurements( initDataDir, int(pitch), cfg['analysisD'] )
|
||||||
|
|
||||||
|
|
||||||
|
ax.plot(usL,dbL,marker='.')
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
inDir = sys.argv[1]
|
||||||
|
cfgFn = sys.argv[2]
|
||||||
|
pitch = sys.argv[3]
|
||||||
|
|
||||||
|
cfg = common.parse_yaml_cfg(cfgFn)
|
||||||
|
cfg = cfg.calibrateArgs
|
||||||
|
|
||||||
|
dataFn = os.path.join(inDir,"meas.json")
|
||||||
|
with open(dataFn,"r") as f:
|
||||||
|
dataD = json.load(f)
|
||||||
|
|
||||||
|
print("pitchL:",dataD['cfg']['pitchL'],"targetDbL:",dataD['cfg']['targetDbL'])
|
||||||
|
plot_calibrate( cfg, pitch, dataD )
|
85
elbow.py
Normal file
85
elbow.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
import sys,os
|
||||||
|
import numpy as np
|
||||||
|
import common
|
||||||
|
import rms_analysis
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def fit_points_to_reference( usL, dbL, usRefL, dbRefL ):
|
||||||
|
|
||||||
|
dbV = None
|
||||||
|
|
||||||
|
yyL = [ (db,dbRefL[ usRefL.index(us)]) for i,(us,db) in enumerate(zip(usL,dbL)) if us in usRefL ]
|
||||||
|
|
||||||
|
if len(yyL) < 10:
|
||||||
|
print("NO FIT")
|
||||||
|
else:
|
||||||
|
|
||||||
|
y0L,yrL = zip(*yyL)
|
||||||
|
yN = len(y0L)
|
||||||
|
|
||||||
|
A = np.vstack([np.ones(yN),y0L]).T
|
||||||
|
c,m = np.linalg.lstsq(A,yrL,rcond=None)[0]
|
||||||
|
|
||||||
|
dbV = (np.array(dbL) * m) + c
|
||||||
|
|
||||||
|
|
||||||
|
return dbV
|
||||||
|
|
||||||
|
def find_elbow( usL, dbL, pointsPerLine=10 ):
|
||||||
|
|
||||||
|
ppl_2 = int(pointsPerLine/2)
|
||||||
|
dL = []
|
||||||
|
|
||||||
|
|
||||||
|
i = pointsPerLine
|
||||||
|
|
||||||
|
# for each consecutive set of 'pointsPerLine' points in usL and dbL
|
||||||
|
while i < len(usL):
|
||||||
|
|
||||||
|
# find the x,y coordinates of the first 'ppl_2' coordinates
|
||||||
|
x0L = np.array([ (us,1.0) for us in usL[i-pointsPerLine:i-ppl_2] ])
|
||||||
|
y0L = np.array(usL[i-pointsPerLine:i-ppl_2])
|
||||||
|
|
||||||
|
# find the x,y coordinates of the second 'ppl_2' coordinates
|
||||||
|
x1L = np.array([ (us,1.0) for us in usL[i-ppl_2:i]])
|
||||||
|
y1L = np.array(dbL[i-ppl_2:i])
|
||||||
|
|
||||||
|
|
||||||
|
m0,c0 = np.linalg.lstsq(x0L,y0L,rcond=None)[0] # fit a line through the first set of points
|
||||||
|
m1,c1 = np.linalg.lstsq(x1L,y1L,rcond=None)[0] # fit a line through the second set of points
|
||||||
|
|
||||||
|
# store the angle between the two lines
|
||||||
|
dL.append(m1-m0)
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# find the max angle
|
||||||
|
i = np.argmax( dL )
|
||||||
|
|
||||||
|
# return the x,y coordinate of the first data point of the second line
|
||||||
|
return (usL[i+ppl_2],dbL[i+ppl_2])
|
||||||
|
|
||||||
|
def find_elbow_main( cfg, inDir, midi_pitch, takeId ):
|
||||||
|
|
||||||
|
inDir = os.path.join(inDir,str(pitch),str(takeId))
|
||||||
|
analysisArgsD = cfg.analysisArgs['rmsAnalysArgs']
|
||||||
|
|
||||||
|
r = rms_analysis_main( inDir, int(midi_pitch), **analysisD )
|
||||||
|
|
||||||
|
usL = r.pkUsL
|
||||||
|
dbL = r.pkDbL
|
||||||
|
|
||||||
|
return find_elbow(r.pkUsL,r.pkDbL)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
inDir = sys.argv[1]
|
||||||
|
cfgFn = sys.argv[2]
|
||||||
|
pitch = sys.argv[3]
|
||||||
|
|
||||||
|
cfg = common.parse_yaml_cfg(cfgFn)
|
||||||
|
|
||||||
|
find_elbow( cfg, inDir, pitch, 0 )
|
132
p_ac.py
132
p_ac.py
@ -10,11 +10,13 @@ from MidiDevice import MidiDevice
|
|||||||
from result import Result
|
from result import Result
|
||||||
from common import parse_yaml_cfg
|
from common import parse_yaml_cfg
|
||||||
from plot_seq import form_resample_pulse_time_list
|
from plot_seq import form_resample_pulse_time_list
|
||||||
from plot_seq import get_resample_points_wrap
|
from plot_seq_1 import get_resample_points_wrap
|
||||||
from plot_seq import form_final_pulse_list
|
from plot_seq import form_final_pulse_list
|
||||||
from rt_note_analysis import RT_Analyzer
|
from rt_note_analysis import RT_Analyzer
|
||||||
from keyboard import Keyboard
|
from keyboard import Keyboard
|
||||||
from calibrate import Calibrate
|
from calibrate import Calibrate
|
||||||
|
from rms_analysis import rms_analyze_one_rt_note_wrap
|
||||||
|
from MidiFilePlayer import MidiFilePlayer
|
||||||
|
|
||||||
class AttackPulseSeq:
|
class AttackPulseSeq:
|
||||||
""" Sequence a fixed pitch over a list of attack pulse lengths."""
|
""" Sequence a fixed pitch over a list of attack pulse lengths."""
|
||||||
@ -29,7 +31,8 @@ class AttackPulseSeq:
|
|||||||
self.noteDurMs = noteDurMs # duration of each chord in milliseconds
|
self.noteDurMs = noteDurMs # duration of each chord in milliseconds
|
||||||
self.pauseDurMs = pauseDurMs # duration between end of previous note and start of next
|
self.pauseDurMs = pauseDurMs # duration between end of previous note and start of next
|
||||||
self.holdDutyPctL= None # hold voltage duty cycle table [ (minPulseSeqUsec,dutyCyclePct) ]
|
self.holdDutyPctL= None # hold voltage duty cycle table [ (minPulseSeqUsec,dutyCyclePct) ]
|
||||||
|
self.holdDutyPctD= None # { us:dutyPct } for each us in self.pulseUsL
|
||||||
|
self.silentNoteN = None
|
||||||
self.pulse_idx = 0 # Index of next pulse
|
self.pulse_idx = 0 # Index of next pulse
|
||||||
self.state = None # 'note_on','note_off'
|
self.state = None # 'note_on','note_off'
|
||||||
self.prevHoldDutyPct = None
|
self.prevHoldDutyPct = None
|
||||||
@ -39,11 +42,13 @@ class AttackPulseSeq:
|
|||||||
self.playOnlyFl = False
|
self.playOnlyFl = False
|
||||||
self.rtAnalyzer = RT_Analyzer()
|
self.rtAnalyzer = RT_Analyzer()
|
||||||
|
|
||||||
def start( self, ms, outDir, pitch, pulseUsL, holdDutyPctL, playOnlyFl=False ):
|
def start( self, ms, outDir, pitch, pulseUsL, holdDutyPctL, holdDutyPctD, playOnlyFl=False ):
|
||||||
self.outDir = outDir # directory to write audio file and results
|
self.outDir = outDir # directory to write audio file and results
|
||||||
self.pitch = pitch # note to play
|
self.pitch = pitch # note to play
|
||||||
self.pulseUsL = pulseUsL # one onset pulse length in microseconds per sequence element
|
self.pulseUsL = pulseUsL # one onset pulse length in microseconds per sequence element
|
||||||
self.holdDutyPctL = holdDutyPctL
|
self.holdDutyPctL = holdDutyPctL
|
||||||
|
self.holdDutyPctD = holdDutyPctD
|
||||||
|
self.silentNoteN = 0
|
||||||
self.pulse_idx = 0
|
self.pulse_idx = 0
|
||||||
self.state = 'note_on'
|
self.state = 'note_on'
|
||||||
self.prevHoldDutyPct = None
|
self.prevHoldDutyPct = None
|
||||||
@ -83,18 +88,40 @@ class AttackPulseSeq:
|
|||||||
|
|
||||||
# if waiting to turn a note off
|
# if waiting to turn a note off
|
||||||
elif self.state == 'note_off':
|
elif self.state == 'note_off':
|
||||||
self._note_off(ms)
|
self._note_off(ms)
|
||||||
|
self._count_silent_notes()
|
||||||
|
|
||||||
|
|
||||||
self.pulse_idx += 1
|
self.pulse_idx += 1
|
||||||
|
|
||||||
# if all notes have been played
|
# if all notes have been played
|
||||||
if self.pulse_idx >= len(self.pulseUsL):
|
if self.pulse_idx >= len(self.pulseUsL): # or self.silentNoteN >= self.cfg.maxSilentNoteCount:
|
||||||
self.stop(ms)
|
self.stop(ms)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
assert(0)
|
assert(0)
|
||||||
|
|
||||||
|
def _count_silent_notes( self ):
|
||||||
|
annBegMs = self.eventTimeL[ self.pulse_idx ][0]
|
||||||
|
annEndMs = self.eventTimeL[ self.pulse_idx ][1]
|
||||||
|
minDurMs = self.cfg.silentNoteMinDurMs
|
||||||
|
maxPulseUs = self.cfg.silentNoteMaxPulseUs
|
||||||
|
|
||||||
|
resD = rms_analyze_one_rt_note_wrap( self.audio, annBegMs, annEndMs, self.pitch, self.pauseDurMs, self.cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
|
print( " %4.1f db %4i ms %i" % (resD['hm']['db'], resD['hm']['durMs'], self.pulse_idx))
|
||||||
|
|
||||||
|
if resD is not None and resD['hm']['durMs'] < minDurMs and self.pulseUsL[ self.pulse_idx ] < maxPulseUs:
|
||||||
|
self.silentNoteN += 1
|
||||||
|
print("SILENT", self.silentNoteN)
|
||||||
|
else:
|
||||||
|
self.silentNoteN = 0
|
||||||
|
|
||||||
|
return self.silentNoteN
|
||||||
|
|
||||||
def _get_duty_cycle( self, pulseUsec ):
|
def _get_duty_cycle( self, pulseUsec ):
|
||||||
|
return self.holdDutyPctD[ pulseUsec ]
|
||||||
|
|
||||||
dutyPct = self.holdDutyPctL[0][1]
|
dutyPct = self.holdDutyPctL[0][1]
|
||||||
for refUsec,refDuty in self.holdDutyPctL:
|
for refUsec,refDuty in self.holdDutyPctL:
|
||||||
if pulseUsec < refUsec:
|
if pulseUsec < refUsec:
|
||||||
@ -232,7 +259,7 @@ class CalibrateKeys:
|
|||||||
print(outDir_id,outDir)
|
print(outDir_id,outDir)
|
||||||
|
|
||||||
# if this is not the first time this note has been sampled then get the resample locations
|
# if this is not the first time this note has been sampled then get the resample locations
|
||||||
if outDir_id == 0:
|
if (outDir_id == 0) or self.cfg.useFullPulseListFl:
|
||||||
self.pulseUsL = self.cfg.full_pulseL
|
self.pulseUsL = self.cfg.full_pulseL
|
||||||
else:
|
else:
|
||||||
#self.pulseUsL,_,_ = form_resample_pulse_time_list( outDir, self.cfg.analysisArgs )
|
#self.pulseUsL,_,_ = form_resample_pulse_time_list( outDir, self.cfg.analysisArgs )
|
||||||
@ -252,8 +279,23 @@ class CalibrateKeys:
|
|||||||
if not os.path.isdir(outDir):
|
if not os.path.isdir(outDir):
|
||||||
os.mkdir(outDir)
|
os.mkdir(outDir)
|
||||||
|
|
||||||
|
#------------------------
|
||||||
|
j = 0
|
||||||
|
holdDutyPctD = {}
|
||||||
|
for us in self.pulseUsL:
|
||||||
|
|
||||||
|
if j+1<len(holdDutyPctL) and us >= holdDutyPctL[j+1][0]:
|
||||||
|
j += 1
|
||||||
|
|
||||||
|
|
||||||
|
holdDutyPctD[ us ] = holdDutyPctL[j][1]
|
||||||
|
#------------------------
|
||||||
|
|
||||||
|
if self.cfg.reversePulseListFl:
|
||||||
|
self.pulseUsL = [ us for us in reversed(self.pulseUsL) ]
|
||||||
|
|
||||||
# start the sequencer
|
# start the sequencer
|
||||||
self.seq.start( ms, outDir, pitch, self.pulseUsL, holdDutyPctL, playOnlyFl )
|
self.seq.start( ms, outDir, pitch, self.pulseUsL, holdDutyPctL, holdDutyPctD, playOnlyFl )
|
||||||
|
|
||||||
|
|
||||||
def _calc_next_out_dir_id( self, outDir ):
|
def _calc_next_out_dir_id( self, outDir ):
|
||||||
@ -268,12 +310,13 @@ class CalibrateKeys:
|
|||||||
# This is the main application API it is running in a child process.
|
# This is the main application API it is running in a child process.
|
||||||
class App:
|
class App:
|
||||||
def __init__(self ):
|
def __init__(self ):
|
||||||
self.cfg = None
|
self.cfg = None
|
||||||
self.audioDev = None
|
self.audioDev = None
|
||||||
self.api = None
|
self.api = None
|
||||||
self.cal_keys = None
|
self.cal_keys = None
|
||||||
self.keyboard = None
|
self.keyboard = None
|
||||||
self.calibrate = None
|
self.calibrate = None
|
||||||
|
self.midiFilePlayer = None
|
||||||
|
|
||||||
def setup( self, cfg ):
|
def setup( self, cfg ):
|
||||||
self.cfg = cfg
|
self.cfg = cfg
|
||||||
@ -281,17 +324,22 @@ class App:
|
|||||||
self.audioDev = AudioDevice()
|
self.audioDev = AudioDevice()
|
||||||
self.midiDev = MidiDevice()
|
self.midiDev = MidiDevice()
|
||||||
|
|
||||||
|
res = None
|
||||||
|
|
||||||
#
|
#
|
||||||
# TODO: unify the result error handling
|
# TODO: unify the result error handling
|
||||||
# (the API and the audio device return two diferent 'Result' types
|
# (the API and the audio device return two diferent 'Result' types
|
||||||
#
|
#
|
||||||
|
if hasattr(cfg,'audio'):
|
||||||
res = self.audioDev.setup(**cfg.audio)
|
res = self.audioDev.setup(**cfg.audio)
|
||||||
|
|
||||||
if not res:
|
if not res:
|
||||||
self.audio_dev_list(0)
|
self.audio_dev_list(0)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
self.audioDev = None
|
||||||
|
|
||||||
|
if True:
|
||||||
if hasattr(cfg,'midi'):
|
if hasattr(cfg,'midi'):
|
||||||
res = self.midiDev.setup(**cfg.midi)
|
res = self.midiDev.setup(**cfg.midi)
|
||||||
|
|
||||||
@ -300,28 +348,31 @@ class App:
|
|||||||
else:
|
else:
|
||||||
self.midiDev = None
|
self.midiDev = None
|
||||||
|
|
||||||
self.api = Picadae( key_mapL=cfg.key_mapL)
|
self.api = Picadae( key_mapL=cfg.key_mapL)
|
||||||
|
|
||||||
# wait for the letter 'a' to come back from the serial port
|
# wait for the letter 'a' to come back from the serial port
|
||||||
api_res = self.api.wait_for_serial_sync(timeoutMs=cfg.serial_sync_timeout_ms)
|
api_res = self.api.wait_for_serial_sync(timeoutMs=cfg.serial_sync_timeout_ms)
|
||||||
|
|
||||||
# did the serial port sync fail?
|
# did the serial port sync fail?
|
||||||
if not api_res:
|
if not api_res:
|
||||||
res.set_error("Serial port sync failed.")
|
res.set_error("Serial port sync failed.")
|
||||||
else:
|
else:
|
||||||
print("Serial port sync'ed")
|
print("Serial port sync'ed")
|
||||||
|
|
||||||
self.cal_keys = CalibrateKeys( cfg, self.audioDev, self.api )
|
self.cal_keys = CalibrateKeys( cfg, self.audioDev, self.api )
|
||||||
|
|
||||||
self.keyboard = Keyboard( cfg, self.audioDev, self.api )
|
self.keyboard = Keyboard( cfg, self.audioDev, self.api )
|
||||||
|
|
||||||
self.calibrate = Calibrate( cfg.calibrateArgs, self.audioDev, self.midiDev, self.api )
|
self.calibrate = None #Calibrate( cfg.calibrateArgs, self.audioDev, self.midiDev, self.api )
|
||||||
|
|
||||||
|
self.midiFilePlayer = MidiFilePlayer( cfg, self.api, self.midiDev, cfg.midiFileFn )
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def tick( self, ms ):
|
def tick( self, ms ):
|
||||||
|
|
||||||
self.audioDev.tick(ms)
|
if self.audioDev is not None:
|
||||||
|
self.audioDev.tick(ms)
|
||||||
|
|
||||||
if self.cal_keys:
|
if self.cal_keys:
|
||||||
self.cal_keys.tick(ms)
|
self.cal_keys.tick(ms)
|
||||||
@ -332,6 +383,9 @@ class App:
|
|||||||
if self.calibrate:
|
if self.calibrate:
|
||||||
self.calibrate.tick(ms)
|
self.calibrate.tick(ms)
|
||||||
|
|
||||||
|
if self.midiFilePlayer:
|
||||||
|
self.midiFilePlayer.tick(ms)
|
||||||
|
|
||||||
def audio_dev_list( self, ms ):
|
def audio_dev_list( self, ms ):
|
||||||
portL = self.audioDev.get_port_list( True )
|
portL = self.audioDev.get_port_list( True )
|
||||||
|
|
||||||
@ -382,6 +436,20 @@ class App:
|
|||||||
self.cal_keys.stop(ms)
|
self.cal_keys.stop(ms)
|
||||||
self.keyboard.stop(ms)
|
self.keyboard.stop(ms)
|
||||||
self.calibrate.stop(ms)
|
self.calibrate.stop(ms)
|
||||||
|
|
||||||
|
def midi_file_player_start( self, ms ):
|
||||||
|
self.midiFilePlayer.start(ms)
|
||||||
|
|
||||||
|
def midi_file_player_stop( self, ms ):
|
||||||
|
self.midiFilePlayer.stop(ms)
|
||||||
|
|
||||||
|
def pedal_down( self, ms ):
|
||||||
|
print("pedal_down")
|
||||||
|
self.midiDev.send_controller(64, 100 )
|
||||||
|
|
||||||
|
def pedal_up( self, ms ):
|
||||||
|
print("pedal_up");
|
||||||
|
self.midiDev.send_controller(64, 0 )
|
||||||
|
|
||||||
def quit( self, ms ):
|
def quit( self, ms ):
|
||||||
if self.api:
|
if self.api:
|
||||||
@ -513,6 +581,10 @@ class Shell:
|
|||||||
'r':{ "func":"keyboard_repeat_pulse_idx", "minN":1, "maxN":1, "help":"Repeat pulse index across keyboard with new pulse_idx"},
|
'r':{ "func":"keyboard_repeat_pulse_idx", "minN":1, "maxN":1, "help":"Repeat pulse index across keyboard with new pulse_idx"},
|
||||||
'K':{ "func":"keyboard_start_target_db", "minN":3, "maxN":3, "help":"Play db across keyboard"},
|
'K':{ "func":"keyboard_start_target_db", "minN":3, "maxN":3, "help":"Play db across keyboard"},
|
||||||
'R':{ "func":"keyboard_repeat_target_db", "minN":1, "maxN":1, "help":"Repeat db across keyboard with new pulse_idx"},
|
'R':{ "func":"keyboard_repeat_target_db", "minN":1, "maxN":1, "help":"Repeat db across keyboard with new pulse_idx"},
|
||||||
|
'F':{ "func":"midi_file_player_start", "minN":0, "maxN":0, "help":"Play the MIDI file."},
|
||||||
|
'f':{ "func":"midi_file_player_stop", "minN":0, "maxN":0, "help":"Stop the MIDI file."},
|
||||||
|
'P':{ "func":"pedal_down", "minN":0, "maxN":0, "help":"Pedal down."},
|
||||||
|
'U':{ "func":"pedal_up", "minN":0, "maxN":0, "help":"Pedal up."},
|
||||||
}
|
}
|
||||||
|
|
||||||
def _help( self, _=None ):
|
def _help( self, _=None ):
|
||||||
|
151
p_ac.yml
151
p_ac.yml
@ -3,19 +3,19 @@
|
|||||||
|
|
||||||
|
|
||||||
# Audio device setup
|
# Audio device setup
|
||||||
audio: {
|
audio_off: {
|
||||||
inPortLabel: "5 USB Audio CODEC:", #"HDA Intel PCH: CS4208", # "5 USB Audio CODEC:", #"5 USB Sound Device",
|
inPortLabel: "5 USB Audio CODEC:", #"HDA Intel PCH: CS4208", # "5 USB Audio CODEC:", #"5 USB Sound Device",
|
||||||
outPortLabel: ,
|
outPortLabel: ,
|
||||||
},
|
},
|
||||||
|
|
||||||
midi_off: {
|
midi: {
|
||||||
inMonitorFl: False,
|
inMonitorFl: False,
|
||||||
outMonitorFl: False,
|
outMonitorFl: False,
|
||||||
throughFl: False,
|
throughFl: False,
|
||||||
inPortLabel: "Fastlane:Fastlane MIDI A",
|
#inPortLabel: "Fastlane:Fastlane MIDI A",
|
||||||
outPortLabel: "Fastlane:Fastlane MIDI A"
|
#outPortLabel: "Fastlane:Fastlane MIDI A"
|
||||||
#inPortLabel: "picadae:picadae MIDI 1",
|
inPortLabel: "picadae:picadae MIDI 1",
|
||||||
#outPortLabel: "picadae:picadae MIDI 1"
|
outPortLabel: "picadae:picadae MIDI 1"
|
||||||
},
|
},
|
||||||
|
|
||||||
# Picadae API args
|
# Picadae API args
|
||||||
@ -27,10 +27,19 @@
|
|||||||
|
|
||||||
|
|
||||||
# MeasureSeq args
|
# MeasureSeq args
|
||||||
outDir: "~/temp/p_ac_3e",
|
outDir: "~/temp/p_ac_3g",
|
||||||
noteDurMs: 1000,
|
noteDurMs: 500,
|
||||||
pauseDurMs: 1000,
|
pauseDurMs: 500,
|
||||||
#holdDutyPctL: [ [0,50], [22000,55] ],
|
reversePulseListFl: True,
|
||||||
|
useFullPulseListFl: True,
|
||||||
|
maxSilentNoteCount: 4,
|
||||||
|
silentNoteMaxPulseUs: 15000,
|
||||||
|
silentNoteMinDurMs: 250,
|
||||||
|
|
||||||
|
# Midi file player
|
||||||
|
midiFileFn: "/home/kevin/media/audio/midi/txt/round4.txt",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
full_pulse0L: [ 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500, 7000, 8000, 9000, 10000, 12000, 14000, 18000, 22000, 26000, 30000, 34000, 40000],
|
full_pulse0L: [ 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500, 7000, 8000, 9000, 10000, 12000, 14000, 18000, 22000, 26000, 30000, 34000, 40000],
|
||||||
full_pulse1L: [ 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 20000, 22000, 24000, 26000, 30000, 32000, 34000, 36000, 40000],
|
full_pulse1L: [ 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 20000, 22000, 24000, 26000, 30000, 32000, 34000, 36000, 40000],
|
||||||
@ -44,11 +53,18 @@
|
|||||||
|
|
||||||
full_pulse6L: [ 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 ],
|
full_pulse6L: [ 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 ],
|
||||||
|
|
||||||
full_pulseL: [ 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 ],
|
full_pulseMainL: [ 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 ],
|
||||||
|
|
||||||
full_pulse7L: [ 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 ],
|
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 ],
|
||||||
|
|
||||||
|
|
||||||
full_pulse9L: [ 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 ],
|
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],
|
||||||
|
|
||||||
|
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 ],
|
||||||
|
|
||||||
|
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 ],
|
||||||
|
|
||||||
|
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 ],
|
||||||
|
|
||||||
# RMS analysis args
|
# RMS analysis args
|
||||||
analysisArgs: {
|
analysisArgs: {
|
||||||
@ -61,8 +77,8 @@
|
|||||||
durDecayPct: 40, # percent drop in RMS to indicate the end of a note
|
durDecayPct: 40, # percent drop in RMS to indicate the end of a note
|
||||||
},
|
},
|
||||||
|
|
||||||
resampleMinDb: 10.0, # note's less than this will be skipped
|
resampleMinDb: 7.0, # note's less than this will be skipped
|
||||||
resampleNoiseLimitPct: 1.0, #
|
resampleNoiseLimitPct: 5.0, #
|
||||||
resampleMinDurMs: 800, # notes's whose duration is less than this will be skipped
|
resampleMinDurMs: 800, # notes's whose duration is less than this will be skipped
|
||||||
|
|
||||||
minAttkDb: 7.0, # threshold of silence level
|
minAttkDb: 7.0, # threshold of silence level
|
||||||
@ -76,11 +92,98 @@
|
|||||||
rmsAnalysisCacheFn: "/home/kevin/temp/rms_analysis_cache.pickle"
|
rmsAnalysisCacheFn: "/home/kevin/temp/rms_analysis_cache.pickle"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
manualMinD: {
|
||||||
|
23: [2, 24],
|
||||||
|
24: [2, 18],
|
||||||
|
25: [2, 41],
|
||||||
|
26: [2, 26], #
|
||||||
|
27: [2, 35], # (36 is an outlier)
|
||||||
|
28: [2, 35], # /36 (resample?)
|
||||||
|
29: [2, 22], # /23 (resample?)
|
||||||
|
30: [2, 28], # /29
|
||||||
|
31: [2, 39], #
|
||||||
|
32: [2, 27], #
|
||||||
|
33: [2, 10], #
|
||||||
|
34: [2, 27], # (29 outlier)
|
||||||
|
35: [2, 15], #
|
||||||
|
36: [2, 16], # ngz: (0 32 36) (1 31 36)
|
||||||
|
37: [2, 18], #
|
||||||
|
38: [2, 33], #
|
||||||
|
39: [2, 18], #
|
||||||
|
40: [2, 6], # ngz: (0 25 41)
|
||||||
|
41: [2, 22], # ngz: (2 9 22)
|
||||||
|
42: [2, 11], #
|
||||||
|
43: [2, 7], #(8 outlier)], #
|
||||||
|
44: [2, 19],
|
||||||
|
45: [4, 7], # 5 sample traes
|
||||||
|
46: [2, 4],
|
||||||
|
47: [2, 11], # /12
|
||||||
|
48: [2, 27], # /28
|
||||||
|
49: [2, 12],
|
||||||
|
50: [2, 6],
|
||||||
|
51: [2, 14],
|
||||||
|
52: [2, 26],
|
||||||
|
53: [3, 24 ], # ngz at onset
|
||||||
|
54: [2, 21], # /22
|
||||||
|
55: [2, 10], # /11
|
||||||
|
56: [2, 5],
|
||||||
|
57: [2, 6],
|
||||||
|
58: [2, 11],
|
||||||
|
59: [2, 5],
|
||||||
|
60: [2, 13],
|
||||||
|
61: [4, 5],
|
||||||
|
62: [2, 7],
|
||||||
|
63: [2, 12],
|
||||||
|
64: [3, 33],
|
||||||
|
65: [2, 23],
|
||||||
|
66: [2, 36],
|
||||||
|
67: [2, 16],
|
||||||
|
68: [2, 1], # needs decreased start us
|
||||||
|
69: [1, 7],
|
||||||
|
70: [2, 34],
|
||||||
|
71: [2, 23],
|
||||||
|
72: [2, 14],
|
||||||
|
73: [2, 30],
|
||||||
|
74: [2, 26],
|
||||||
|
75: [2, 31],
|
||||||
|
76: [2, 20],
|
||||||
|
77: [2, 28],
|
||||||
|
78: [2, 28],
|
||||||
|
79: [2, 44],
|
||||||
|
80: [2, 25],
|
||||||
|
81: [2, 36],
|
||||||
|
82: [2, 51], # incorrect hold voltages (resample)
|
||||||
|
83: [2, 43],
|
||||||
|
84: [2, 38],
|
||||||
|
85: [2, 27],
|
||||||
|
86: [2, 43],
|
||||||
|
87: [2, 33],
|
||||||
|
88: [2, 42],
|
||||||
|
89: [3, 21], # ngz (3 15 19)
|
||||||
|
91: [2, 4], # bad samples (resample)
|
||||||
|
92: [2, 10],
|
||||||
|
93: [2, 42],
|
||||||
|
94: [2, 39],
|
||||||
|
95: [2, 19],
|
||||||
|
96: [2, 1], # needs decreaed start us ngz: (0 22 38)
|
||||||
|
97: [2, 51],
|
||||||
|
98: [2, 30],
|
||||||
|
99: [2, 41],
|
||||||
|
100: [2, 24],
|
||||||
|
101: [2, 39],
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
manualAnchorPitchMinDbL: [ 23, 27, 31, 34, 44, 51, 61, 70, 74, 81, 87, 93, 96, 101 ],
|
||||||
|
manualAnchorPitchMaxDbL: [ 23, 32, 49, 57, 67, 76, 83, 93, 99, 101 ],
|
||||||
|
|
||||||
calibrateArgs: {
|
calibrateArgs: {
|
||||||
|
|
||||||
outDir: "~/temp/calib0",
|
outDir: "~/temp/calib2",
|
||||||
outLabel: "test_1",
|
outLabel: "test_3",
|
||||||
|
|
||||||
|
inDir: "~/temp/p_ac_3f",
|
||||||
|
|
||||||
analysisD: {
|
analysisD: {
|
||||||
rmsWndMs: 300, # length of the RMS measurment window
|
rmsWndMs: 300, # length of the RMS measurment window
|
||||||
@ -91,21 +194,23 @@
|
|||||||
durDecayPct: 40 # percent drop in RMS to indicate the end of a note
|
durDecayPct: 40 # percent drop in RMS to indicate the end of a note
|
||||||
},
|
},
|
||||||
|
|
||||||
noteOnDurMs: 1000,
|
noteOnDurMs: 500,
|
||||||
noteOffDurMs: 1000,
|
noteOffDurMs: 500,
|
||||||
|
|
||||||
|
|
||||||
pitchL: [ 44, 45, 46, 47, 48, 49, 50, 51 ], # list of pitches
|
#pitchL: [ 31, 33, 34, 35 ], # list of pitches
|
||||||
targetDbL: [ 16, 17, 18, 19, 20, 21, 22, 23, 24 ], # list of target db
|
#pitchL: [ 80,81,82 ], # 8
|
||||||
|
pitchL: [ 40,41,42 ], # 12
|
||||||
|
targetDbL: [ 13 ], # list of target db
|
||||||
|
|
||||||
minMeasDurMs: 800, # minimum candidate note duration
|
minMeasDurMs: 140, # minimum candidate note duration
|
||||||
tolDbPct: 2.0, # tolerance as a percent of targetDb above/below used to form match db window
|
tolDbPct: 2.0, # tolerance as a percent of targetDb above/below used to form match db window
|
||||||
maxPulseUs: 45000, # max. allowable pulse us
|
maxPulseUs: 45000, # max. allowable pulse us
|
||||||
minPulseUs: 8000, # min. allowable pulse us
|
minPulseUs: 8000, # min. allowable pulse us
|
||||||
initPulseUs: 15000, # pulseUs for first note
|
initPulseUs: 15000, # pulseUs for first note
|
||||||
minMatchN: 3, # at least 3 candidate notes must be within tolDbPct to move on to a new targetDb
|
minMatchN: 3, # at least 3 candidate notes must be within tolDbPct to move on to a new targetDb
|
||||||
maxAttemptN: 30, # give up if more than 20 candidate notes fail for a given targetDb
|
maxAttemptN: 30, # give up if more than 20 candidate notes fail for a given targetDb
|
||||||
dbSrcLabel: 'td', # source of the db measurement 'td' (time-domain) or 'hm' (harmonic)
|
dbSrcLabel: 'hm', # source of the db measurement 'td' (time-domain) or 'hm' (harmonic)
|
||||||
|
|
||||||
holdDutyPctD: {
|
holdDutyPctD: {
|
||||||
23: [[0, 70]],
|
23: [[0, 70]],
|
||||||
|
@ -143,7 +143,7 @@ if __name__ == "__main__":
|
|||||||
inDir = sys.argv[1]
|
inDir = sys.argv[1]
|
||||||
yamlFn = sys.argv[2]
|
yamlFn = sys.argv[2]
|
||||||
if len(sys.argv) > 3:
|
if len(sys.argv) > 3:
|
||||||
pitch = int(sys.argv[2])
|
pitch = int(sys.argv[3])
|
||||||
|
|
||||||
keyInfoD = key_info_dictionary( yamlCfgFn=yamlFn)
|
keyInfoD = key_info_dictionary( yamlCfgFn=yamlFn)
|
||||||
#plot_all_notes( inDir )
|
#plot_all_notes( inDir )
|
||||||
|
650
plot_seq_1.py
650
plot_seq_1.py
@ -1,31 +1,62 @@
|
|||||||
import os, sys
|
import os, sys,json
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from common import parse_yaml_cfg
|
from common import parse_yaml_cfg
|
||||||
import rms_analysis
|
import rms_analysis
|
||||||
|
import elbow
|
||||||
|
|
||||||
|
def fit_to_reference( pkL, refTakeId ):
|
||||||
|
|
||||||
|
us_outL = []
|
||||||
|
db_outL = []
|
||||||
|
dur_outL = []
|
||||||
|
tid_outL = []
|
||||||
|
|
||||||
|
dbL,usL,durMsL,takeIdL = tuple(zip(*pkL))
|
||||||
|
|
||||||
|
us_refL,db_refL,dur_refL = zip(*[(usL[i],dbL[i],durMsL[i]) for i in range(len(usL)) if takeIdL[i]==refTakeId])
|
||||||
|
|
||||||
|
|
||||||
|
for takeId in set(takeIdL):
|
||||||
|
us0L,db0L,dur0L = zip(*[(usL[i],dbL[i],durMsL[i]) for i in range(len(usL)) if takeIdL[i]==takeId ])
|
||||||
|
|
||||||
|
|
||||||
|
if takeId == refTakeId:
|
||||||
|
db_outL += db0L
|
||||||
|
else:
|
||||||
|
db1V = elbow.fit_points_to_reference(us0L,db0L,us_refL,db_refL)
|
||||||
|
db_outL += db1V.tolist()
|
||||||
|
|
||||||
|
us_outL += us0L
|
||||||
|
dur_outL+= dur0L
|
||||||
|
tid_outL+= [takeId] * len(us0L)
|
||||||
|
|
||||||
|
return zip(db_outL,us_outL,dur_outL,tid_outL)
|
||||||
|
|
||||||
|
|
||||||
def get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD ):
|
def get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD ):
|
||||||
|
|
||||||
inDir = os.path.join(inDir,"%i" % (midi_pitch))
|
inDir = os.path.join(inDir,"%i" % (midi_pitch))
|
||||||
|
|
||||||
dirL = os.listdir(inDir)
|
takeDirL = os.listdir(inDir)
|
||||||
|
|
||||||
pkL = []
|
pkL = []
|
||||||
|
|
||||||
# for each take in this directory
|
usRefL = None
|
||||||
for idir in dirL:
|
dbRefL = None
|
||||||
|
|
||||||
take_number = int(idir)
|
# for each take in this directory
|
||||||
|
for take_number in range(len(takeDirL)):
|
||||||
|
|
||||||
# analyze this takes audio and locate the note peaks
|
# analyze this takes audio and locate the note peaks
|
||||||
r = rms_analysis.rms_analysis_main( os.path.join(inDir,idir), midi_pitch, **analysisArgsD )
|
r = rms_analysis.rms_analysis_main( os.path.join(inDir,str(take_number)), midi_pitch, **analysisArgsD )
|
||||||
|
|
||||||
# store the peaks in pkL[ (db,us) ]
|
# store the peaks in pkL[ (db,us) ]
|
||||||
for db,us,stats in zip(r.pkDbL,r.pkUsL,r.statsL):
|
for db,us,stats in zip(r.pkDbL,r.pkUsL,r.statsL):
|
||||||
pkL.append( (db,us,stats.durMs,take_number) )
|
pkL.append( (db,us,stats.durMs,take_number) )
|
||||||
|
|
||||||
|
pkL = fit_to_reference( pkL, 0 )
|
||||||
|
|
||||||
# sort the peaks on increasing attack pulse microseconds
|
# sort the peaks on increasing attack pulse microseconds
|
||||||
pkL = sorted( pkL, key= lambda x: x[1] )
|
pkL = sorted( pkL, key= lambda x: x[1] )
|
||||||
|
|
||||||
@ -37,22 +68,34 @@ def get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD ):
|
|||||||
|
|
||||||
return pkUsL,pkDbL,durMsL,takeIdL,r.holdDutyPctL
|
return pkUsL,pkDbL,durMsL,takeIdL,r.holdDutyPctL
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def select_resample_reference_indexes( noiseIdxL ):
|
def select_resample_reference_indexes( noiseIdxL ):
|
||||||
|
|
||||||
resampleIdxS = set()
|
resampleIdxS = set()
|
||||||
|
|
||||||
|
# for each noisy sample index store that index and the index
|
||||||
|
# before and after it
|
||||||
for i in noiseIdxL:
|
for i in noiseIdxL:
|
||||||
resampleIdxS.add( i )
|
resampleIdxS.add( i )
|
||||||
resampleIdxS.add( i+1 )
|
if i+1 < len(noiseIdxL):
|
||||||
resampleIdxS.add( i-1 )
|
resampleIdxS.add( i+1 )
|
||||||
|
if i-1 >= 0:
|
||||||
|
resampleIdxS.add( i-1 )
|
||||||
|
|
||||||
resampleIdxL = list(resampleIdxS)
|
resampleIdxL = list(resampleIdxS)
|
||||||
|
|
||||||
# if a single sample point is left out of a region of
|
# if a single sample point is left out of a region of
|
||||||
# contiguous sample points then include this as a resample point
|
# contiguous sample points then include this as a resample point also
|
||||||
for i in resampleIdxL:
|
for i in resampleIdxL:
|
||||||
if i + 1 not in resampleIdxL and i + 2 in resampleIdxL: # BUG BUG BUG: Hardcoded constant
|
if i + 1 not in resampleIdxL and i + 2 in resampleIdxL: # BUG BUG BUG: Hardcoded constant
|
||||||
resampleIdxL.append(i+1)
|
if i+1 < len(noiseIdxL):
|
||||||
|
resampleIdxL.append(i+1)
|
||||||
|
|
||||||
return resampleIdxL
|
return resampleIdxL
|
||||||
|
|
||||||
@ -99,11 +142,13 @@ def locate_resample_regions( usL, dbL, resampleIdxL ):
|
|||||||
|
|
||||||
return reUsL,reDbL
|
return reUsL,reDbL
|
||||||
|
|
||||||
def get_dur_skip_indexes( durMsL, dbL, takeIdL, minDurMs, minDb ):
|
def get_dur_skip_indexes( durMsL, dbL, takeIdL, scoreL, minDurMs, minDb, noiseLimitPct ):
|
||||||
|
|
||||||
firstAudibleIdx = None
|
firstAudibleIdx = None
|
||||||
firstNonSkipIdx = None
|
firstNonSkipIdx = None
|
||||||
skipIdxL = [ i for i,(ms,db) in enumerate(zip(durMsL,dbL)) if ms < minDurMs or db < minDb ]
|
|
||||||
|
# get the indexes of samples which do not meet the duration, db level, or noise criteria
|
||||||
|
skipIdxL = [ i for i,(ms,db,score) in enumerate(zip(durMsL,dbL,scoreL)) if ms < minDurMs or db < minDb or score > noiseLimitPct ]
|
||||||
|
|
||||||
# if a single sample point is left out of a region of
|
# if a single sample point is left out of a region of
|
||||||
# contiguous skipped points then skip this point also
|
# contiguous skipped points then skip this point also
|
||||||
@ -143,18 +188,22 @@ def get_dur_skip_indexes( durMsL, dbL, takeIdL, minDurMs, minDb ):
|
|||||||
|
|
||||||
def get_resample_points( usL, dbL, durMsL, takeIdL, minDurMs, minDb, noiseLimitPct ):
|
def get_resample_points( usL, dbL, durMsL, takeIdL, minDurMs, minDb, noiseLimitPct ):
|
||||||
|
|
||||||
skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, takeIdL, minDurMs, minDb )
|
|
||||||
|
|
||||||
durL = [ (usL[i],dbL[i]) for i in skipIdxL ]
|
|
||||||
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
||||||
|
|
||||||
|
skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, takeIdL, scoreV.tolist(), minDurMs, minDb, noiseLimitPct )
|
||||||
|
|
||||||
|
skipL = [ (usL[i],dbL[i]) for i in skipIdxL ]
|
||||||
noiseIdxL = [ i for i in range(scoreV.shape[0]) if scoreV[i] > noiseLimitPct ]
|
noiseIdxL = [ i for i in range(scoreV.shape[0]) if scoreV[i] > noiseLimitPct ]
|
||||||
noiseL = [ (usL[i],dbL[i]) for i in noiseIdxL ]
|
noiseL = [ (usL[i],dbL[i]) for i in noiseIdxL ]
|
||||||
resampleIdxL = select_resample_reference_indexes( noiseIdxL )
|
resampleIdxL = select_resample_reference_indexes( noiseIdxL )
|
||||||
resampleIdxL = [ i for i in resampleIdxL if i >= firstNonSkipIdx ]
|
|
||||||
|
if firstNonSkipIdx is not None:
|
||||||
|
resampleIdxL = [ i for i in resampleIdxL if i >= firstNonSkipIdx ]
|
||||||
|
|
||||||
resampleL = [ (usL[i],dbL[i]) for i in resampleIdxL ]
|
resampleL = [ (usL[i],dbL[i]) for i in resampleIdxL ]
|
||||||
reUsL,reDbL = locate_resample_regions( usL, dbL, resampleIdxL )
|
reUsL,reDbL = locate_resample_regions( usL, dbL, resampleIdxL )
|
||||||
|
|
||||||
return reUsL, reDbL, noiseL, resampleL, durL, firstAudibleIdx, firstNonSkipIdx
|
return reUsL, reDbL, noiseL, resampleL, skipL, firstAudibleIdx, firstNonSkipIdx
|
||||||
|
|
||||||
def get_resample_points_wrap( inDir, midi_pitch, analysisArgsD ):
|
def get_resample_points_wrap( inDir, midi_pitch, analysisArgsD ):
|
||||||
|
|
||||||
@ -164,46 +213,75 @@ def get_resample_points_wrap( inDir, midi_pitch, analysisArgsD ):
|
|||||||
|
|
||||||
return reUsL
|
return reUsL
|
||||||
|
|
||||||
def plot_noise_region( ax, inDir, keyMapD, midi_pitch, analysisArgsD ):
|
|
||||||
|
|
||||||
plotResampleFl = False
|
|
||||||
plotTakesFl = True
|
def plot_us_db_curves( ax, inDir, keyMapD, midi_pitch, analysisArgsD, plotResamplePointsFl=False, plotTakesFl=True, usMax=None ):
|
||||||
|
|
||||||
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'] )
|
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'] )
|
||||||
|
reUsL, reDbL, noiseL, resampleL, skipL, firstAudibleIdx, firstNonSkipIdx = get_resample_points( usL, dbL, durMsL, takeIdL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
|
||||||
reUsL, reDbL, noiseL, resampleL, durL, firstAudibleIdx, firstNonSkipIdx = get_resample_points( usL, dbL, durMsL, takeIdL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
|
|
||||||
|
|
||||||
# plot first audible and non-skip position
|
# plot first audible and non-skip position
|
||||||
ax.plot( usL[firstNonSkipIdx], dbL[firstNonSkipIdx], markersize=15, marker='+', linestyle='None', color='red')
|
if False:
|
||||||
ax.plot( usL[firstNonSkipIdx], dbL[firstAudibleIdx], markersize=15, marker='*', linestyle='None', color='red')
|
|
||||||
|
|
||||||
# plot the resample points
|
if firstNonSkipIdx is not None:
|
||||||
if plotResampleFl:
|
ax.plot( usL[firstNonSkipIdx], dbL[firstNonSkipIdx], markersize=15, marker='+', linestyle='None', color='red')
|
||||||
ax.plot( reUsL, reDbL, markersize=10, marker='x', linestyle='None', color='green')
|
|
||||||
|
|
||||||
# plot the noisy sample positions
|
if firstAudibleIdx is not None:
|
||||||
if noiseL:
|
ax.plot( usL[firstAudibleIdx], dbL[firstAudibleIdx], markersize=15, marker='*', linestyle='None', color='red')
|
||||||
nUsL,nDbL = zip(*noiseL)
|
|
||||||
ax.plot( nUsL, nDbL, marker='o', linestyle='None', color='black')
|
|
||||||
|
|
||||||
# plot the noisy sample positions and the neighbors included in the noisy region
|
|
||||||
if resampleL:
|
|
||||||
nUsL,nDbL = zip(*resampleL)
|
|
||||||
ax.plot( nUsL, nDbL, marker='*', linestyle='None', color='red')
|
|
||||||
|
|
||||||
|
# plot the resample points
|
||||||
|
if plotResamplePointsFl:
|
||||||
|
ax.plot( reUsL, reDbL, markersize=13, marker='x', linestyle='None', color='green')
|
||||||
|
|
||||||
|
# plot the noisy sample positions
|
||||||
|
if noiseL:
|
||||||
|
nUsL,nDbL = zip(*noiseL)
|
||||||
|
ax.plot( nUsL, nDbL, marker='o', markersize=9, linestyle='None', color='black')
|
||||||
|
|
||||||
|
# plot the noisy sample positions and the neighbors included in the noisy region
|
||||||
|
if resampleL:
|
||||||
|
nUsL,nDbL = zip(*resampleL)
|
||||||
|
ax.plot( nUsL, nDbL, marker='+', markersize=8, linestyle='None', color='red')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# plot actual sample points
|
# plot actual sample points
|
||||||
|
|
||||||
|
elbow_us = None
|
||||||
|
elbow_db = None
|
||||||
|
elbow_len = None
|
||||||
|
|
||||||
|
usL,dbL,takeIdL = zip(*[(us,dbL[i],takeIdL[i]) for i,us in enumerate(usL) if usMax is None or us <= usMax])
|
||||||
|
|
||||||
if plotTakesFl:
|
if plotTakesFl:
|
||||||
for takeId in list(set(takeIdL)):
|
for takeId in list(set(takeIdL)):
|
||||||
|
|
||||||
|
# get the us,db points included in this take
|
||||||
xL,yL = zip(*[(usL[i],dbL[i]) for i in range(len(usL)) if takeIdL[i]==takeId ])
|
xL,yL = zip(*[(usL[i],dbL[i]) for i in range(len(usL)) if takeIdL[i]==takeId ])
|
||||||
ax.plot(xL,yL, marker='.')
|
|
||||||
|
ax.plot(xL,yL, marker='.',label=takeId)
|
||||||
|
|
||||||
for i,(x,y) in enumerate(zip(xL,yL)):
|
for i,(x,y) in enumerate(zip(xL,yL)):
|
||||||
ax.text(x,y,str(i))
|
ax.text(x,y,str(i))
|
||||||
|
|
||||||
|
|
||||||
|
#if elbow_len is None or len(xL) > elbow_len:
|
||||||
|
if takeId+1 == len(set(takeIdL)):
|
||||||
|
elbow_us,elbow_db = elbow.find_elbow(xL,yL)
|
||||||
|
elbow_len = len(xL)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
ax.plot(usL, dbL, marker='.')
|
ax.plot(usL, dbL, marker='.')
|
||||||
|
|
||||||
# plot the duration skip points
|
ax.plot([elbow_us],[elbow_db],marker='*',markersize=12,color='red',linestyle='None')
|
||||||
if durL:
|
|
||||||
nUsL,nDbL = zip(*durL)
|
# plot the skip points in yellow
|
||||||
ax.plot( nUsL, nDbL, marker='.', linestyle='None', color='yellow')
|
if False:
|
||||||
|
if skipL:
|
||||||
|
nUsL,nDbL = zip(*skipL)
|
||||||
|
ax.plot( nUsL, nDbL, marker='.', linestyle='None', color='yellow')
|
||||||
|
|
||||||
# plot the locations where the hold duty cycle changes with vertical black lines
|
# plot the locations where the hold duty cycle changes with vertical black lines
|
||||||
for us_duty in holdDutyPctL:
|
for us_duty in holdDutyPctL:
|
||||||
@ -213,36 +291,504 @@ def plot_noise_region( ax, inDir, keyMapD, midi_pitch, analysisArgsD ):
|
|||||||
|
|
||||||
# plot the 'minDb' reference line
|
# plot the 'minDb' reference line
|
||||||
ax.axhline(analysisArgsD['resampleMinDb'] ,color='black')
|
ax.axhline(analysisArgsD['resampleMinDb'] ,color='black')
|
||||||
|
|
||||||
|
if os.path.isfile("minInterpDb.json"):
|
||||||
|
with open("minInterpDb.json","r") as f:
|
||||||
|
r = json.load(f)
|
||||||
|
if midi_pitch in r['pitchL']:
|
||||||
|
ax.axhline( r['minDbL'][ r['pitchL'].index(midi_pitch) ], color='blue' )
|
||||||
|
ax.axhline( r['maxDbL'][ r['pitchL'].index(midi_pitch) ], color='blue' )
|
||||||
|
|
||||||
ax.set_ylabel( "%i %s %s" % (midi_pitch, keyMapD[midi_pitch]['type'],keyMapD[midi_pitch]['class']))
|
ax.set_ylabel( "%i %s %s" % (midi_pitch, keyMapD[midi_pitch]['type'],keyMapD[midi_pitch]['class']))
|
||||||
|
|
||||||
def plot_noise_regions_main( inDir, cfg, pitchL ):
|
def plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True, usMax=None ):
|
||||||
|
|
||||||
analysisArgsD = cfg.analysisArgs
|
analysisArgsD = cfg.analysisArgs
|
||||||
keyMapD = { d['midi']:d for d in cfg.key_mapL }
|
keyMapD = { d['midi']:d for d in cfg.key_mapL }
|
||||||
axN = len(pitchL)
|
axN = len(pitchL)
|
||||||
fig,axL = plt.subplots(axN,1)
|
fig,axL = plt.subplots(axN,1,sharex=True)
|
||||||
if axN == 1:
|
if axN == 1:
|
||||||
axL = [axL]
|
axL = [axL]
|
||||||
fig.set_size_inches(18.5, 10.5*axN)
|
fig.set_size_inches(18.5, 10.5*axN)
|
||||||
|
|
||||||
for ax,midi_pitch in zip(axL,pitchL):
|
for ax,midi_pitch in zip(axL,pitchL):
|
||||||
plot_noise_region( ax,inDir, cfg.key_mapL, midi_pitch, analysisArgsD )
|
plot_us_db_curves( ax,inDir, keyMapD, midi_pitch, analysisArgsD, plotTakesFl=plotTakesFl, usMax=usMax )
|
||||||
|
|
||||||
|
if plotTakesFl:
|
||||||
|
plt.legend()
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def plot_all_noise_curves( inDir, cfg, pitchL=None ):
|
||||||
|
|
||||||
|
pitchFolderL = os.listdir(inDir)
|
||||||
|
|
||||||
|
if pitchL is None:
|
||||||
|
pitchL = [ int( int(pitchFolder) ) for pitchFolder in pitchFolderL ]
|
||||||
|
|
||||||
|
fig,ax = plt.subplots()
|
||||||
|
|
||||||
|
for midi_pitch in pitchL:
|
||||||
|
|
||||||
|
print(midi_pitch)
|
||||||
|
|
||||||
|
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
|
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
||||||
|
|
||||||
|
minDurMs = cfg.analysisArgs['resampleMinDurMs']
|
||||||
|
minDb = cfg.analysisArgs['resampleMinDb'],
|
||||||
|
noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
|
||||||
|
|
||||||
|
skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, scoreV.tolist(), takeIdL, minDurMs, minDb, noiseLimitPct )
|
||||||
|
|
||||||
|
|
||||||
|
if False:
|
||||||
|
ax.plot( usL[firstAudibleIdx], scoreV[firstAudibleIdx], markersize=10, marker='*', linestyle='None', color='red')
|
||||||
|
ax.plot( usL, scoreV, label="%i"%(midi_pitch) )
|
||||||
|
ax.set_xlabel('us')
|
||||||
|
|
||||||
|
else:
|
||||||
|
xL = [ (score,db,i) for i,(score,db) in enumerate(zip(scoreV,dbL)) ]
|
||||||
|
xL = sorted(xL, key=lambda x: x[1] )
|
||||||
|
|
||||||
|
scoreV,dbL,idxL = zip(*xL)
|
||||||
|
ax.plot( dbL[idxL[firstAudibleIdx]], scoreV[idxL[firstAudibleIdx]], markersize=10, marker='*', linestyle='None', color='red')
|
||||||
|
ax.plot( dbL, scoreV, label="%i"%(midi_pitch) )
|
||||||
|
ax.set_xlabel('db')
|
||||||
|
|
||||||
|
ax.set_ylabel("noise db %")
|
||||||
|
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2 ):
|
||||||
|
|
||||||
|
pitchFolderL = os.listdir(inDir)
|
||||||
|
|
||||||
|
if pitchL is None:
|
||||||
|
pitchL = [ int( int(pitchFolder) ) for pitchFolder in pitchFolderL ]
|
||||||
|
|
||||||
|
|
||||||
|
okL = []
|
||||||
|
outPitchL = []
|
||||||
|
minDbL = []
|
||||||
|
maxDbL = []
|
||||||
|
for midi_pitch in pitchL:
|
||||||
|
|
||||||
|
print(midi_pitch)
|
||||||
|
|
||||||
|
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
|
okL.append(False)
|
||||||
|
|
||||||
|
takeId = len(set(takeIdL))-1
|
||||||
|
|
||||||
|
db_maxL = sorted(dbL)
|
||||||
|
maxDbL.append( np.mean(db_maxL[-5:]) )
|
||||||
|
|
||||||
|
|
||||||
|
usL,dbL = zip(*[(usL[i],dbL[i]) for i in range(len(usL)) if takeIdL[i]==takeId ])
|
||||||
|
|
||||||
|
if len(set(takeIdL)) == 3:
|
||||||
|
okL[-1] = True
|
||||||
|
|
||||||
|
elbow_us,elbow_db = elbow.find_elbow(usL,dbL)
|
||||||
|
minDbL.append(elbow_db)
|
||||||
|
outPitchL.append(midi_pitch)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
p_dL = sorted( zip(outPitchL,minDbL,maxDbL,okL), key=lambda x: x[0] )
|
||||||
|
outPitchL,minDbL,maxDbL,okL = zip(*p_dL)
|
||||||
|
|
||||||
|
fig,ax = plt.subplots()
|
||||||
|
ax.plot(outPitchL,minDbL)
|
||||||
|
ax.plot(outPitchL,maxDbL)
|
||||||
|
|
||||||
|
keyMapD = { d['midi']:d for d in cfg.key_mapL }
|
||||||
|
for pitch,min_db,max_db,okFl in zip(outPitchL,minDbL,maxDbL,okL):
|
||||||
|
c = 'black' if okFl else 'red'
|
||||||
|
ax.text( pitch, min_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
|
||||||
|
ax.text( pitch, max_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
|
||||||
|
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
def plot_min_db_manual( inDir, cfg ):
|
||||||
|
|
||||||
|
pitchL = list(cfg.manualMinD.keys())
|
||||||
|
|
||||||
|
outPitchL = []
|
||||||
|
maxDbL = []
|
||||||
|
minDbL = []
|
||||||
|
okL = []
|
||||||
|
anchorMinDbL = []
|
||||||
|
anchorMaxDbL = []
|
||||||
|
|
||||||
|
for midi_pitch in pitchL:
|
||||||
|
|
||||||
|
manual_take_id = cfg.manualMinD[midi_pitch][0]
|
||||||
|
manual_sample_idx = cfg.manualMinD[midi_pitch][1]
|
||||||
|
|
||||||
|
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
|
okL.append(False)
|
||||||
|
|
||||||
|
takeId = len(set(takeIdL))-1
|
||||||
|
|
||||||
|
# maxDb is computed on all takes (not just the specified take)
|
||||||
|
db_maxL = sorted(dbL)
|
||||||
|
max_db = np.mean(db_maxL[-4:])
|
||||||
|
maxDbL.append( max_db )
|
||||||
|
|
||||||
|
# get the us,db values for the specified take
|
||||||
|
usL,dbL = zip(*[(usL[i],dbL[i]) for i in range(len(usL)) if takeIdL[i]==manual_take_id ])
|
||||||
|
|
||||||
|
# most pitches have 3 sample takes that do not
|
||||||
|
if len(set(takeIdL)) == 3 and manual_take_id == takeId:
|
||||||
|
okL[-1] = True
|
||||||
|
|
||||||
|
# min db from the sample index manually specified in cfg
|
||||||
|
manualMinDb = dbL[ manual_sample_idx ]
|
||||||
|
|
||||||
|
minDbL.append( manualMinDb )
|
||||||
|
outPitchL.append(midi_pitch)
|
||||||
|
|
||||||
|
|
||||||
|
if midi_pitch in cfg.manualAnchorPitchMinDbL:
|
||||||
|
anchorMinDbL.append( manualMinDb )
|
||||||
|
|
||||||
|
if midi_pitch in cfg.manualAnchorPitchMaxDbL:
|
||||||
|
anchorMaxDbL.append( max_db )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Form the complete set of min/max db levels for each pitch by interpolating the
|
||||||
|
# db values between the manually selected anchor points.
|
||||||
|
interpMinDbL = np.interp( pitchL, cfg.manualAnchorPitchMinDbL, anchorMinDbL )
|
||||||
|
interpMaxDbL = np.interp( pitchL, cfg.manualAnchorPitchMaxDbL, anchorMaxDbL )
|
||||||
|
|
||||||
|
fig,ax = plt.subplots()
|
||||||
|
|
||||||
|
|
||||||
|
ax.plot(outPitchL,minDbL) # plot the manually selected minDb values
|
||||||
|
ax.plot(outPitchL,maxDbL) # plot the max db values
|
||||||
|
|
||||||
|
# plot the interpolated minDb/maxDb values
|
||||||
|
ax.plot(pitchL,interpMinDbL)
|
||||||
|
ax.plot(pitchL,interpMaxDbL)
|
||||||
|
|
||||||
|
|
||||||
|
keyMapD = { d['midi']:d for d in cfg.key_mapL }
|
||||||
|
for pitch,min_db,max_db,okFl in zip(outPitchL,minDbL,maxDbL,okL):
|
||||||
|
c = 'black' if okFl else 'red'
|
||||||
|
ax.text( pitch, min_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
|
||||||
|
ax.text( pitch, max_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
|
||||||
|
|
||||||
|
with open("minInterpDb.json",'w') as f:
|
||||||
|
json.dump( { "pitchL":pitchL, "minDbL":list(interpMinDbL), "maxDbL":list(interpMaxDbL) }, f )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def plot_min_max_db( inDir, cfg, pitchL=None ):
|
||||||
|
|
||||||
|
pitchFolderL = os.listdir(inDir)
|
||||||
|
|
||||||
|
if pitchL is None:
|
||||||
|
pitchL = [ int( int(pitchFolder) ) for pitchFolder in pitchFolderL ]
|
||||||
|
|
||||||
|
|
||||||
|
maxDbL = []
|
||||||
|
minDbL = []
|
||||||
|
for midi_pitch in pitchL:
|
||||||
|
|
||||||
|
print(midi_pitch)
|
||||||
|
|
||||||
|
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
|
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
||||||
|
|
||||||
|
minDurMs = cfg.analysisArgs['resampleMinDurMs']
|
||||||
|
minDb = cfg.analysisArgs['resampleMinDb'],
|
||||||
|
noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
|
||||||
|
|
||||||
|
skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, takeIdL, scoreV.tolist(), minDurMs, minDb, noiseLimitPct )
|
||||||
|
|
||||||
|
minDbL.append( dbL[firstAudibleIdx] )
|
||||||
|
|
||||||
|
dbL = sorted(dbL)
|
||||||
|
|
||||||
|
x = np.mean(dbL[-3:])
|
||||||
|
x = np.max(dbL)
|
||||||
|
maxDbL.append( x )
|
||||||
|
|
||||||
|
|
||||||
|
fig,ax = plt.subplots()
|
||||||
|
|
||||||
|
fig.set_size_inches(18.5, 10.5)
|
||||||
|
|
||||||
|
p_dL = sorted( zip(pitchL,maxDbL), key=lambda x: x[0] )
|
||||||
|
pitchL,maxDbL = zip(*p_dL)
|
||||||
|
|
||||||
|
ax.plot(pitchL,maxDbL)
|
||||||
|
ax.plot(pitchL,minDbL)
|
||||||
|
|
||||||
|
for pitch,db in zip(pitchL,maxDbL):
|
||||||
|
|
||||||
|
keyMapD = { d['midi']:d for d in cfg.key_mapL }
|
||||||
|
|
||||||
|
ax.text( pitch, db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']))
|
||||||
|
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def estimate_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=0.5, pitchL=None ):
|
||||||
|
|
||||||
|
pitchFolderL = os.listdir(inDir)
|
||||||
|
|
||||||
|
if pitchL is None:
|
||||||
|
pitchL = [ int( int(pitchFolder) ) for pitchFolder in pitchFolderL ]
|
||||||
|
|
||||||
|
mapD = {} # pitch:{ loDb: { hiDb, us_avg, us_cls, us_std, us_min, us_max, db_avg, db_std, cnt }}
|
||||||
|
# where: cnt=count of valid sample points in this db range
|
||||||
|
# us_cls=us of closest point to center of db range
|
||||||
|
|
||||||
|
dbS = set() # { (loDb,hiDb) } track the set of db ranges
|
||||||
|
|
||||||
|
for pitch in pitchL:
|
||||||
|
|
||||||
|
print(pitch)
|
||||||
|
|
||||||
|
# get the sample measurements for pitch
|
||||||
|
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
|
# calc the fit to local straight line curve fit at each point
|
||||||
|
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
||||||
|
|
||||||
|
minDurMs = cfg.analysisArgs['resampleMinDurMs']
|
||||||
|
minDb = cfg.analysisArgs['resampleMinDb'],
|
||||||
|
noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
|
||||||
|
|
||||||
|
# get the set of samples that are not valid (too short, too quiet, too noisy)
|
||||||
|
skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, takeIdL, scoreV.tolist(), minDurMs, minDb, noiseLimitPct )
|
||||||
|
|
||||||
|
mapD[ pitch ] = {}
|
||||||
|
|
||||||
|
# get the count of db ranges
|
||||||
|
N = int(round((maxMapDb - minMapDb) / incrMapDb)) + 1
|
||||||
|
|
||||||
|
# for each db range
|
||||||
|
for i in range(N):
|
||||||
|
|
||||||
|
loDb = minMapDb + (i*incrMapDb)
|
||||||
|
hiDb = loDb + incrMapDb
|
||||||
|
|
||||||
|
dbS.add((loDb,hiDb))
|
||||||
|
|
||||||
|
# get the valid (pulse,db) pairs for this range
|
||||||
|
u_dL = [(us,db) for i,(us,db) in enumerate(zip(usL,dbL)) if i not in skipIdxL and loDb<=db and db<hiDb ]
|
||||||
|
|
||||||
|
us_avg = 0
|
||||||
|
us_cls = 0
|
||||||
|
us_std = 0
|
||||||
|
us_min = 0
|
||||||
|
us_max = 0
|
||||||
|
db_avg = 0
|
||||||
|
db_std = 0
|
||||||
|
|
||||||
|
if len(u_dL) == 0:
|
||||||
|
print("No valid samples for pitch:",pitch," db range:",loDb,hiDb)
|
||||||
|
else:
|
||||||
|
us0L,db0L = zip(*u_dL)
|
||||||
|
|
||||||
|
if len(us0L) == 1:
|
||||||
|
us_avg = us0L[0]
|
||||||
|
us_cls = us_avg
|
||||||
|
us_min = us_avg
|
||||||
|
us_max = us_avg
|
||||||
|
db_avg = db0L[0]
|
||||||
|
|
||||||
|
elif len(us0L) > 1:
|
||||||
|
us_avg = np.mean(us0L)
|
||||||
|
us_cls = us0L[ np.argmin(np.abs(np.array(db0L)-(loDb - (hiDb-loDb)/2.0 ))) ]
|
||||||
|
us_min = np.min(us0L)
|
||||||
|
us_max = np.max(us0L)
|
||||||
|
us_std = np.std(us0L)
|
||||||
|
db_avg = np.mean(db0L)
|
||||||
|
db_std = np.std(db0L)
|
||||||
|
|
||||||
|
us_avg = int(round(us_avg))
|
||||||
|
|
||||||
|
|
||||||
|
mapD[pitch][loDb] = { 'hiDb':hiDb, 'us_avg':us_avg, 'us_cls':us_cls, 'us_std':us_std,'us_min':us_min,'us_max':us_max, 'db_avg':db_avg, 'db_std':db_std, 'cnt':len(u_dL) }
|
||||||
|
|
||||||
|
return mapD, list(dbS)
|
||||||
|
|
||||||
|
def plot_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=1.0, pitchL=None ):
|
||||||
|
|
||||||
|
fig,ax = plt.subplots()
|
||||||
|
|
||||||
|
mapD, dbRefL = estimate_us_to_db_map( inDir, cfg, minMapDb, maxMapDb, incrMapDb, pitchL )
|
||||||
|
|
||||||
|
# for each pitch
|
||||||
|
for pitch, dbD in mapD.items():
|
||||||
|
|
||||||
|
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 ]
|
||||||
|
|
||||||
|
# get the us/db lists for this pitch
|
||||||
|
usL,uscL,dbL,ussL,usnL,usxL,dbsL = zip(*u_dL)
|
||||||
|
|
||||||
|
# plot central curve and std dev's
|
||||||
|
p = ax.plot(usL,dbL, marker='.', label=str(pitch))
|
||||||
|
ax.plot(uscL,dbL, marker='x', label=str(pitch), color=p[0].get_color(), linestyle='None')
|
||||||
|
ax.plot(usL,np.array(dbL)+dbsL, color=p[0].get_color(), alpha=0.3)
|
||||||
|
ax.plot(usL,np.array(dbL)-dbsL, color=p[0].get_color(), alpha=0.3)
|
||||||
|
|
||||||
|
# plot us error bars
|
||||||
|
for db,us,uss,us_min,us_max in zip(dbL,usL,ussL,usnL,usxL):
|
||||||
|
ax.plot([us_min,us_max],[db,db], color=p[0].get_color(), alpha=0.3 )
|
||||||
|
ax.plot([us-uss,us+uss],[db,db], color=p[0].get_color(), alpha=0.3, marker='.', linestyle='None' )
|
||||||
|
|
||||||
|
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def report_take_ids( inDir ):
|
||||||
|
|
||||||
|
pitchDirL = os.listdir(inDir)
|
||||||
|
|
||||||
|
for pitch in pitchDirL:
|
||||||
|
|
||||||
|
pitchDir = os.path.join(inDir,pitch)
|
||||||
|
|
||||||
|
takeDirL = os.listdir(pitchDir)
|
||||||
|
|
||||||
|
if len(takeDirL) == 0:
|
||||||
|
print(pitch," directory empty")
|
||||||
|
else:
|
||||||
|
with open( os.path.join(pitchDir,'0','seq.json'), "rb") as f:
|
||||||
|
r = json.load(f)
|
||||||
|
|
||||||
|
if len(r['eventTimeL']) != 81:
|
||||||
|
print(pitch," ",len(r['eventTimeL']))
|
||||||
|
|
||||||
|
if len(takeDirL) != 3:
|
||||||
|
print("***",pitch,len(takeDirL))
|
||||||
|
|
||||||
|
def cache_us_db( inDir, cfg, outFn ):
|
||||||
|
|
||||||
|
pitch_usDbD = {}
|
||||||
|
pitchDirL = os.listdir(inDir)
|
||||||
|
|
||||||
|
for pitch in pitchDirL:
|
||||||
|
|
||||||
|
pitch = int(pitch)
|
||||||
|
|
||||||
|
print(pitch)
|
||||||
|
|
||||||
|
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
|
pitch_usDbD[pitch] = { 'usL':usL, 'dbL':dbL, 'durMsL':durMsL, 'takeIdL':takeIdL, 'holdDutyPctL': holdDutyPctL }
|
||||||
|
|
||||||
|
|
||||||
|
with open(outFn,"w") as f:
|
||||||
|
json.dump(pitch_usDbD,f)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def gen_vel_map( inDir, cfg, minMaxDbFn, dynLevelN, cacheFn ):
|
||||||
|
|
||||||
|
velMapD = {} # { pitch:[ us ] }
|
||||||
|
|
||||||
|
pitchDirL = os.listdir(inDir)
|
||||||
|
|
||||||
|
with open(cacheFn,"r") as f:
|
||||||
|
pitchUsDbD = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
with open("minInterpDb.json","r") as f:
|
||||||
|
r = json.load(f)
|
||||||
|
minMaxDbD = { pitch:(minDb,maxDb) for pitch,minDb,maxDb in zip(r['pitchL'],r['minDbL'],r['maxDbL']) }
|
||||||
|
|
||||||
|
|
||||||
|
pitchL = sorted( [ int(pitch) for pitch in pitchUsDbD.keys()] )
|
||||||
|
|
||||||
|
for pitch in pitchL:
|
||||||
|
d = pitchUsDbD[str(pitch)]
|
||||||
|
|
||||||
|
usL = d['usL']
|
||||||
|
dbL = np.array(d['dbL'])
|
||||||
|
|
||||||
|
velMapD[pitch] = []
|
||||||
|
|
||||||
|
for i in range(dynLevelN+1):
|
||||||
|
|
||||||
|
db = minMaxDbD[pitch][0] + (i * (minMaxDbD[pitch][1] - minMaxDbD[pitch][0])/ dynLevelN)
|
||||||
|
|
||||||
|
usIdx = np.argmin( np.abs(dbL - db) )
|
||||||
|
|
||||||
|
velMapD[pitch].append( (usL[ usIdx ],db) )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
with open("velMapD.json","w") as f:
|
||||||
|
json.dump(velMapD,f)
|
||||||
|
|
||||||
|
mtx = np.zeros((len(velMapD),dynLevelN+1))
|
||||||
|
print(mtx.shape)
|
||||||
|
|
||||||
|
for i,(pitch,usDbL) in enumerate(velMapD.items()):
|
||||||
|
for j in range(len(usDbL)):
|
||||||
|
mtx[i,j] = usDbL[j][1]
|
||||||
|
|
||||||
|
fig,ax = plt.subplots()
|
||||||
|
ax.plot(pitchL,mtx)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
inDir = sys.argv[1]
|
inDir = sys.argv[1]
|
||||||
cfgFn = sys.argv[2]
|
cfgFn = sys.argv[2]
|
||||||
pitch = int(sys.argv[3])
|
mode = sys.argv[3]
|
||||||
|
if len(sys.argv) <= 4:
|
||||||
|
pitchL = None
|
||||||
|
else:
|
||||||
|
pitchL = [ int(sys.argv[i]) for i in range(4,len(sys.argv)) ]
|
||||||
|
|
||||||
cfg = parse_yaml_cfg( cfgFn )
|
cfg = parse_yaml_cfg( cfgFn )
|
||||||
|
|
||||||
pitchL = [pitch]
|
if mode == 'us_db':
|
||||||
|
plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True,usMax=None )
|
||||||
plot_noise_regions_main( inDir, cfg, pitchL )
|
elif mode == 'noise':
|
||||||
|
plot_all_noise_curves( inDir, cfg, pitchL )
|
||||||
|
elif mode == 'min_max':
|
||||||
|
plot_min_max_db( inDir, cfg, pitchL )
|
||||||
|
elif mode == 'min_max_2':
|
||||||
|
plot_min_max_2_db( inDir, cfg, pitchL )
|
||||||
|
elif mode == 'us_db_map':
|
||||||
|
plot_us_to_db_map( inDir, cfg, pitchL=pitchL )
|
||||||
|
elif mode == 'audacity':
|
||||||
|
rms_analysis.write_audacity_label_files( inDir, cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
|
elif mode == 'rpt_take_ids':
|
||||||
|
report_take_ids( inDir )
|
||||||
|
elif mode == 'manual_db':
|
||||||
|
plot_min_db_manual( inDir, cfg )
|
||||||
|
elif mode == 'gen_vel_map':
|
||||||
|
gen_vel_map( inDir, cfg, "minInterpDb.json", 9, "cache_us_db.json" )
|
||||||
|
elif mode == 'cache_us_db':
|
||||||
|
cache_us_db( inDir, cfg, "cache_us_db.json")
|
||||||
|
else:
|
||||||
|
print("Unknown mode:",mode)
|
||||||
|
|
||||||
|
|
||||||
#rms_analysis.write_audacity_label_files( inDir, cfg.analysisArgs['rmsAnalysisArgs'] )
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -30,8 +30,8 @@ def rms_to_db( xV, rms_srate, dbLinRef ):
|
|||||||
#dbWndN = int(round(refWndMs * rms_srate / 1000.0))
|
#dbWndN = int(round(refWndMs * rms_srate / 1000.0))
|
||||||
#dbRef = ref = np.mean(xV[0:dbWndN])
|
#dbRef = ref = np.mean(xV[0:dbWndN])
|
||||||
|
|
||||||
#print("DB REF:",dbLinRef)
|
#print("DB REF:",dbLinRef, min(xV), np.argmin(xV))
|
||||||
rmsDbV = 20.0 * np.log10( xV / dbLinRef )
|
rmsDbV = 20.0 * np.log10( (xV+np.nextafter(0,1)) / dbLinRef )
|
||||||
|
|
||||||
return rmsDbV
|
return rmsDbV
|
||||||
|
|
||||||
@ -41,7 +41,9 @@ def audio_rms( srate, xV, rmsWndMs, hopMs, dbLinRef ):
|
|||||||
hopSmpN = int(round( hopMs * srate / 1000.0))
|
hopSmpN = int(round( hopMs * srate / 1000.0))
|
||||||
|
|
||||||
xN = xV.shape[0]
|
xN = xV.shape[0]
|
||||||
|
|
||||||
yN = int(((xN - wndSmpN) / hopSmpN) + 1)
|
yN = int(((xN - wndSmpN) / hopSmpN) + 1)
|
||||||
|
|
||||||
assert( yN > 0)
|
assert( yN > 0)
|
||||||
yV = np.zeros( (yN, ) )
|
yV = np.zeros( (yN, ) )
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ def audio_rms( srate, xV, rmsWndMs, hopMs, dbLinRef ):
|
|||||||
while i < xN and j < yN:
|
while i < xN and j < yN:
|
||||||
|
|
||||||
if i == 0:
|
if i == 0:
|
||||||
yV[j] = np.sqrt(xV[0]*xV[0])
|
yV[j] = np.sqrt(xV[0]*xV[0])
|
||||||
elif i < wndSmpN:
|
elif i < wndSmpN:
|
||||||
yV[j] = np.sqrt( np.mean( xV[0:i] * xV[0:i] ) )
|
yV[j] = np.sqrt( np.mean( xV[0:i] * xV[0:i] ) )
|
||||||
else:
|
else:
|
||||||
@ -62,7 +64,7 @@ def audio_rms( srate, xV, rmsWndMs, hopMs, dbLinRef ):
|
|||||||
j += 1
|
j += 1
|
||||||
|
|
||||||
rms_srate = srate / hopSmpN
|
rms_srate = srate / hopSmpN
|
||||||
return rms_to_db( yV, rms_srate, dbLinRef ), rms_srate
|
return rms_to_db( yV[0:j], rms_srate, dbLinRef ), rms_srate
|
||||||
|
|
||||||
|
|
||||||
def audio_stft_rms( srate, xV, rmsWndMs, hopMs, dbLinRef, spectrumIdx ):
|
def audio_stft_rms( srate, xV, rmsWndMs, hopMs, dbLinRef, spectrumIdx ):
|
||||||
@ -219,12 +221,19 @@ def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
|
|||||||
if qualityCoeff > qmax:
|
if qualityCoeff > qmax:
|
||||||
qmax = qualityCoeff
|
qmax = qualityCoeff
|
||||||
|
|
||||||
durAvgDb = (np.mean(r.rmsDbV[bi:ei]) + np.mean(r.tdRmsDbV[bi:ei]))/2.0
|
if ei-bi == 0:
|
||||||
|
tdRmsDb_v = 0.0 if bi >= len(r.tdRmsDbV) else np.mean(r.tdRmsDbV[bi])
|
||||||
|
hmRmsDb_v = 0.0 if bi >= len(r.rmsDbV) else np.mean(r.rmsDbV[bi])
|
||||||
|
durAvgDb = (hmRmsDb_v + tdRmsDb_v)/2.0
|
||||||
|
else:
|
||||||
|
tdRmsDb_u = 0.0 if ei >= len(r.tdRmsDbV) else np.mean(r.tdRmsDbV[bi:ei])
|
||||||
|
hmRmsDb_u = 0.0 if ei >= len(r.rmsDbV) else np.mean(r.rmsDbV[bi:ei])
|
||||||
|
durAvgDb = (hmRmsDb_u + tdRmsDb_u)/2.0
|
||||||
|
|
||||||
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 }))
|
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 }))
|
||||||
|
|
||||||
for i,r in enumerate(statsL):
|
for i,r in enumerate(statsL):
|
||||||
statsL[i].quality /= qmax
|
statsL[i].quality = 0 if qmax <= 0 else statsL[i].quality / qmax
|
||||||
|
|
||||||
|
|
||||||
return statsL
|
return statsL
|
||||||
@ -284,6 +293,42 @@ def rms_analyze_one_rt_note( sigV, srate, begMs, endMs, midi_pitch, rmsWndMs=300
|
|||||||
|
|
||||||
return { "td":tdD, "hm":hmD }
|
return { "td":tdD, "hm":hmD }
|
||||||
|
|
||||||
|
def rms_analyze_one_rt_note_wrap( audioDev, annBegMs, annEndMs, midi_pitch, noteOffDurMs, rmsAnalysisD ):
|
||||||
|
|
||||||
|
resD = None
|
||||||
|
buf_result = audioDev.linear_buffer()
|
||||||
|
|
||||||
|
if buf_result:
|
||||||
|
|
||||||
|
sigV = buf_result.value
|
||||||
|
|
||||||
|
# get the annotated begin and end of the note as sample indexes into sigV
|
||||||
|
bi = int(round(annBegMs * audioDev.srate / 1000))
|
||||||
|
ei = int(round(annEndMs * audioDev.srate / 1000))
|
||||||
|
|
||||||
|
# calculate half the length of the note-off duration in samples
|
||||||
|
noteOffSmp_o_2 = int(round( (noteOffDurMs/2) * audioDev.srate / 1000))
|
||||||
|
|
||||||
|
# widen the note analysis space noteOffSmp_o_2 samples pre/post the annotated begin/end of the note
|
||||||
|
bi = max(0,bi - noteOffSmp_o_2)
|
||||||
|
ei = min(ei+noteOffSmp_o_2,sigV.shape[0]-1)
|
||||||
|
|
||||||
|
|
||||||
|
ar = types.SimpleNamespace(**rmsAnalysisD)
|
||||||
|
|
||||||
|
# shift the annotatd begin/end of the note to be relative to index bi
|
||||||
|
begMs = noteOffSmp_o_2 * 1000 / audioDev.srate
|
||||||
|
endMs = begMs + (annEndMs - annBegMs)
|
||||||
|
|
||||||
|
#print("MEAS:",begMs,endMs,bi,ei,sigV.shape,audioDev.is_recording_enabled(),ar)
|
||||||
|
|
||||||
|
|
||||||
|
# analyze the note
|
||||||
|
resD = rms_analyze_one_rt_note( sigV[bi:ei], audioDev.srate, begMs, endMs, midi_pitch, rmsWndMs=ar.rmsWndMs, rmsHopMs=ar.rmsHopMs, dbLinRef=ar.dbLinRef, harmCandN=ar.harmCandN, harmN=ar.harmN, durDecayPct=ar.durDecayPct )
|
||||||
|
|
||||||
|
#print( "hm:%4.1f %4i td:%4.1f %4i" % (resD['hm']['db'], resD['hm']['durMs'], resD['td']['db'], resD['td']['durMs']))
|
||||||
|
|
||||||
|
return resD
|
||||||
|
|
||||||
def calibrate_rms( sigV, srate, beg_ms, end_ms ):
|
def calibrate_rms( sigV, srate, beg_ms, end_ms ):
|
||||||
|
|
||||||
@ -368,7 +413,7 @@ def rms_analysis_main( inDir, midi_pitch, rmsWndMs=300, rmsHopMs=30, dbLinRef=0.
|
|||||||
tdPkIdxL = locate_peak_indexes( tdRmsDbV, rms0_srate, r['eventTimeL'])
|
tdPkIdxL = locate_peak_indexes( tdRmsDbV, rms0_srate, r['eventTimeL'])
|
||||||
|
|
||||||
rmsDbV, rms_srate, binHz = audio_harm_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, midi_pitch, harmCandN, harmN )
|
rmsDbV, rms_srate, binHz = audio_harm_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, midi_pitch, harmCandN, harmN )
|
||||||
|
|
||||||
pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'] )
|
pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'] )
|
||||||
|
|
||||||
holdDutyPctL = None
|
holdDutyPctL = None
|
||||||
@ -393,7 +438,6 @@ def rms_analysis_main( inDir, midi_pitch, rmsWndMs=300, rmsHopMs=30, dbLinRef=0.
|
|||||||
'pkDbL': [ rmsDbV[ i ] for i in pkIdxL ],
|
'pkDbL': [ rmsDbV[ i ] for i in pkIdxL ],
|
||||||
'pkUsL':r['pulseUsL'] })
|
'pkUsL':r['pulseUsL'] })
|
||||||
|
|
||||||
|
|
||||||
statsL = note_stats(r,durDecayPct)
|
statsL = note_stats(r,durDecayPct)
|
||||||
|
|
||||||
setattr(r,"statsL", statsL )
|
setattr(r,"statsL", statsL )
|
||||||
@ -492,7 +536,7 @@ def samples_to_linear_residual( usL, dbL, pointsPerLine=5 ):
|
|||||||
assert( len(scoreL) == len(usL) )
|
assert( len(scoreL) == len(usL) )
|
||||||
return np.array(scoreL)
|
return np.array(scoreL)
|
||||||
|
|
||||||
def write_audacity_label_files( inDir, analysisArgsD ):
|
def write_audacity_label_files( inDir, analysisArgsD, reverseFl=True ):
|
||||||
|
|
||||||
pitchDirL = os.listdir(inDir)
|
pitchDirL = os.listdir(inDir)
|
||||||
|
|
||||||
@ -520,7 +564,9 @@ def write_audacity_label_files( inDir, analysisArgsD ):
|
|||||||
|
|
||||||
for i,s in enumerate(r.statsL):
|
for i,s in enumerate(r.statsL):
|
||||||
|
|
||||||
label = "%i %4.1f %6.1f" % (i, s.pkDb, s.durMs )
|
noteIndex = len(r.statsL)-(i+1) if reverseFl else i
|
||||||
|
|
||||||
|
label = "%i %4.1f %6.1f" % (noteIndex, s.pkDb, s.durMs )
|
||||||
|
|
||||||
f.write("%f\t%f\t%s\n" % ( s.begSmpSec, s.endSmpSec, label ))
|
f.write("%f\t%f\t%s\n" % ( s.begSmpSec, s.endSmpSec, label ))
|
||||||
|
|
||||||
|
162
velMapD.h
Normal file
162
velMapD.h
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
{
|
||||||
|
{ 23, { 12800, 12950, 13175, 13500, 13750, 14750, 15375, 17500, 23000, 37000, } },
|
||||||
|
{ 24, { 12425, 12800, 13175, 14225, 14750, 15500, 17500, 22500, 32000, 39000, } },
|
||||||
|
{ 25, { 14150, 14375, 14975, 14625, 15500, 16500, 20000, 28500, 40000, 40000, } },
|
||||||
|
{ 26, { 13000, 13175, 13500, 13700, 13925, 14250, 15000, 16250, 19000, 26500, } },
|
||||||
|
{ 27, { 13625, 13925, 14075, 14250, 14500, 14875, 15375, 16500, 18750, 25000, } },
|
||||||
|
{ 28, { 12625, 13750, 13775, 14225, 14500, 16500, 18000, 20000, 25500, 34000, } },
|
||||||
|
{ 29, { 12125, 12725, 13000, 12950, 14150, 15500, 16250, 17750, 21500, 28000, } },
|
||||||
|
{ 30, { 13175, 13325, 13550, 14450, 14875, 15500, 16250, 17750, 21500, 27000, } },
|
||||||
|
{ 31, { 13925, 14075, 14450, 14625, 15500, 16250, 16750, 17750, 19500, 23500, } },
|
||||||
|
{ 32, { 13250, 14150, 14975, 14750, 15250, 16000, 17500, 21000, 27000, 38000, } },
|
||||||
|
{ 33, { 11825, 13025, 14075, 14825, 14375, 14875, 16250, 17500, 22000, 28000, } },
|
||||||
|
{ 34, { 13025, 13375, 13325, 13775, 14375, 14500, 15250, 18000, 22000, 27000, } },
|
||||||
|
{ 35, { 11375, 12250, 12350, 12725, 14225, 13750, 15375, 17000, 20500, 25000, } },
|
||||||
|
{ 36, { 11750, 13875, 14125, 14225, 14675, 14750, 16500, 18500, 22500, 32000, } },
|
||||||
|
{ 37, { 12425, 12575, 13000, 13025, 13375, 15000, 16000, 18750, 25500, 35000, } },
|
||||||
|
{ 38, { 13750, 13875, 14075, 14600, 14750, 15500, 17750, 21500, 27500, 37000, } },
|
||||||
|
{ 39, { 11000, 12500, 12950, 13700, 14875, 15500, 16250, 20000, 26500, 37000, } },
|
||||||
|
{ 40, { 11525, 11750, 12125, 12500, 12875, 13500, 14625, 18250, 23500, 29000, } },
|
||||||
|
{ 41, { 11675, 11750, 12500, 13000, 13925, 15250, 17000, 20000, 26500, 36000, } },
|
||||||
|
{ 42, { 11875, 12000, 11975, 12050, 12275, 13375, 15000, 17250, 22000, 29000, } },
|
||||||
|
{ 43, { 11500, 11625, 11750, 11750, 12625, 12250, 13625, 16750, 19500, 25500, } },
|
||||||
|
{ 44, { 12425, 12500, 12750, 12650, 13000, 14000, 15250, 16500, 20000, 27000, } },
|
||||||
|
{ 45, { 11250, 11600, 11875, 12000, 12250, 13100, 14750, 15500, 18250, 25500, } },
|
||||||
|
{ 46, { 11450, 11525, 11600, 11625, 11875, 12250, 14000, 15750, 17750, 21500, } },
|
||||||
|
{ 47, { 11900, 11975, 12125, 12375, 13125, 14375, 15750, 18750, 22500, 28500, } },
|
||||||
|
{ 48, { 11750, 13100, 13325, 13625, 14300, 14975, 15750, 19000, 24000, 30000, } },
|
||||||
|
{ 49, { 11975, 12050, 12500, 12750, 13125, 14000, 17000, 20000, 25500, 40000, } },
|
||||||
|
{ 50, { 11625, 11525, 11750, 11825, 12125, 12375, 14750, 16250, 19000, 25500, } },
|
||||||
|
{ 51, { 12050, 12125, 12125, 12275, 12350, 12500, 12875, 16250, 18500, 22500, } },
|
||||||
|
{ 52, { 12950, 13025, 13125, 13175, 13250, 13500, 13875, 15750, 18000, 22000, } },
|
||||||
|
{ 53, { 10600, 10250, 10350, 10450, 10900, 11375, 13025, 14750, 18250, 26500, } },
|
||||||
|
{ 54, { 12650, 12625, 12725, 12800, 13000, 13625, 16250, 18500, 23000, 32000, } },
|
||||||
|
{ 55, { 11875, 12125, 12250, 12425, 12875, 13175, 13750, 17250, 20000, 26000, } },
|
||||||
|
{ 56, { 11625, 11750, 12000, 12200, 12500, 13125, 14375, 17000, 20500, 26500, } },
|
||||||
|
{ 57, { 11625, 11750, 12125, 12275, 12750, 14625, 16750, 20000, 25500, 39000, } },
|
||||||
|
{ 58, { 12000, 12500, 12750, 12875, 13100, 13375, 15000, 17750, 21000, 28000, } },
|
||||||
|
{ 59, { 11625, 11525, 12050, 13375, 13625, 14150, 16500, 21000, 24500, 30000, } },
|
||||||
|
{ 60, { 12250, 12250, 12375, 12350, 13000, 13500, 16000, 17750, 22000, 29000, } },
|
||||||
|
{ 61, { 11375, 11500, 11625, 11750, 12000, 12200, 12725, 13625, 17500, 21000, } },
|
||||||
|
{ 62, { 11600, 11675, 11825, 12125, 12650, 13375, 14375, 18500, 24500, 32000, } },
|
||||||
|
{ 63, { 12125, 12200, 12350, 12500, 13025, 13625, 16250, 18750, 24500, 36000, } },
|
||||||
|
{ 64, { 10550, 10650, 10850, 11250, 11875, 12250, 14000, 16250, 19500, 26500, } },
|
||||||
|
{ 65, { 12750, 12800, 12875, 13175, 13250, 14625, 14975, 17500, 20500, 26000, } },
|
||||||
|
{ 66, { 10750, 11000, 11250, 11500, 12000, 12875, 15375, 17000, 20500, 28500, } },
|
||||||
|
{ 67, { 10950, 11125, 11250, 11500, 11875, 13875, 15750, 17750, 23000, 37000, } },
|
||||||
|
{ 68, { 10150, 10300, 10550, 10800, 11125, 11875, 13000, 16000, 19000, 25000, } },
|
||||||
|
{ 69, { 11750, 11875, 12375, 12500, 12750, 13500, 16250, 18250, 23500, 31000, } },
|
||||||
|
{ 70, { 10700, 10850, 10950, 11125, 11625, 13875, 14500, 15750, 18750, 24500, } },
|
||||||
|
{ 71, { 10200, 10700, 11000, 11250, 11625, 14000, 14875, 16250, 22000, 27000, } },
|
||||||
|
{ 72, { 9800, 10100, 10400, 10550, 11000, 11625, 13000, 15500, 17750, 23000, } },
|
||||||
|
{ 73, { 10750, 10900, 11125, 11375, 11625, 12750, 14750, 15500, 18500, 23000, } },
|
||||||
|
{ 74, { 10300, 10450, 10600, 10850, 11250, 12000, 14250, 15000, 17500, 21000, } },
|
||||||
|
{ 75, { 10600, 10750, 10900, 11125, 12500, 14500, 14750, 15000, 21000, 31000, } },
|
||||||
|
{ 76, { 10200, 11625, 12375, 12875, 13500, 15750, 19000, 22500, 27500, 39000, } },
|
||||||
|
{ 77, { 10500, 10700, 11125, 11375, 11750, 14000, 14875, 16500, 20500, 27000, } },
|
||||||
|
{ 78, { 10450, 10800, 11000, 11625, 12000, 13125, 15500, 18250, 22000, 34000, } },
|
||||||
|
{ 79, { 12250, 13500, 14125, 14750, 16250, 17500, 19000, 24500, 31000, 40000, } },
|
||||||
|
{ 80, { 10400, 10450, 10750, 11125, 12125, 13375, 14750, 17750, 23000, 39000, } },
|
||||||
|
{ 81, { 10800, 10950, 11125, 11375, 12625, 13875, 14875, 16000, 19000, 23500, } },
|
||||||
|
{ 82, { 12000, 12375, 13750, 13750, 12625, 14000, 17000, 19000, 21000, 24500, } },
|
||||||
|
{ 83, { 12250, 12500, 13625, 13875, 14375, 16500, 17750, 20500, 25000, 35000, } },
|
||||||
|
{ 84, { 11500, 12000, 12250, 12500, 13125, 14250, 15375, 16750, 19500, 25500, } },
|
||||||
|
{ 85, { 10400, 10500, 10600, 11250, 12250, 13375, 15000, 16750, 20000, 26000, } },
|
||||||
|
{ 86, { 11500, 11750, 11875, 12000, 12250, 12500, 13500, 15000, 20500, 21000, } },
|
||||||
|
{ 87, { 10650, 11500, 13125, 13375, 13750, 14500, 16500, 18000, 20000, 24000, } },
|
||||||
|
{ 88, { 11375, 11375, 11500, 12375, 12000, 13375, 14500, 16500, 19000, 23000, } },
|
||||||
|
{ 89, { 9200, 10900, 11500, 12125, 22000, 12875, 14125, 16000, 19000, 26500, } },
|
||||||
|
{ 91, { 9450, 9950, 10000, 10150, 10600, 11250, 12125, 13875, 15250, 19000, } },
|
||||||
|
{ 92, { 9050, 9500, 9600, 10100, 10900, 11875, 13000, 16000, 20500, 31000, } },
|
||||||
|
{ 93, { 11250, 11375, 12000, 12375, 12875, 13625, 14250, 17500, 21500, 39000, } },
|
||||||
|
{ 94, { 11125, 11375, 11750, 13500, 14000, 14875, 15750, 17750, 22000, 25500, } },
|
||||||
|
{ 95, { 10200, 10350, 11500, 12250, 12500, 13125, 13875, 15250, 19000, 21000, } },
|
||||||
|
{ 96, { 9050, 9550, 10100, 13875, 13000, 14000, 18500, 22000, 27000, 39000, } },
|
||||||
|
{ 97, { 11000, 12500, 13250, 13000, 13750, 15750, 15000, 18000, 19000, 22500, } },
|
||||||
|
{ 98, { 10400, 10850, 12125, 12125, 13250, 13875, 16000, 18750, 26500, 37000, } },
|
||||||
|
{ 99, { 11000, 12625, 13125, 14000, 15500, 16750, 19000, 21500, 25000, 36000, } },
|
||||||
|
{ 100, { 9650, 10450, 11500, 12375, 12500, 12875, 13500, 15500, 17500, 21500, } },
|
||||||
|
{ 101, { 10950, 11250, 11500, 11875, 12250, 12875, 13500, 14375, 22500, 39000, } },
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
23, {{ 0, 70 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
24, {{ 0, 75 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
25, {{ 0, 70 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
26, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
27, {{ 0, 70 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
28, {{ 0, 70 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
29, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
30, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
31, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
32, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
33, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
34, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
35, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
36, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
37, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
38, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
39, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
40, {{ 0, 55 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
41, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
42, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
43, {{ 0, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
44, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
45, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
46, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
47, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
48, {{ 0, 70 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
49, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
50, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
51, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
52, {{ 0, 55 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
53, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
54, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
55, {{ 0, 50 }, { 22000, 55 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
56, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
57, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
58, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
59, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
60, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
61, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
62, {{ 0, 55 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
63, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
64, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
65, {{ 0, 50 }, { 17000, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
66, {{ 0, 53 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
67, {{ 0, 55 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
68, {{ 0, 53 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
69, {{ 0, 55 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
70, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
71, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
72, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
73, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
74, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
75, {{ 0, 55 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
76, {{ 0, 70 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
77, {{ 0, 50 }, { 15000, 60 }, { 19000, 70 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
78, {{ 0, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
79, {{ 0, 50 }, { 15000, 60 }, { 19000, 70 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
80, {{ 0, 45 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
81, {{ 0, 50 }, { 15000, 70 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
82, {{ 0, 50 }, { 12500, 60 }, { 14000, 65 }, { 17000, 70 }, { -1, -1 }, }
|
||||||
|
83, {{ 0, 50 }, { 15000, 65 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
84, {{ 0, 50 }, { 12500, 60 }, { 14000, 65 }, { 17000, 70 }, { -1, -1 }, }
|
||||||
|
85, {{ 0, 50 }, { 12500, 60 }, { 14000, 65 }, { 17000, 70 }, { -1, -1 }, }
|
||||||
|
86, {{ 0, 50 }, { 12500, 60 }, { 14000, 65 }, { 17000, 70 }, { -1, -1 }, }
|
||||||
|
87, {{ 0, 50 }, { 14000, 60 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
88, {{ 0, 50 }, { 12500, 60 }, { 14000, 65 }, { 17000, 70 }, { -1, -1 }, }
|
||||||
|
89, {{ 0, 50 }, { 12500, 60 }, { 14000, 65 }, { 17000, 70 }, { -1, -1 }, }
|
||||||
|
91, {{ 0, 40 }, { 12500, 50 }, { 14000, 60 }, { 17000, 65 }, { -1, -1 }, }
|
||||||
|
92, {{ 0, 40 }, { 14000, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
93, {{ 0, 40 }, { 12500, 50 }, { 14000, 60 }, { 17000, 65 }, { -1, -1 }, }
|
||||||
|
94, {{ 0, 40 }, { 14000, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
95, {{ 0, 40 }, { 12500, 50 }, { 14000, 60 }, { 17000, 65 }, { -1, -1 }, }
|
||||||
|
96, {{ 0, 40 }, { 12500, 50 }, { 14000, 60 }, { 17000, 65 }, { -1, -1 }, }
|
||||||
|
97, {{ 0, 40 }, { 14000, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
98, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
99, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
100, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
101, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
106, {{ 0, 50 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, }
|
||||||
|
}
|
1
velMapD.json
Normal file
1
velMapD.json
Normal file
File diff suppressed because one or more lines are too long
53
velTableToDataStruct.py
Normal file
53
velTableToDataStruct.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import json
|
||||||
|
from common import parse_yaml_cfg
|
||||||
|
|
||||||
|
ymlFn = 'p_ac.yml'
|
||||||
|
ifn = 'velMapD.json'
|
||||||
|
ofn = 'velMapD.h'
|
||||||
|
|
||||||
|
with open(ofn,"wt") as f1:
|
||||||
|
|
||||||
|
with open(ifn,"r") as f:
|
||||||
|
d = json.load(f)
|
||||||
|
|
||||||
|
f1.write("{\n");
|
||||||
|
for key,velL in d.items():
|
||||||
|
f1.write("{ ")
|
||||||
|
f1.write( str(key) + ", { " )
|
||||||
|
for us,x in velL:
|
||||||
|
f1.write("%5i, " % (us))
|
||||||
|
f1.write("} },\n")
|
||||||
|
f1.write("}\n\n");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cfg = parse_yaml_cfg(ymlFn)
|
||||||
|
|
||||||
|
d = cfg.calibrateArgs['holdDutyPctD']
|
||||||
|
|
||||||
|
n = 0
|
||||||
|
for key,dutyL in d.items():
|
||||||
|
n = max(n, len(dutyL))
|
||||||
|
|
||||||
|
f1.write("{\n")
|
||||||
|
|
||||||
|
for key,dutyL in d.items():
|
||||||
|
f1.write( str(key)+", {")
|
||||||
|
|
||||||
|
for i,(us,duty) in enumerate(dutyL):
|
||||||
|
f1.write("{ %i, %i }, " % (us,duty))
|
||||||
|
|
||||||
|
for j in range(i,n):
|
||||||
|
f1.write("{ -1, -1 }, ")
|
||||||
|
|
||||||
|
f1.write("},\n");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
f1.write("}\n");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user