picadae calibration programs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

plot_seq.py 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. ##| Copyright: (C) 2019-2020 Kevin Larke <contact AT larke DOT org>
  2. ##| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. import os, sys,json
  4. import matplotlib.pyplot as plt
  5. import numpy as np
  6. from scipy.io import wavfile
  7. from common import parse_yaml_cfg
  8. from rms_analysis import rms_analysis_main
  9. from rms_analysis import select_first_stable_note_by_delta_db
  10. from rms_analysis import select_first_stable_note_by_dur
  11. from rms_analysis import samples_to_linear_residual
  12. import rms_analysis as ra
  13. #from rms_analysis import audio_rms
  14. #from rms_analysis import locate_peak_indexes
  15. #from rms_analysis import audio_stft_rms
  16. #from rms_analysis import calc_harm_bins
  17. def is_nanV( xV ):
  18. for i in range(xV.shape[0]):
  19. if np.isnan( xV[i] ):
  20. return True
  21. return False
  22. def _find_max_take_id( inDir ):
  23. id = 0
  24. while os.path.isdir( os.path.join(inDir, "%i" % id) ):
  25. id += 1
  26. if id > 0:
  27. id -= 1
  28. return id
  29. def form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None ):
  30. # append the midi pitch to the input directory
  31. inDir = os.path.join( inDir, str(midi_pitch))
  32. dirL = os.listdir(inDir)
  33. pkL = []
  34. maxTakeNumber = 0
  35. # for each take in this directory
  36. for idir in dirL:
  37. take_number = int(idir)
  38. if not os.path.isfile(os.path.join( inDir,idir, "seq.json")):
  39. continue
  40. if analysisArgsD['useLastTakeOnlyFl']:
  41. if take_number > maxTakeNumber:
  42. pkL.clear()
  43. maxTakeNumber = take_number
  44. else:
  45. continue
  46. # analyze this takes audio and locate the note peaks
  47. r = rms_analysis_main( os.path.join(inDir,idir), midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
  48. # store the peaks in pkL[ (db,us) ]
  49. for db,us in zip(r.pkDbL,r.pkUsL):
  50. pkL.append( (db,us) )
  51. # sort the peaks on increasing attack pulse microseconds
  52. pkL = sorted( pkL, key= lambda x: x[1] )
  53. # merge sample points that separated by less than 'minSampleDistUs' milliseconds
  54. pkL = merge_close_sample_points( pkL, analysisArgsD['minSampleDistUs'] )
  55. # split pkL
  56. pkDbL,pkUsL = tuple(zip(*pkL))
  57. #-------------------------------------------
  58. # locate the first and last note
  59. min_pk_idx, max_pk_idx = find_min_max_peak_index( pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
  60. print("MIN MAX:",min_pk_idx,pkUsL[min_pk_idx],max_pk_idx,pkUsL[max_pk_idx])
  61. db1 = pkDbL[ max_pk_idx ]
  62. db0 = pkDbL[ min_pk_idx ]
  63. pulseUsL = []
  64. pulseDbL = []
  65. multValL = []
  66. for out_idx in range(128):
  67. # calc the target volume
  68. db = db0 + (out_idx * (db1-db0)/127.0)
  69. multi_value_count = 0
  70. # look for the target between each of the sampled points
  71. for i in range(1,len(pkDbL)):
  72. # if the target volume is between these two sample points
  73. if pkDbL[i-1] <= db and db < pkDbL[i]:
  74. # if the target has not already been located
  75. if len(pulseUsL) == out_idx:
  76. # interpolate the pulse time from between the sampled points
  77. frac = (db - pkDbL[i-1]) / (pkDbL[i] - pkDbL[i-1])
  78. us = pkUsL[i-1] + frac * (pkUsL[i] - pkUsL[i-1])
  79. db = pkDbL[i-1] + frac * (pkDbL[i] - pkDbL[i-1])
  80. pulseUsL.append(us)
  81. pulseDbL.append(db)
  82. else:
  83. # this target db value was found between multiple sampled points
  84. # therefore the sampled volume function is not monotonic
  85. multi_value_count += 1
  86. if multi_value_count > 0:
  87. multValL.append((out_idx,multi_value_count))
  88. if len(multValL) > 0:
  89. # print("Multi-value pulse locations were found during velocity table formation: ",multValL)
  90. pass
  91. return pulseUsL,pulseDbL,r.holdDutyPctL
  92. def merge_close_sample_points( pkDbUsL, minSampleDistanceUs ):
  93. avg0Us = np.mean(np.diff([ x[1] for x in pkDbUsL ]))
  94. n0 = len(pkDbUsL)
  95. while True and n0>0:
  96. us0 = None
  97. db0 = None
  98. for i,(db,us) in enumerate(pkDbUsL):
  99. if i > 0 and us - us0 < minSampleDistanceUs:
  100. us1 = (us0 + us)/2
  101. db1 = (db0 + db)/2
  102. pkDbUsL[i-1] = (db1,us1)
  103. del pkDbUsL[i]
  104. break
  105. else:
  106. us0 = us
  107. db0 = db
  108. if i+1 == len(pkDbUsL):
  109. break
  110. avg1Us = np.mean(np.diff([ x[1] for x in pkDbUsL ]))
  111. print("%i sample points deleted by merging close points." % (n0 - len(pkDbUsL)))
  112. print("Mean time between samples - before:%f after:%f " % (avg0Us,avg1Us))
  113. print("Min time between samples: %i " % (np.min(np.diff([x[1] for x in pkDbUsL]))))
  114. return pkDbUsL
  115. def _calc_resample_points( dPkDb, pkUs0, pkUs1, samplePerDb, minSampleDistUs ):
  116. dPkUs = pkUs1 - pkUs0
  117. sampleCnt = max(int(round(abs(dPkDb) * samplePerDb)),samplePerDb)
  118. dUs = max(int(round(dPkUs/sampleCnt)),minSampleDistUs)
  119. sampleCnt = int(round(dPkUs/dUs))
  120. dUs = int(round(dPkUs/sampleCnt))
  121. usL = [ pkUs0 + dUs*j for j in range(sampleCnt+1)]
  122. return usL
  123. def calc_resample_ranges( pkDbL, pkUsL, min_pk_idx, max_pk_idx, maxDeltaDb, samplePerDb, minSampleDistUs ):
  124. if min_pk_idx == 0:
  125. print("No silent notes were generated. Decrease the minimum peak level or the hold voltage.")
  126. return None
  127. resampleUsSet = set()
  128. refPkDb = pkDbL[min_pk_idx]
  129. #pkDbL = pkDbL[ pkIdxL ]
  130. for i in range( min_pk_idx, max_pk_idx+1 ):
  131. d = pkDbL[i] - pkDbL[i-1]
  132. usL = []
  133. # if this peak is less than maxDeltaDb above the previous pk or
  134. # it is below the previous max peak
  135. if d > maxDeltaDb or d <= 0 or pkDbL[i] < refPkDb:
  136. usL = _calc_resample_points( d, pkUsL[i-1], pkUsL[i], samplePerDb, minSampleDistUs )
  137. if d <= 0 and i + 1 < len(pkDbL):
  138. d = pkDbL[i+1] - pkDbL[i]
  139. usL += _calc_resample_points( d, pkUsL[i-1], pkUsL[i], samplePerDb, minSampleDistUs )
  140. if pkDbL[i] > refPkDb:
  141. refPkDb = pkDbL[i]
  142. if usL:
  143. resampleUsSet = resampleUsSet.union( usL )
  144. return resampleUsSet
  145. def form_resample_pulse_time_list( inDir, midi_pitch, analysisArgsD ):
  146. """" This function merges all available data from previous takes to form
  147. a new list of pulse times to sample.
  148. """
  149. inDir = os.path.join( inDir, str(midi_pitch) )
  150. dirL = os.listdir(inDir)
  151. pkL = []
  152. # for each take in this directory
  153. for idir in dirL:
  154. take_number = int(idir)
  155. # analyze this takes audio and locate the note peaks
  156. r = rms_analysis_main( os.path.join(inDir,idir), midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
  157. # store the peaks in pkL[ (db,us) ]
  158. for db,us in zip(r.pkDbL,r.pkUsL):
  159. pkL.append( (db,us) )
  160. # sort the peaks on increasing attack pulse microseconds
  161. pkL = sorted( pkL, key= lambda x: x[1] )
  162. # merge sample points that separated by less than 'minSampleDistUs' milliseconds
  163. pkL = merge_close_sample_points( pkL, analysisArgsD['minSampleDistUs'] )
  164. # split pkL
  165. pkDbL,pkUsL = tuple(zip(*pkL))
  166. # locate the first and last note
  167. min_pk_idx, max_pk_idx = find_min_max_peak_index( pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
  168. # estimate the microsecond locations to resample
  169. resampleUsSet = calc_resample_ranges( pkDbL, pkUsL, min_pk_idx, max_pk_idx, analysisArgsD['maxDeltaDb'], analysisArgsD['samplesPerDb'], analysisArgsD['minSampleDistUs'] )
  170. resampleUsL = sorted( list(resampleUsSet) )
  171. #print(resampleUsL)
  172. return resampleUsL, pkDbL, pkUsL
  173. def plot_curve( ax, pulseUsL, rmsDbV ):
  174. coeff = np.polyfit(pulseUsL,rmsDbV,5)
  175. func = np.poly1d(coeff)
  176. ax.plot( pulseUsL, func(pulseUsL), color='red')
  177. def plot_resample_pulse_times_0( inDir, analysisArgsD, midi_pitch, printDir="" ):
  178. newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, midi_pitch, analysisArgsD )
  179. velTblUsL,velTblDbL,_ = form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None )
  180. fig,ax = plt.subplots()
  181. ax.plot(pulseUsL,rmsDbV,marker='.' )
  182. for us in newPulseUsL:
  183. ax.axvline( x = us )
  184. print(len(velTblUsL))
  185. ax.plot(velTblUsL,velTblDbL,marker='.',linestyle='None',color='red')
  186. if printDir:
  187. plt.savefig(os.path.join(printDir,"plot_resample_times_0.png"),format="png")
  188. plt.show()
  189. def plot_resample_pulse_times( inDir, analysisArgsD, midi_pitch, printDir="" ):
  190. newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, midi_pitch, analysisArgsD )
  191. velTblUsL,velTblDbL,_ = form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None )
  192. fig,axL = plt.subplots(2,1,gridspec_kw={'height_ratios': [2, 1]})
  193. axL[0].plot(pulseUsL,rmsDbV,marker='.',color="red" )
  194. #plot_curve( ax, velTblUsL,velTblDbL)
  195. scoreV = samples_to_linear_residual( pulseUsL, rmsDbV)
  196. axL[0].plot(pulseUsL,rmsDbV + scoreV)
  197. axL[0].plot(pulseUsL,rmsDbV + np.power(scoreV,2.0))
  198. axL[0].plot(pulseUsL,rmsDbV - np.power(scoreV,2.0))
  199. axL[1].axhline(0.0,color='black')
  200. axL[1].axhline(1.0,color='black')
  201. axL[1].plot(pulseUsL,np.abs(scoreV * 100.0 / rmsDbV))
  202. axL[1].set_ylim((0.0,50))
  203. if printDir:
  204. plt.savefig(os.path.join(printDir,"plot_resample_times.png"),format="png")
  205. plt.show()
  206. def find_min_max_peak_index( pkDbL, minDb, maxDbOffs ):
  207. """
  208. Find the min db and max db peak.
  209. """
  210. # select only the peaks from rmsV[] to work with
  211. yV = pkDbL
  212. # get the max volume note
  213. max_i = np.argmax( yV )
  214. maxDb = yV[ max_i ]
  215. min_i = max_i
  216. # starting from the max volume peak go backwards
  217. for i in range( max_i, 0, -1 ):
  218. # if this peak is within maxDbOffs of the loudest then choose this one instead
  219. #if maxDb - yV[i] < maxDbOffs:
  220. # max_i = i
  221. # if this peak is less than minDb then the previous note is the min note
  222. if yV[i] < minDb:
  223. break
  224. min_i = i
  225. if min_i >= max_i:
  226. min_i = 0
  227. max_i = len(pkDbL)-1
  228. if min_i == 0:
  229. print("No silent notes were generated. Decrease the minimum peak level or the hold voltage.")
  230. return min_i, max_i
  231. def find_skip_peaks( rmsV, pkIdxL, min_pk_idx, max_pk_idx ):
  232. """ Fine peaks associated with longer attacks pulses that are lower than peaks with a shorter attack pulse.
  233. These peaks indicate degenerate portions of the pulse/db curve which must be skipped during velocity table formation
  234. """
  235. skipPkIdxL = []
  236. yV = rmsV[pkIdxL]
  237. refPkDb = yV[min_pk_idx]
  238. for i in range( min_pk_idx+1, max_pk_idx+1 ):
  239. if yV[i] > refPkDb:
  240. refPkDb = yV[i]
  241. else:
  242. skipPkIdxL.append(i)
  243. return skipPkIdxL
  244. def find_out_of_range_peaks( rmsV, pkIdxL, min_pk_idx, max_pk_idx, maxDeltaDb ):
  245. """ Locate peaks which are more than maxDeltaDb from the previous peak.
  246. If two peaks are separated by more than maxDeltaDb then the range must be resampled
  247. """
  248. oorPkIdxL = []
  249. yV = rmsV[pkIdxL]
  250. for i in range( min_pk_idx, max_pk_idx+1 ):
  251. if i > 0:
  252. d = yV[i] - yV[i-1]
  253. if d > maxDeltaDb or d < 0:
  254. oorPkIdxL.append(i)
  255. return oorPkIdxL
  256. def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
  257. """ Plot a single spectrum, 'specV' and the harmonic peak location boundaries."""
  258. binN = specV.shape[0]
  259. harmLBinL,harmMBinL,harmUBinL = ra.calc_harm_bins( srate, binHz, midiPitch, harmN )
  260. fundHz = harmMBinL[0] * binHz
  261. maxPlotHz = fundHz * (harmN+1)
  262. maxPlotBinN = int(round(maxPlotHz/binHz))
  263. hzV = np.arange(binN) * (srate/(binN*2))
  264. specV = 20.0 * np.log10(specV)
  265. ax.plot(hzV[0:maxPlotBinN], specV[0:maxPlotBinN] )
  266. for h0,h1,h2 in zip(harmLBinL,harmMBinL,harmUBinL):
  267. ax.axvline( x=h0 * binHz, color="blue")
  268. ax.axvline( x=h1 * binHz, color="black")
  269. ax.axvline( x=h2 * binHz, color="blue")
  270. ax.set_ylabel("dB : %i " % (midiPitch))
  271. def plot_spectral_ranges( inDir, pitchTakeL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbLinRef=0.001, printDir="" ):
  272. """ Plot the spectrum from one note (7th from last) in each attack pulse length sequence referred to by pitchTakeL."""
  273. plotN = len(pitchTakeL)
  274. fig,axL = plt.subplots(plotN,1)
  275. for plot_idx,(midiPitch,takeId) in enumerate(pitchTakeL):
  276. # get the audio and meta-data file names
  277. seqFn = os.path.join( inDir, str(midiPitch), str(takeId), "seq.json")
  278. audioFn = os.path.join( inDir, str(midiPitch), str(takeId), "audio.wav")
  279. # read the meta data object
  280. with open( seqFn, "rb") as f:
  281. r = json.load(f)
  282. # read the audio file
  283. srate, signalM = wavfile.read(audioFn)
  284. # convert the audio signal vector to contain only the first (left) channel
  285. if len(signalM.shape)>1:
  286. signalM = signalM[:,0].squeeze()
  287. sigV = signalM / float(0x7fff)
  288. # calc. the RMS envelope in the time domain
  289. rms0DbV, rms0_srate = ra.audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef )
  290. # locate the sample index of the peak of each note attack
  291. pkIdx0L = ra.locate_peak_indexes( rms0DbV, rms0_srate, r['eventTimeL'] )
  292. # select the 7th to last note for spectrum measurement
  293. #
  294. # TODO: come up with a better way to select the note to measure
  295. #
  296. spectrumSmpIdx = pkIdx0L[ len(pkIdx0L) - 7 ]
  297. # calc. the RMS envelope by taking the max spectral peak in each STFT window
  298. rmsDbV, rms_srate, specV, specHopIdx, binHz = ra.audio_stft_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, spectrumSmpIdx)
  299. # specV[] is the spectrum of the note at spectrumSmpIdx
  300. # plot the spectrum and the harmonic selection ranges
  301. plot_spectrum( axL[plot_idx], srate, binHz, specV, midiPitch, harmN )
  302. axL[-1].set_xlabel("Hertz")
  303. if printDir:
  304. plt.savefig(os.path.join(printDir,"plot_spectral_ranges.png"),format="png")
  305. plt.show()
  306. def td_plot( ax, inDir, midi_pitch, id, analysisArgsD ):
  307. #
  308. r = rms_analysis_main( inDir, midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
  309. # find min/max peak in the sequence
  310. min_pk_idx, max_pk_idx = find_min_max_peak_index( r.pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
  311. # find ranges of the sequence which should be skipped because they are noisy or unreliable
  312. skipPkIdxL = find_skip_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx )
  313. # find peaks whose difference to surrounding peaks is greater than 'maxDeltaDb'.
  314. jmpPkIdxL = find_out_of_range_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx, analysisArgsD['maxDeltaDb'] )
  315. secV = np.arange(0,len(r.rmsDbV)) / r.rms_srate
  316. # plot the harmonic RMS signal
  317. ax.plot( secV, r.rmsDbV, color='blue', label="Harmonic" )
  318. # plot the time-domain RMS signal
  319. ax.plot( np.arange(0,len(r.tdRmsDbV)) / r.rms_srate, r.tdRmsDbV, color="black", label="TD" )
  320. # print note beg/end/peak boundaries
  321. for i,(begMs, endMs) in enumerate(r.eventTimeL):
  322. pkSec = r.pkIdxL[i] / r.rms_srate
  323. endSec = pkSec + r.statsL[i].durMs / 1000.0
  324. ax.axvline( x=begMs/1000.0, color="green")
  325. ax.axvline( x=endMs/1000.0, color="red")
  326. ax.axvline( x=pkSec, color="black")
  327. ax.axvline( x=endSec, color="black")
  328. ax.text(begMs/1000.0, 20.0, str(i) )
  329. # plot peak markers
  330. for i,pki in enumerate(r.pkIdxL):
  331. marker = 4 if i==min_pk_idx or i==max_pk_idx else 5
  332. color = "red" if i in skipPkIdxL else "black"
  333. ax.plot( [pki / r.rms_srate], [ r.rmsDbV[pki] ], marker=marker, color=color)
  334. if i in jmpPkIdxL:
  335. ax.plot( [pki / r.rms_srate], [ r.rmsDbV[pki] ], marker=6, color="blue")
  336. ax.legend();
  337. ax.set_ylabel("dB");
  338. return r
  339. def do_td_plot( inDir, analysisArgs, midi_pitch, takeId, printDir="" ):
  340. fig,axL = plt.subplots(3,1)
  341. fig.set_size_inches(18.5, 10.5, forward=True)
  342. # parse the file name
  343. inDir = os.path.join(inDir,str(midi_pitch),str(takeId));
  344. # plot the time domain signal
  345. r = td_plot(axL[0],inDir,midi_pitch,takeId,analysisArgs)
  346. qualityV = np.array([ x.quality for x in r.statsL ]) * np.max(r.pkDbL)
  347. durMsV = np.array([ x.durMs for x in r.statsL ])
  348. avgV = np.array([ x.durAvgDb for x in r.statsL ])
  349. dV = np.diff(r.pkDbL) / r.pkDbL[1:]
  350. axL[1].plot( r.pkUsL, r.pkDbL, marker='.', label="harmonic" )
  351. #axL[1].plot( r.pkUsL, qualityV, marker='.', label="quality" )
  352. axL[1].plot( r.pkUsL, avgV, marker='.', label="harm-td avg" )
  353. axL[1].legend()
  354. axL[1].set_ylabel("dB");
  355. #axL[2].plot( r.pkUsL, durMsV, marker='.' )
  356. axL[2].plot( r.pkUsL[1:], dV, marker='.',label='delta')
  357. axL[2].set_ylim([-1,1])
  358. axL[2].legend()
  359. axL[2].set_ylabel("dB");
  360. axL[2].set_xlabel("Microseconds")
  361. sni = select_first_stable_note_by_dur( durMsV )
  362. if sni is not None:
  363. axL[1].plot( r.pkUsL[sni], r.pkDbL[sni], marker='*', color='red')
  364. sni = select_first_stable_note_by_delta_db( r.pkDbL )
  365. if sni is not None:
  366. axL[2].plot( r.pkUsL[sni], dV[sni-1], marker='*', color='red')
  367. for i,s in enumerate(r.statsL):
  368. axL[1].text( r.pkUsL[i], r.pkDbL[i] + 1, "%i" % (i))
  369. for i in range(1,len(r.pkUsL)):
  370. axL[2].text( r.pkUsL[i], dV[i-1], "%i" % (i))
  371. if printDir:
  372. plt.savefig(os.path.join(printDir,"do_td_plot.png"),format="png")
  373. plt.show()
  374. def do_td_multi_plot( inDir, analysisArgs, pitchTakeL, printPlotFl=False ):
  375. #midi_pitch = int(inDir.split("/")[-1])
  376. fig,axL = plt.subplots(len(pitchTakeL),1)
  377. for id,((pitch,takeId),ax) in enumerate(zip(pitchTakeL,axL)):
  378. td_plot(ax, os.path.join(inDir,str(pitch),str(takeId)), pitch, takeId, analysisArgs )
  379. ax.set_xlabel("Seconds")
  380. if printDir:
  381. plt.savefig(os.path.join(printDir,"multi_plot.png"),format="png")
  382. plt.show()
  383. if __name__ == "__main__":
  384. printDir = os.path.expanduser("~/src/picadae_ac_3/doc")
  385. cfgFn = sys.argv[1]
  386. inDir = sys.argv[2]
  387. mode = sys.argv[3]
  388. cfg = parse_yaml_cfg( cfgFn )
  389. if mode == "td_plot":
  390. # python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_plot 60 10
  391. pitch = int(sys.argv[4])
  392. take_id = int(sys.argv[5])
  393. do_td_plot(inDir,cfg.analysisArgs, pitch, take_id, printDir )
  394. elif mode == "td_multi_plot" or mode == 'plot_spectral_ranges':
  395. pitchTakeIdL = []
  396. for i in range(4,len(sys.argv),2):
  397. pitchTakeIdL.append( (int(sys.argv[i]), int(sys.argv[i+1])) )
  398. if mode == "td_multi_plot":
  399. # python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_multi_plot 36 2 48 3 60 4
  400. do_td_multi_plot(inDir, cfg.analysisArgs, pitchTakeIdL, printDir )
  401. else:
  402. # python plot_seq.py p_ac.yml ~/temp/p_ac_3_od plot_spectral_ranges 36 2 48 3 60 4
  403. plot_spectral_ranges( inDir, pitchTakeIdL, printDir=printDir )
  404. elif mode == "resample_pulse_times":
  405. # python plot_seq.py p_ac.yml ~/temp/p_ac_3_od resample_pulse_times 60
  406. pitch = int(sys.argv[4])
  407. plot_resample_pulse_times( inDir, cfg.analysisArgs, pitch, printDir )
  408. else:
  409. print("Unknown plot mode:%s" % (mode))