##| Copyright: (C) 2019-2020 Kevin Larke ##| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. import os,sys,pickle,json import numpy as np import matplotlib.pyplot as plt import matplotlib._color_data as mcd from matplotlib.pyplot import figure from rms_analysis import rms_analysis_main from rms_analysis import note_stats from rms_analysis import key_info_dictionary from rms_analysis import select_first_stable_note_by_delta_db from rms_analysis import select_first_stable_note_by_dur def do_plot(r, statsL ): fig,ax = plt.subplots() x = [ i / r.rms_srate for i in range(len(r.tdRmsDbV)) ] ax.plot( x,r.tdRmsDbV, color="blue" ) x = [ i / r.rms_srate for i in range(len(r.rmsDbV)) ] ax.plot( x,r.rmsDbV, color="green") ymx = np.max(r.tdRmsDbV) ymn = np.min(r.tdRmsDbV) for r in statsL: x = r.pkSmpSec ax.axvline(x,ymax=ymx,ymin=ymn) ax.text(x,r.pkDb+1,"%i ms" % r.durMs) ax.text(x,r.pkDb+2,"%4.1f dB" % r.pkDb) ax.text(x,r.pkDb+3,"%i us" % r.pulse_us) if hasattr(r,"MIN"): ax.plot(x,r.pkDb,marker="*",color="red") plt.show() def select_min_note( rr, statsL, minDurMs=600, minDb=8, contextSecs=10 ): sel_note_r = None for r in statsL: if r.pkDb > minDb and r.durMs > minDurMs: sel_note_r = r break #print(rr.tdRmsDbV.shape,rr.rmsDbV.shape) sL = [] if sel_note_r is None: print("ERROR: No min note found.") else: #print(sel_note_r) bi = max(0, int(round(sel_note_r.pkSmpSec * rr.rms_srate - contextSecs*rr.rms_srate))) ei = min(rr.tdRmsDbV.shape[0],int(round(sel_note_r.pkSmpSec * rr.rms_srate + contextSecs*rr.rms_srate))) rr.tdRmsDbV = rr.tdRmsDbV[bi:ei] rr.rmsDbV = rr.rmsDbV[bi:ei] offsSec = bi / rr.rms_srate for r in statsL: begSmpIdx = int(round(r.begSmpSec * rr.rms_srate)) endSmpIdx = int(round(r.endSmpSec * rr.rms_srate)) if begSmpIdx > bi and endSmpIdx < ei: r0 = r r0.begSmpSec = r.begSmpSec - offsSec r0.endSmpSec = r.endSmpSec - offsSec r0.pkSmpSec = r.pkSmpSec - offsSec if r.begSmpSec == sel_note_r.begSmpSec: setattr(r0,"MIN",True) sL.append(r0) return rr,sL def plot_note_audio_reanalysis( inDir ): rmsWndMs=300 rmsHopMs=30 dbLinRef=0.001 harmCandN=5 harmN=3 durDecayPct = 50 path = os.path.normpath(inDir) pathL = inDir.split(os.sep) take_id = int(pathL[-1]) midi_pitch = int(pathL[-2]) r = rms_analysis_main( inDir, midi_pitch, rmsWndMs=rmsWndMs, rmsHopMs=rmsHopMs, dbLinRef=dbLinRef, harmCandN=harmCandN, harmN=harmN, durDecayPct=durDecayPct ) r,statsL = select_min_note(r,r.statsL) do_plot(r,statsL) def plot_note_audio_reanalysis_dir( inDir, dirL ): for folder in dirL: path = os.path.join(inDir,str(folder),"0") plot_note_audio_reanalysis( path ) def get_all_note_durations( inDir, cacheFn ): folderL = os.listdir( inDir ) yL = [] for folder in folderL: takeId = 0 rmsWndMs=300 rmsHopMs=30 dbLinRef=0.001 harmCandN=5 harmN=3 durDecayPct = 40 path = os.path.normpath(inDir) midi_pitch = int( folder ) takePath = os.path.join(inDir,folder,str(takeId)) if os.path.isfile(os.path.join(takePath,'seq.json')): print(midi_pitch) r = rms_analysis_main( takePath, midi_pitch, rmsWndMs=rmsWndMs, rmsHopMs=rmsHopMs, dbLinRef=dbLinRef, harmCandN=harmCandN, harmN=harmN, durDecayPct=durDecayPct ) xL = [] for i,sr in enumerate(r.statsL): xL.append((r.pkUsL[i],sr.durMs,sr.pkDb,sr.quality)) yL.append((midi_pitch,xL)) with open(cacheFn,"wb") as f: pickle.dump(yL,f) def plot_all_note_durations( cacheFn, pitchL=None, axN=12, yamlCfgFn=None, minDurMs=800, maxPulseUs=None ): keyMapD = None if yamlCfgFn is not None: keyMapD = key_info_dictionary( keyMapL=None, yamlCfgFn=yamlCfgFn) fig,axL = plt.subplots(axN,1) fig.set_size_inches(18.5, 10.5*axN) #cL = list(mcd.CSS4_COLORS.values()) cL = ['black','brown','orangered','saddlebrown','peru','olivedrab','lightgreen','springgreen','cadetblue','slategray','royalblue','navy','darkviolet','deeppink','crimson'] yD=[] with open(cacheFn,"rb") as f: yD = dict(pickle.load(f)) cn = 3 #min(1,len(cL)//len(yD)) ci = 0 i = 0 for midi_pitch in pitchL: if (pitchL is not None and midi_pitch not in pitchL) or midi_pitch not in yD: continue xL = yD[midi_pitch] pkUsL,durMsL,pkDbL,qualityL = tuple(zip(*xL)) if maxPulseUs is not None: pkUsL = np.array(pkUsL) pkUsL = pkUsL[ pkUsL < maxPulseUs ] durMsL = durMsL[0:len(pkUsL)] pkDbL = pkDbL[0:len(pkUsL)] qualityL = qualityL[0:len(pkUsL)] axi = i//(len(pitchL)//axN) if keyMapD is None: legendLabel = str(midi_pitch) else: legendLabel = getattr(keyMapD[midi_pitch],'type') + " " + getattr(keyMapD[midi_pitch],'class') + str(midi_pitch) axL[axi].plot(pkUsL,durMsL,color=cL[ci],label=legendLabel) # plot the quietest stable note if minDurMs is not None: sni = select_first_stable_note_by_dur( durMsL, minDurMs ) if sni is not None: axL[axi].plot(pkUsL[sni],durMsL[sni],marker=".",color='red') axL[axi].text(pkUsL[sni],durMsL[sni] + 50,"%4.1f" % pkDbL[sni]) sni = select_first_stable_note_by_delta_db( pkDbL, pkUsL ) if sni is not None: axL[axi].plot(pkUsL[sni],durMsL[sni],marker=".",color='blue') axL[axi].text(pkUsL[sni],durMsL[sni] + 50,"%4.1f" % pkDbL[sni]) ci += cn if ci >= len(cL): ci = ci - len(cL) axL[axi].legend() i+=1 plt.show() def plot_quiet_note_db( cacheFn, yamlCfgFn, minDurMs=700 ): keyMapD = key_info_dictionary( keyMapL=None, yamlCfgFn=yamlCfgFn) yD=[] with open(cacheFn,"rb") as f: yD = dict(pickle.load(f)) dbL = [] for midi_pitch in range(24,108): pk0Db = 0 pk1Db = 0 minDb = 0 if midi_pitch in yD: xL = yD[midi_pitch] pkUsL,durMsL,pkDbL,qualityL = tuple(zip(*xL)) # plot the quietest stable note sni = select_first_stable_note_by_dur( durMsL, minDurMs ) if sni is not None: pk0Db = pkDbL[sni] sni = select_first_stable_note_by_delta_db( pkDbL, pkUsL ) if sni is not None: pk1Db = pkDbL[sni] minDb = min(pk0Db,pk1Db) dbL.append( (midi_pitch, minDb, pk0Db,pk1Db) ) fig,ax = plt.subplots() pitchL,minDbL,pk0DbL,pk1DbL = tuple(zip(*dbL)) ax.plot( pitchL, pk0DbL, label="dur" ) ax.plot( pitchL, pk1DbL, label="delta" ) #ax.plot( pitchL, minDbL, label="min" ) for i,pitch in enumerate(pitchL): ax.text( pitch, pk0DbL[i]+1, "%i %s" % (pitch,getattr(keyMapD[pitch],'type'))) ax.axhline( np.mean(minDbL), label="mean", color="blue" ) ax.axhline( np.median(minDbL), label="median", color="green" ) ax.legend() plt.show() def dump_hold_duty_pct( inDir ): pitchL = [] folderL = os.listdir(inDir) for folder in folderL: midi_pitch = int(folder) fn = os.path.join( inDir,folder,"0","seq.json") if not os.path.isfile(fn): print("No sequence file:%s" % (fn)) else: with open(fn,"r") as f: d = json.load(f) if 'holdDutyPct' in d: holdDutyPctL = [ [0,d['holdDutyPct']] ] else: holdDutyPctL = d['holdDutyPctL'] pitchL.append( {'pitch':midi_pitch, 'holdDutyPctL':holdDutyPctL} ) #print(midi_pitch, holdDutyPctL) pitchL = sorted(pitchL, key=lambda x: x['pitch']) for d in pitchL: print("{",d['pitch'],":",d['holdDutyPctL'],"},") if __name__ == "__main__": #inDir = sys.argv[1] #plot_note_analysis( inDir ) pitchL = [ 30,31,32,33,34,35 ] pitchL = [ 70,71,72,73,74,75 ] plot_note_audio_reanalysis_dir( "/home/kevin/temp/p_ac_3c",pitchL) durFn = "/home/kevin/temp/cache_note_dur.pickle" #get_all_note_durations("/home/kevin/temp/p_ac_3c",durFn) #plot_all_note_durations(durFn, np.arange(45,55),2,"p_ac.yml",800,20000) #plot_quiet_note_db(durFn,"p_ac.yml") #dump_hold_duty_pct( "/home/kevin/temp/p_ac_3c" )