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])
|
||||
|
||||
|
||||
|
||||
|
68
calibrate.py
68
calibrate.py
@ -1,6 +1,7 @@
|
||||
import os,types,wave,json,array
|
||||
import numpy as np
|
||||
from rms_analysis import rms_analyze_one_rt_note
|
||||
from plot_seq_1 import get_merged_pulse_db_measurements
|
||||
|
||||
class Calibrate:
|
||||
def __init__( self, cfg, audio, midi, api ):
|
||||
@ -17,6 +18,8 @@ class Calibrate:
|
||||
|
||||
self.measD = None # { midi_pitch: [ {pulseUs, db, durMs, targetDb } ] }
|
||||
|
||||
self.initPulseDbListD = self._get_init_pulseDbD()
|
||||
|
||||
self.curNoteStartMs = None
|
||||
self.curPitchIdx = None
|
||||
self.curTargetDbIdx = None
|
||||
@ -47,6 +50,7 @@ class Calibrate:
|
||||
self.lastAudiblePulseUs = None
|
||||
self.maxTooShortPulseUs = None
|
||||
self.pulseDbL = []
|
||||
self.pulseDbL = self.initPulseDbListD[ self.cfg.pitchL[ self.curPitchIdx ] ]
|
||||
self.deltaUpMult = 1
|
||||
self.deltaDnMult = 1
|
||||
self.curTargetDbIdx = -1
|
||||
@ -84,7 +88,7 @@ class Calibrate:
|
||||
|
||||
self.audio.record_enable(True)
|
||||
|
||||
self._do_play_update()
|
||||
self._do_play_only_update()
|
||||
|
||||
def tick(self,ms):
|
||||
|
||||
@ -105,7 +109,7 @@ class Calibrate:
|
||||
|
||||
elif self.state == 'note_off':
|
||||
if self.playOnlyFl:
|
||||
if not self._do_play_update():
|
||||
if not self._do_play_only_update():
|
||||
self.stop(ms)
|
||||
self.state = 'stopped'
|
||||
else:
|
||||
@ -120,7 +124,7 @@ class Calibrate:
|
||||
self.state = 'started'
|
||||
|
||||
|
||||
def _calc_play_pulse_us( self, pitch, targetDb ):
|
||||
def _calc_play_only_pulse_us( self, pitch, targetDb ):
|
||||
|
||||
pulseDbL = []
|
||||
for d in self.measD[ pitch ]:
|
||||
@ -136,7 +140,7 @@ class Calibrate:
|
||||
|
||||
return np.mean(pulseL)
|
||||
|
||||
def _do_play_update( self ):
|
||||
def _do_play_only_update( self ):
|
||||
|
||||
if self.curPitchIdx >= 0:
|
||||
self._meas_note( self.cfg.pitchL[self.curPitchIdx], self.curPulseUs )
|
||||
@ -150,7 +154,7 @@ class Calibrate:
|
||||
|
||||
pitch = self.cfg.pitchL[ self.curPitchIdx ]
|
||||
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
|
||||
|
||||
if self.curPulseUs == -1:
|
||||
@ -161,7 +165,24 @@ class Calibrate:
|
||||
|
||||
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 ):
|
||||
|
||||
@ -232,8 +253,9 @@ class Calibrate:
|
||||
|
||||
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]
|
||||
pulse1,db1 = self.pulseDbL[-1]
|
||||
|
||||
@ -255,19 +277,22 @@ class Calibrate:
|
||||
|
||||
def _calc_next_pulse_us( self, targetDb ):
|
||||
|
||||
|
||||
# sort pulseDb ascending on db
|
||||
#self.pulseDbL = sorted( self.pulseDbL, key=lambda x: x[1] )
|
||||
pulseDbL = sorted( self.pulseDbL, key=lambda x: x[1] )
|
||||
|
||||
|
||||
pulseL,dbL = zip(*self.pulseDbL)
|
||||
# get the set of us/db values tried so far
|
||||
pulseL,dbL = zip(*pulseDbL)
|
||||
|
||||
max_i = np.argmax(dbL)
|
||||
min_i = np.argmin(dbL)
|
||||
|
||||
# if the targetDb is greater than the max. db value achieved so far
|
||||
if targetDb > dbL[max_i]:
|
||||
pu = pulseL[max_i] + self.deltaUpMult * 500
|
||||
self.deltaUpMult += 1
|
||||
|
||||
# if the targetDb is less than the min. db value achieved so far
|
||||
elif targetDb < dbL[min_i]:
|
||||
pu = pulseL[min_i] - self.deltaDnMult * 500
|
||||
self.deltaDnMult += 1
|
||||
@ -277,12 +302,18 @@ class Calibrate:
|
||||
pu = self.maxTooShortPulseUs + (abs(pulseL[min_i] - self.maxTooShortPulseUs))/2
|
||||
self.deltaDnMult = 1
|
||||
else:
|
||||
# the targetDb value is inside the min/max range of the db values acheived so far
|
||||
self.deltaUpMult = 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)
|
||||
|
||||
# the selected pulse has already been sampled
|
||||
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)
|
||||
@ -319,23 +350,23 @@ class Calibrate:
|
||||
|
||||
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) )
|
||||
|
||||
# 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
|
||||
|
||||
# calc the upper and lower bounds db range
|
||||
lwr_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:
|
||||
self.curMatchN += 1
|
||||
measD['matchFl'] = True
|
||||
print("MATCH!")
|
||||
|
||||
#
|
||||
# calculate the next pulse length
|
||||
self.curPulseUs = int(self._calc_next_pulse_us(self.curTargetDb))
|
||||
|
||||
# if at least minMatchN matches have been made on this pitch/targetDb
|
||||
@ -371,8 +402,6 @@ class Calibrate:
|
||||
|
||||
sigV = buf_result.value
|
||||
|
||||
|
||||
|
||||
# get the annotated begin and end of the note as sample indexes into sigV
|
||||
bi = int(round(annD['beg_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)
|
||||
ei = min(ei+noteOffSmp_o_2,sigV.shape[0]-1)
|
||||
|
||||
|
||||
ar = types.SimpleNamespace(**self.cfg.analysisD)
|
||||
|
||||
# shift the annotatd begin/end of the note to be relative to index bi
|
||||
@ -394,8 +422,9 @@ class Calibrate:
|
||||
#print("MEAS:",begMs,endMs,bi,ei,sigV.shape,self.audio.is_recording_enabled(),ar)
|
||||
|
||||
|
||||
|
||||
# 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["midi_pitch"] = midi_pitch
|
||||
@ -408,7 +437,6 @@ class Calibrate:
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -428,12 +456,14 @@ class Calibrate:
|
||||
return False
|
||||
|
||||
|
||||
# reset the variables prior to begining the next target search
|
||||
self.curTargetDb = self.cfg.targetDbL[ self.curTargetDbIdx ]
|
||||
self.curMatchN = 0
|
||||
self.curAttemptN = 0
|
||||
self.lastAudiblePulseUs = None
|
||||
self.maxTooShortPulseUs = None
|
||||
self.pulseDbL = []
|
||||
self.pulseDbL = self.initPulseDbListD[ self.cfg.pitchL[ self.curPitchIdx ] ]
|
||||
self.deltaUpMult = 1
|
||||
self.deltaDnMult = 1
|
||||
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 )
|
90
p_ac.py
90
p_ac.py
@ -10,11 +10,13 @@ from MidiDevice import MidiDevice
|
||||
from result import Result
|
||||
from common import parse_yaml_cfg
|
||||
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 rt_note_analysis import RT_Analyzer
|
||||
from keyboard import Keyboard
|
||||
from calibrate import Calibrate
|
||||
from rms_analysis import rms_analyze_one_rt_note_wrap
|
||||
from MidiFilePlayer import MidiFilePlayer
|
||||
|
||||
class AttackPulseSeq:
|
||||
""" 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.pauseDurMs = pauseDurMs # duration between end of previous note and start of next
|
||||
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.state = None # 'note_on','note_off'
|
||||
self.prevHoldDutyPct = None
|
||||
@ -39,11 +42,13 @@ class AttackPulseSeq:
|
||||
self.playOnlyFl = False
|
||||
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.pitch = pitch # note to play
|
||||
self.pulseUsL = pulseUsL # one onset pulse length in microseconds per sequence element
|
||||
self.holdDutyPctL = holdDutyPctL
|
||||
self.holdDutyPctD = holdDutyPctD
|
||||
self.silentNoteN = 0
|
||||
self.pulse_idx = 0
|
||||
self.state = 'note_on'
|
||||
self.prevHoldDutyPct = None
|
||||
@ -84,17 +89,39 @@ class AttackPulseSeq:
|
||||
# if waiting to turn a note off
|
||||
elif self.state == 'note_off':
|
||||
self._note_off(ms)
|
||||
self._count_silent_notes()
|
||||
|
||||
|
||||
self.pulse_idx += 1
|
||||
|
||||
# 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)
|
||||
|
||||
else:
|
||||
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 ):
|
||||
return self.holdDutyPctD[ pulseUsec ]
|
||||
|
||||
dutyPct = self.holdDutyPctL[0][1]
|
||||
for refUsec,refDuty in self.holdDutyPctL:
|
||||
if pulseUsec < refUsec:
|
||||
@ -232,7 +259,7 @@ class CalibrateKeys:
|
||||
print(outDir_id,outDir)
|
||||
|
||||
# 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
|
||||
else:
|
||||
#self.pulseUsL,_,_ = form_resample_pulse_time_list( outDir, self.cfg.analysisArgs )
|
||||
@ -252,8 +279,23 @@ class CalibrateKeys:
|
||||
if not os.path.isdir(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
|
||||
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 ):
|
||||
@ -274,6 +316,7 @@ class App:
|
||||
self.cal_keys = None
|
||||
self.keyboard = None
|
||||
self.calibrate = None
|
||||
self.midiFilePlayer = None
|
||||
|
||||
def setup( self, cfg ):
|
||||
self.cfg = cfg
|
||||
@ -281,17 +324,22 @@ class App:
|
||||
self.audioDev = AudioDevice()
|
||||
self.midiDev = MidiDevice()
|
||||
|
||||
res = None
|
||||
|
||||
#
|
||||
# TODO: unify the result error handling
|
||||
# (the API and the audio device return two diferent 'Result' types
|
||||
#
|
||||
|
||||
if hasattr(cfg,'audio'):
|
||||
res = self.audioDev.setup(**cfg.audio)
|
||||
|
||||
if not res:
|
||||
self.audio_dev_list(0)
|
||||
else:
|
||||
|
||||
else:
|
||||
self.audioDev = None
|
||||
|
||||
if True:
|
||||
if hasattr(cfg,'midi'):
|
||||
res = self.midiDev.setup(**cfg.midi)
|
||||
|
||||
@ -315,12 +363,15 @@ class App:
|
||||
|
||||
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
|
||||
|
||||
def tick( self, ms ):
|
||||
|
||||
if self.audioDev is not None:
|
||||
self.audioDev.tick(ms)
|
||||
|
||||
if self.cal_keys:
|
||||
@ -332,6 +383,9 @@ class App:
|
||||
if self.calibrate:
|
||||
self.calibrate.tick(ms)
|
||||
|
||||
if self.midiFilePlayer:
|
||||
self.midiFilePlayer.tick(ms)
|
||||
|
||||
def audio_dev_list( self, ms ):
|
||||
portL = self.audioDev.get_port_list( True )
|
||||
|
||||
@ -383,6 +437,20 @@ class App:
|
||||
self.keyboard.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 ):
|
||||
if self.api:
|
||||
self.api.close()
|
||||
@ -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"},
|
||||
'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"},
|
||||
'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 ):
|
||||
|
151
p_ac.yml
151
p_ac.yml
@ -3,19 +3,19 @@
|
||||
|
||||
|
||||
# Audio device setup
|
||||
audio: {
|
||||
audio_off: {
|
||||
inPortLabel: "5 USB Audio CODEC:", #"HDA Intel PCH: CS4208", # "5 USB Audio CODEC:", #"5 USB Sound Device",
|
||||
outPortLabel: ,
|
||||
},
|
||||
|
||||
midi_off: {
|
||||
midi: {
|
||||
inMonitorFl: False,
|
||||
outMonitorFl: False,
|
||||
throughFl: False,
|
||||
inPortLabel: "Fastlane:Fastlane MIDI A",
|
||||
outPortLabel: "Fastlane:Fastlane MIDI A"
|
||||
#inPortLabel: "picadae:picadae MIDI 1",
|
||||
#outPortLabel: "picadae:picadae MIDI 1"
|
||||
#inPortLabel: "Fastlane:Fastlane MIDI A",
|
||||
#outPortLabel: "Fastlane:Fastlane MIDI A"
|
||||
inPortLabel: "picadae:picadae MIDI 1",
|
||||
outPortLabel: "picadae:picadae MIDI 1"
|
||||
},
|
||||
|
||||
# Picadae API args
|
||||
@ -27,10 +27,19 @@
|
||||
|
||||
|
||||
# MeasureSeq args
|
||||
outDir: "~/temp/p_ac_3e",
|
||||
noteDurMs: 1000,
|
||||
pauseDurMs: 1000,
|
||||
#holdDutyPctL: [ [0,50], [22000,55] ],
|
||||
outDir: "~/temp/p_ac_3g",
|
||||
noteDurMs: 500,
|
||||
pauseDurMs: 500,
|
||||
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_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_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
|
||||
analysisArgs: {
|
||||
@ -61,8 +77,8 @@
|
||||
durDecayPct: 40, # percent drop in RMS to indicate the end of a note
|
||||
},
|
||||
|
||||
resampleMinDb: 10.0, # note's less than this will be skipped
|
||||
resampleNoiseLimitPct: 1.0, #
|
||||
resampleMinDb: 7.0, # note's less than this will be skipped
|
||||
resampleNoiseLimitPct: 5.0, #
|
||||
resampleMinDurMs: 800, # notes's whose duration is less than this will be skipped
|
||||
|
||||
minAttkDb: 7.0, # threshold of silence level
|
||||
@ -76,11 +92,98 @@
|
||||
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: {
|
||||
|
||||
outDir: "~/temp/calib0",
|
||||
outLabel: "test_1",
|
||||
outDir: "~/temp/calib2",
|
||||
outLabel: "test_3",
|
||||
|
||||
inDir: "~/temp/p_ac_3f",
|
||||
|
||||
analysisD: {
|
||||
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
|
||||
},
|
||||
|
||||
noteOnDurMs: 1000,
|
||||
noteOffDurMs: 1000,
|
||||
noteOnDurMs: 500,
|
||||
noteOffDurMs: 500,
|
||||
|
||||
|
||||
pitchL: [ 44, 45, 46, 47, 48, 49, 50, 51 ], # list of pitches
|
||||
targetDbL: [ 16, 17, 18, 19, 20, 21, 22, 23, 24 ], # list of target db
|
||||
#pitchL: [ 31, 33, 34, 35 ], # list of pitches
|
||||
#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
|
||||
maxPulseUs: 45000, # max. allowable pulse us
|
||||
minPulseUs: 8000, # min. allowable pulse us
|
||||
initPulseUs: 15000, # pulseUs for first note
|
||||
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
|
||||
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: {
|
||||
23: [[0, 70]],
|
||||
|
@ -143,7 +143,7 @@ if __name__ == "__main__":
|
||||
inDir = sys.argv[1]
|
||||
yamlFn = sys.argv[2]
|
||||
if len(sys.argv) > 3:
|
||||
pitch = int(sys.argv[2])
|
||||
pitch = int(sys.argv[3])
|
||||
|
||||
keyInfoD = key_info_dictionary( yamlCfgFn=yamlFn)
|
||||
#plot_all_notes( inDir )
|
||||
|
616
plot_seq_1.py
616
plot_seq_1.py
@ -1,30 +1,61 @@
|
||||
import os, sys
|
||||
import os, sys,json
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from common import parse_yaml_cfg
|
||||
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 ):
|
||||
|
||||
inDir = os.path.join(inDir,"%i" % (midi_pitch))
|
||||
|
||||
dirL = os.listdir(inDir)
|
||||
takeDirL = os.listdir(inDir)
|
||||
|
||||
pkL = []
|
||||
|
||||
# for each take in this directory
|
||||
for idir in dirL:
|
||||
usRefL = None
|
||||
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
|
||||
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) ]
|
||||
for db,us,stats in zip(r.pkDbL,r.pkUsL,r.statsL):
|
||||
pkL.append( (db,us,stats.durMs,take_number) )
|
||||
|
||||
|
||||
pkL = fit_to_reference( pkL, 0 )
|
||||
|
||||
# sort the peaks on increasing attack pulse microseconds
|
||||
pkL = sorted( pkL, key= lambda x: x[1] )
|
||||
@ -37,21 +68,33 @@ def get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD ):
|
||||
|
||||
return pkUsL,pkDbL,durMsL,takeIdL,r.holdDutyPctL
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def select_resample_reference_indexes( noiseIdxL ):
|
||||
|
||||
resampleIdxS = set()
|
||||
|
||||
# for each noisy sample index store that index and the index
|
||||
# before and after it
|
||||
for i in noiseIdxL:
|
||||
resampleIdxS.add( i )
|
||||
if i+1 < len(noiseIdxL):
|
||||
resampleIdxS.add( i+1 )
|
||||
if i-1 >= 0:
|
||||
resampleIdxS.add( i-1 )
|
||||
|
||||
resampleIdxL = list(resampleIdxS)
|
||||
|
||||
# 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:
|
||||
if i + 1 not in resampleIdxL and i + 2 in resampleIdxL: # BUG BUG BUG: Hardcoded constant
|
||||
if i+1 < len(noiseIdxL):
|
||||
resampleIdxL.append(i+1)
|
||||
|
||||
return resampleIdxL
|
||||
@ -99,11 +142,13 @@ def locate_resample_regions( usL, dbL, resampleIdxL ):
|
||||
|
||||
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
|
||||
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
|
||||
# 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 ):
|
||||
|
||||
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 )
|
||||
|
||||
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 ]
|
||||
noiseL = [ (usL[i],dbL[i]) for i in noiseIdxL ]
|
||||
resampleIdxL = select_resample_reference_indexes( noiseIdxL )
|
||||
|
||||
if firstNonSkipIdx is not None:
|
||||
resampleIdxL = [ i for i in resampleIdxL if i >= firstNonSkipIdx ]
|
||||
|
||||
resampleL = [ (usL[i],dbL[i]) for i in 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 ):
|
||||
|
||||
@ -164,45 +213,74 @@ def get_resample_points_wrap( inDir, midi_pitch, analysisArgsD ):
|
||||
|
||||
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'] )
|
||||
|
||||
reUsL, reDbL, noiseL, resampleL, durL, firstAudibleIdx, firstNonSkipIdx = get_resample_points( usL, dbL, durMsL, takeIdL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
|
||||
reUsL, reDbL, noiseL, resampleL, skipL, firstAudibleIdx, firstNonSkipIdx = get_resample_points( usL, dbL, durMsL, takeIdL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
|
||||
|
||||
# plot first audible and non-skip position
|
||||
if False:
|
||||
|
||||
if firstNonSkipIdx is not None:
|
||||
ax.plot( usL[firstNonSkipIdx], dbL[firstNonSkipIdx], markersize=15, marker='+', linestyle='None', color='red')
|
||||
ax.plot( usL[firstNonSkipIdx], dbL[firstAudibleIdx], markersize=15, marker='*', linestyle='None', color='red')
|
||||
|
||||
if firstAudibleIdx is not None:
|
||||
ax.plot( usL[firstAudibleIdx], dbL[firstAudibleIdx], markersize=15, marker='*', linestyle='None', color='red')
|
||||
|
||||
# plot the resample points
|
||||
if plotResampleFl:
|
||||
ax.plot( reUsL, reDbL, markersize=10, marker='x', linestyle='None', color='green')
|
||||
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', linestyle='None', color='black')
|
||||
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='*', linestyle='None', color='red')
|
||||
ax.plot( nUsL, nDbL, marker='+', markersize=8, linestyle='None', color='red')
|
||||
|
||||
|
||||
|
||||
# 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:
|
||||
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 ])
|
||||
ax.plot(xL,yL, marker='.')
|
||||
|
||||
ax.plot(xL,yL, marker='.',label=takeId)
|
||||
|
||||
for i,(x,y) in enumerate(zip(xL,yL)):
|
||||
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:
|
||||
ax.plot(usL, dbL, marker='.')
|
||||
|
||||
# plot the duration skip points
|
||||
if durL:
|
||||
nUsL,nDbL = zip(*durL)
|
||||
ax.plot([elbow_us],[elbow_db],marker='*',markersize=12,color='red',linestyle='None')
|
||||
|
||||
# plot the skip points in 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
|
||||
@ -214,35 +292,503 @@ def plot_noise_region( ax, inDir, keyMapD, midi_pitch, analysisArgsD ):
|
||||
# plot the 'minDb' reference line
|
||||
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']))
|
||||
|
||||
def plot_noise_regions_main( inDir, cfg, pitchL ):
|
||||
def plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True, usMax=None ):
|
||||
|
||||
analysisArgsD = cfg.analysisArgs
|
||||
keyMapD = { d['midi']:d for d in cfg.key_mapL }
|
||||
axN = len(pitchL)
|
||||
fig,axL = plt.subplots(axN,1)
|
||||
fig,axL = plt.subplots(axN,1,sharex=True)
|
||||
if axN == 1:
|
||||
axL = [axL]
|
||||
fig.set_size_inches(18.5, 10.5*axN)
|
||||
|
||||
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()
|
||||
|
||||
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__":
|
||||
|
||||
inDir = sys.argv[1]
|
||||
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 )
|
||||
|
||||
pitchL = [pitch]
|
||||
if mode == 'us_db':
|
||||
plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True,usMax=None )
|
||||
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)
|
||||
|
||||
plot_noise_regions_main( inDir, cfg, pitchL )
|
||||
|
||||
#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))
|
||||
#dbRef = ref = np.mean(xV[0:dbWndN])
|
||||
|
||||
#print("DB REF:",dbLinRef)
|
||||
rmsDbV = 20.0 * np.log10( xV / dbLinRef )
|
||||
#print("DB REF:",dbLinRef, min(xV), np.argmin(xV))
|
||||
rmsDbV = 20.0 * np.log10( (xV+np.nextafter(0,1)) / dbLinRef )
|
||||
|
||||
return rmsDbV
|
||||
|
||||
@ -41,7 +41,9 @@ def audio_rms( srate, xV, rmsWndMs, hopMs, dbLinRef ):
|
||||
hopSmpN = int(round( hopMs * srate / 1000.0))
|
||||
|
||||
xN = xV.shape[0]
|
||||
|
||||
yN = int(((xN - wndSmpN) / hopSmpN) + 1)
|
||||
|
||||
assert( yN > 0)
|
||||
yV = np.zeros( (yN, ) )
|
||||
|
||||
@ -62,7 +64,7 @@ def audio_rms( srate, xV, rmsWndMs, hopMs, dbLinRef ):
|
||||
j += 1
|
||||
|
||||
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 ):
|
||||
@ -219,12 +221,19 @@ def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
|
||||
if qualityCoeff > qmax:
|
||||
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 }))
|
||||
|
||||
for i,r in enumerate(statsL):
|
||||
statsL[i].quality /= qmax
|
||||
statsL[i].quality = 0 if qmax <= 0 else statsL[i].quality / qmax
|
||||
|
||||
|
||||
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 }
|
||||
|
||||
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 ):
|
||||
|
||||
@ -393,7 +438,6 @@ def rms_analysis_main( inDir, midi_pitch, rmsWndMs=300, rmsHopMs=30, dbLinRef=0.
|
||||
'pkDbL': [ rmsDbV[ i ] for i in pkIdxL ],
|
||||
'pkUsL':r['pulseUsL'] })
|
||||
|
||||
|
||||
statsL = note_stats(r,durDecayPct)
|
||||
|
||||
setattr(r,"statsL", statsL )
|
||||
@ -492,7 +536,7 @@ def samples_to_linear_residual( usL, dbL, pointsPerLine=5 ):
|
||||
assert( len(scoreL) == len(usL) )
|
||||
return np.array(scoreL)
|
||||
|
||||
def write_audacity_label_files( inDir, analysisArgsD ):
|
||||
def write_audacity_label_files( inDir, analysisArgsD, reverseFl=True ):
|
||||
|
||||
pitchDirL = os.listdir(inDir)
|
||||
|
||||
@ -520,7 +564,9 @@ def write_audacity_label_files( inDir, analysisArgsD ):
|
||||
|
||||
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 ))
|
||||
|
||||
|
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