Added min/max volume note and skip note detection.

This commit is contained in:
kpl 2019-08-20 20:45:53 -04:00
parent c3db8c8ab6
commit ac9061a72d

View File

@ -11,6 +11,49 @@ def is_nanV( xV ):
return True return True
return False return False
def find_min_max_peak_index( rmsV, pkIdxL, minDb, maxDbOffs=0.5 ):
# select only the peaks from rmsV[] to work with
yV = rmsV[ pkIdxL ]
# get the max volume note
max_i = np.argmax( yV )
maxDb = yV[ max_i ]
min_i = max_i
# starting from the max volume peak go backwards
for i in range( max_i, 0, -1 ):
# if this peak is within maxDbOffs of the loudest then choose this one instead
if maxDb - yV[i] < maxDbOffs:
max_i = i
# if this peak is less than minDb then the previous note is the min note
if yV[i] < minDb:
break
min_i = i
assert( min_i < max_i )
return min_i, max_i
def find_skip_peaks( rmsV, pkIdxL, min_pk_idx, max_pk_idx ):
skipPkIdxL = []
yV = rmsV[pkIdxL]
refPkDb = yV[min_pk_idx]
for i in range( min_pk_idx+1, max_pk_idx+1 ):
if yV[i] > refPkDb:
refPkDb = yV[i]
else:
skipPkIdxL.append(i)
return skipPkIdxL
def calc_harm_bins( srate, binHz, midiPitch, harmN ): def calc_harm_bins( srate, binHz, midiPitch, harmN ):
@ -33,9 +76,14 @@ def calc_harm_bins( srate, binHz, midiPitch, harmN ):
return fund_l_binL, fund_m_binL, fund_u_binL return fund_l_binL, fund_m_binL, fund_u_binL
def rms_to_db( xV, rms_srate, refWndMs ):
dbWndN = int(round(refWndMs * rms_srate / 1000.0))
dbRef = ref = np.mean(xV[0:dbWndN])
rmsDbV = 20.0 * np.log10( xV / dbRef )
return rmsDbV
def audio_rms( srate, xV, rmsWndMs, hopMs ): def audio_rms( srate, xV, rmsWndMs, hopMs, refWndMs ):
wndSmpN = int(round( rmsWndMs * srate / 1000.0)) wndSmpN = int(round( rmsWndMs * srate / 1000.0))
hopSmpN = int(round( hopMs * srate / 1000.0)) hopSmpN = int(round( hopMs * srate / 1000.0))
@ -60,19 +108,12 @@ def audio_rms( srate, xV, rmsWndMs, hopMs ):
i += hopSmpN i += hopSmpN
j += 1 j += 1
return yV, srate / hopSmpN
def audio_db_rms( srate, xV, rmsWndMs, hopMs, dbRefWndMs ): rms_srate = srate / hopSmpN
return rms_to_db( yV, rms_srate, refWndMs ), rms_srate
rmsV, rms_srate = audio_rms( srate, xV, rmsWndMs, hopMs )
dbWndN = int(round(dbRefWndMs * rms_srate / 1000.0))
dbRef = ref = np.mean(rmsV[0:dbWndN])
return 20.0 * np.log10( rmsV / dbRef ), rms_srate
def audio_stft_rms( srate, xV, rmsWndMs, hopMs, refWndMs, spectrumIdx ):
def audio_stft_rms( srate, xV, rmsWndMs, hopMs, spectrumIdx ):
wndSmpN = int(round( rmsWndMs * srate / 1000.0)) wndSmpN = int(round( rmsWndMs * srate / 1000.0))
hopSmpN = int(round( hopMs * srate / 1000.0)) hopSmpN = int(round( hopMs * srate / 1000.0))
@ -88,18 +129,14 @@ def audio_stft_rms( srate, xV, rmsWndMs, hopMs, spectrumIdx ):
for i in range(xM.shape[1]): for i in range(xM.shape[1]):
mV[i] = np.max(np.sqrt(np.abs(xM[:,i]))) mV[i] = np.max(np.sqrt(np.abs(xM[:,i])))
return mV, srate / hopSmpN, specV, specHopIdx, binHz
def audio_stft_db_rms( srate, xV, rmsWndMs, hopMs, dbRefWndMs, spectrumIdx ): rms_srate = srate / hopSmpN
rmsV, rms_srate, specV, specHopIdx, binHz = audio_stft_rms( srate, xV, rmsWndMs, hopMs, spectrumIdx ) mV = rms_to_db( mV, rms_srate, refWndMs )
dbWndN = int(round(dbRefWndMs * rms_srate / 1000.0)) return mV, rms_srate, specV, specHopIdx, binHz
dbRef = ref = np.mean(rmsV[0:dbWndN])
rmsDbV = 20.0 * np.log10( rmsV / dbRef )
return rmsDbV, rms_srate, specV, specHopIdx, binHz
def audio_harm_rms( srate, xV, rmsWndMs, hopMs, midiPitch, harmCandN, harmN ): def audio_harm_rms( srate, xV, rmsWndMs, hopMs, dbRefWndMs, midiPitch, harmCandN, harmN ):
wndSmpN = int(round( rmsWndMs * srate / 1000.0)) wndSmpN = int(round( rmsWndMs * srate / 1000.0))
hopSmpN = int(round( hopMs * srate / 1000.0)) hopSmpN = int(round( hopMs * srate / 1000.0))
@ -125,20 +162,9 @@ def audio_harm_rms( srate, xV, rmsWndMs, hopMs, midiPitch, harmCandN, harmN ):
rms_srate = srate / hopSmpN
return rmsV, srate / hopSmpN, binHz rmsV = rms_to_db( rmsV, rms_srate, dbRefWndMs )
return rmsV, rms_srate, binHz
def audio_harm_db_rms( srate, xV, rmsWndMs, hopMs, dbRefWndMs, midiPitch, harmCandN, harmN ):
rmsV, rms_srate, binHz = audio_harm_rms( srate, xV, rmsWndMs, hopMs, midiPitch, harmCandN, harmN )
dbWndN = int(round(dbRefWndMs * rms_srate / 1000.0))
dbRef = ref = np.mean(rmsV[0:dbWndN])
rmsDbV = 20.0 * np.log10( rmsV / dbRef )
return rmsDbV, rms_srate, binHz
@ -156,7 +182,8 @@ def locate_peak_indexes( xV, xV_srate, eventMsL ):
def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ): def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
""" Plot a single spectrum, 'specV' and the harmonic peak location boundaries."""
binN = specV.shape[0] binN = specV.shape[0]
harmLBinL,harmMBinL,harmUBinL = calc_harm_bins( srate, binHz, midiPitch, harmN ) harmLBinL,harmMBinL,harmUBinL = calc_harm_bins( srate, binHz, midiPitch, harmN )
@ -178,7 +205,8 @@ def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
ax.set_ylabel(str(midiPitch)) ax.set_ylabel(str(midiPitch))
def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbRefWndMs=500 ): def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbRefWndMs=500 ):
""" Plot the spectrum from one note (7th from last) in each attack pulse length sequence referred to by pitchL."""
plotN = len(pitchL) plotN = len(pitchL)
fig,axL = plt.subplots(plotN,1) fig,axL = plt.subplots(plotN,1)
@ -197,7 +225,7 @@ def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbR
sigV = signalM / float(0x7fff) sigV = signalM / float(0x7fff)
# calc. the RMS envelope in the time domain # calc. the RMS envelope in the time domain
rms0DbV, rms0_srate = audio_db_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs ) rms0DbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs )
# locate the sample index of the peak of each note attack # locate the sample index of the peak of each note attack
pkIdx0L = locate_peak_indexes( rms0DbV, rms0_srate, r['eventTimeL'] ) pkIdx0L = locate_peak_indexes( rms0DbV, rms0_srate, r['eventTimeL'] )
@ -211,7 +239,7 @@ def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbR
# calc. the RMS envelope by taking the max spectral peak in each STFT window # calc. the RMS envelope by taking the max spectral peak in each STFT window
rmsDbV, rms_srate, specV, specHopIdx, binHz = audio_stft_db_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs, spectrumSmpIdx) rmsDbV, rms_srate, specV, specHopIdx, binHz = audio_stft_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs, spectrumSmpIdx)
# specV[] is the spectrum of the note at spectrumSmpIdx # specV[] is the spectrum of the note at spectrumSmpIdx
@ -222,12 +250,12 @@ def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbR
def do_td_plot( inDir ): def do_td_plot( inDir ):
rmsWndMs = 300 rmsWndMs = 300
rmsHopMs = 30 rmsHopMs = 30
dbRefWndMs = 500 dbRefWndMs = 500
harmCandN = 5 harmCandN = 5
harmN = 3 harmN = 3
minAttkDb = 5.0
seqFn = os.path.join( inDir, "seq.json") seqFn = os.path.join( inDir, "seq.json")
audioFn = os.path.join( inDir, "audio.wav") audioFn = os.path.join( inDir, "audio.wav")
@ -241,12 +269,17 @@ def do_td_plot( inDir ):
srate, signalM = wavfile.read(audioFn) srate, signalM = wavfile.read(audioFn)
sigV = signalM / float(0x7fff) sigV = signalM / float(0x7fff)
rms0DbV, rms0_srate = audio_db_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs ) rms0DbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs )
rmsDbV, rms_srate, binHz = audio_harm_db_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs, midiPitch, harmCandN, harmN ) rmsDbV, rms_srate, binHz = audio_harm_rms( srate, sigV, rmsWndMs, rmsHopMs, dbRefWndMs, midiPitch, harmCandN, harmN )
pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'] ) pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'] )
min_pk_idx, max_pk_idx = find_min_max_peak_index( rmsDbV, pkIdxL, minAttkDb )
skipPkIdxL = find_skip_peaks( rmsDbV, pkIdxL, min_pk_idx, max_pk_idx )
fig,ax = plt.subplots() fig,ax = plt.subplots()
fig.set_size_inches(18.5, 10.5, forward=True) fig.set_size_inches(18.5, 10.5, forward=True)
@ -255,13 +288,17 @@ def do_td_plot( inDir ):
ax.plot( secV, rmsDbV ) ax.plot( secV, rmsDbV )
ax.plot( np.arange(0,len(rms0DbV)) / rms0_srate, rms0DbV, color="black" ) ax.plot( np.arange(0,len(rms0DbV)) / rms0_srate, rms0DbV, color="black" )
for begMs, endMs in r['eventTimeL']: # print beg/end boundaries
for i,(begMs, endMs) in enumerate(r['eventTimeL']):
ax.axvline( x=begMs/1000.0, color="green") ax.axvline( x=begMs/1000.0, color="green")
ax.axvline( x=endMs/1000.0, color="red") ax.axvline( x=endMs/1000.0, color="red")
ax.text(begMs/1000.0, 20.0, str(i) )
# plot peak markers
for i,pki in enumerate(pkIdxL): for i,pki in enumerate(pkIdxL):
ax.plot( [pki / rms_srate], [ rmsDbV[pki] ], marker='.', color="black") marker = "o" if i==min_pk_idx or i==max_pk_idx else "."
color = "red" if i in skipPkIdxL else "black"
ax.plot( [pki / rms_srate], [ rmsDbV[pki] ], marker=marker, color=color)
plt.show() plt.show()