annotate scripts/PitchBihist.py @ 36:3b67cd634b9a branch-tests

tests for pitch bihist, before refactoring
author Maria Panteli
date Thu, 14 Sep 2017 14:16:29 +0100
parents e50c63cf96be
children 2cc444441f42
rev   line source
Maria@4 1 # -*- coding: utf-8 -*-
Maria@4 2 """
Maria@4 3 Created on Tue Feb 2 22:26:10 2016
Maria@4 4
Maria@4 5 @author: mariapanteli
Maria@4 6 """
Maria@36 7 import numpy as np
Maria@36 8 import os
Maria@36 9 import scipy.signal
Maria@36 10
Maria@4 11 import smoothiecore as s
Maria@4 12
Maria@4 13
Maria@4 14 class PitchBihist:
Maria@4 15 def __init__(self, win2sec=8):
Maria@4 16 self.y = None
Maria@4 17 self.sr = None
Maria@4 18 self.chroma = None
Maria@4 19 self.chromasr = None
Maria@36 20 self.melodiasr = 44100. / 128.
Maria@4 21 self.bihist = None
Maria@4 22 self.win2sec = win2sec
Maria@4 23
Maria@36 24
Maria@36 25 def hz_to_cents(self, freq_Hz, ref_Hz=32.703, n_cents=1200):
Maria@36 26 """ convert frequency values from Hz to cents
Maria@36 27 reference frequency at C1
Maria@36 28 """
Maria@36 29 freq_cents = np.round(n_cents * np.log2(freq_Hz/ref_Hz))
Maria@36 30 return freq_cents
Maria@36 31
Maria@36 32
Maria@36 33 def wrap_to_octave(self, cents, octave_length=1200):
Maria@36 34 """ wrap to a single octave 0-1200
Maria@36 35 """
Maria@36 36 octave_cents = cents % octave_length
Maria@36 37 return octave_cents
Maria@36 38
Maria@36 39
Maria@36 40 def get_melody_from_file(self, melodia_file, stop_sec=None):
Maria@36 41 if not os.path.exists(melodia_file):
Maria@36 42 return []
Maria@36 43 data = np.loadtxt(melodia_file, delimiter=',')
Maria@36 44 times, freqs = (data[:, 0], data[:, 1])
Maria@36 45 self.chromasr = 1. / (times[1] - times[0])
Maria@36 46 if stop_sec is not None:
Maria@36 47 stop_idx = np.where(times < stop_sec)[0]
Maria@36 48 times, freqs = times[stop_idx], freqs[stop_idx]
Maria@36 49 freqs[freqs<=0] = np.nan
Maria@36 50 melody = freqs
Maria@36 51 return melody
Maria@36 52
Maria@36 53
Maria@36 54 def get_melody_matrix(self, melody):
Maria@36 55 n_bins = 60
Maria@36 56 n_frames = len(melody)
Maria@36 57 melody_cents = self.hz_to_cents(melody, n_cents=n_bins)
Maria@36 58 melody_octave = self.wrap_to_octave(melody_cents, octave_length=n_bins)
Maria@36 59 melody_matrix = np.zeros((n_bins, n_frames))
Maria@36 60 for time, pitch in enumerate(melody_octave):
Maria@36 61 if not np.isnan(pitch):
Maria@36 62 melody_matrix[int(pitch), time] = 1
Maria@36 63 return melody_matrix
Maria@36 64
Maria@36 65
Maria@36 66 def bihist_from_melodia(self, filename='sample_melodia.csv', secondframedecomp=True, stop_sec=None):
Maria@36 67 melody = self.get_melody_from_file(filename, stop_sec=stop_sec)
Maria@36 68 if len(melody) == 0:
Maria@36 69 self.bihist = []
Maria@36 70 return self.bihist
Maria@36 71 melody_matrix = self.get_melody_matrix(melody)
Maria@36 72 if secondframedecomp:
Maria@36 73 nbins, norigframes = melody_matrix.shape
Maria@36 74 win2 = int(round(self.win2sec*self.chromasr))
Maria@36 75 hop2 = int(round(0.5*self.chromasr))
Maria@36 76 if norigframes<=win2:
Maria@36 77 nframes = 1
Maria@36 78 win2 = norigframes
Maria@36 79 else:
Maria@36 80 nframes = int(np.ceil((norigframes-win2)/float(hop2)))
Maria@36 81 bihistframes = np.empty((nbins*nbins, nframes))
Maria@36 82 for i in range(nframes): # loop over all 8-sec frames
Maria@36 83 frame = melody_matrix[:, (i*hop2):(i*hop2+win2)]
Maria@36 84 bihist = self.bihistogram(frame)
Maria@36 85 bihist = np.reshape(bihist, -1)
Maria@36 86 bihistframes[:, i] = bihist
Maria@36 87 self.bihist = bihistframes
Maria@36 88 else:
Maria@36 89 self.bihist = self.bihistogram(melody_matrix)
Maria@36 90 return self.bihist
Maria@36 91
Maria@36 92
Maria@36 93 def bihistogram(self, spec, winsec=0.5, align=True):
Maria@36 94 win = int(round(winsec*self.chromasr))
Maria@36 95 ker = np.concatenate([np.zeros((win, 1)), np.ones((win+1, 1))], axis=0)
Maria@36 96 spec = spec.T # transpose to have franes as rows in convolution
Maria@36 97
Maria@36 98 # energy threshold
Maria@36 99 thr = 0.3*np.max(spec)
Maria@36 100 spec[spec < max(thr, 0)] = 0
Maria@36 101
Maria@36 102 # transitions via convolution
Maria@36 103 tra = scipy.signal.convolve2d(spec, ker, mode='same')
Maria@36 104 tra[spec > 0] = 0
Maria@36 105
Maria@36 106 # multiply with original
Maria@36 107 B = np.dot(tra.T, spec)
Maria@36 108
Maria@36 109 # normalize to [0, 1]
Maria@36 110 mxB = np.max(B)
Maria@36 111 mnB = np.min(B)
Maria@36 112 if mxB != mnB:
Maria@36 113 B = (B - mnB)/float(mxB-mnB)
Maria@36 114
Maria@36 115 # circshift to highest?
Maria@36 116 if align:
Maria@36 117 ref = np.argmax(np.sum(spec, axis=0))
Maria@36 118 B = np.roll(B, -ref, axis=0)
Maria@36 119 B = np.roll(B, -ref, axis=1)
Maria@36 120 return B
Maria@36 121
Maria@36 122
Maria@4 123 def bihist_from_chroma(self, filename='test.wav', secondframedecomp=True):
Maria@4 124 self.chroma, self.chromasr = s.get_smoothie_for_bihist(filename=filename, hopinsec=0.005)
Maria@4 125 if secondframedecomp:
Maria@4 126 win2 = int(round(8*self.chromasr))
Maria@4 127 hop2 = int(round(0.5*self.chromasr))
Maria@4 128 nbins, norigframes = self.chroma.shape
Maria@4 129 if norigframes<win2:
Maria@4 130 nframes = 1
Maria@4 131 else:
Maria@36 132 nframes = int(1+np.floor((norigframes-win2)/float(hop2)))
Maria@36 133 bihistframes = np.empty((nbins*nbins, nframes))
Maria@4 134 for i in range(nframes): # loop over all 8-sec frames
Maria@4 135 frame = self.chroma[:, (i*hop2):min((i*hop2+win2),norigframes)]
Maria@4 136 bihist = self.bihistogram(frame)
Maria@36 137 bihist = np.reshape(bihist, -1)
Maria@4 138 bihistframes[:, i] = bihist
Maria@4 139 self.bihist = bihistframes
Maria@4 140 else:
Maria@36 141 self.bihist = np.reshape(self.bihistogram(), -1)
Maria@4 142
Maria@4 143
Maria@4 144 def bihist_from_precomp_chroma(self, align=False):
Maria@4 145 win2 = int(round(self.win2sec*self.chromasr))
Maria@4 146 hop2 = int(round(0.5*self.chromasr))
Maria@4 147 nbins, norigframes = self.chroma.shape
Maria@4 148 if norigframes<win2:
Maria@4 149 nframes = 1
Maria@4 150 else:
Maria@36 151 nframes = int(1+np.floor((norigframes-win2)/float(hop2)))
Maria@36 152 bihistframes = np.empty((nbins*nbins, nframes))
Maria@4 153 for i in range(nframes): # loop over all 8-sec frames
Maria@4 154 frame = self.chroma[:, (i*hop2):min((i*hop2+win2),norigframes)]
Maria@4 155 bihist = self.bihistogram(frame, align=align)
Maria@36 156 bihist = np.reshape(bihist, -1)
Maria@4 157 bihistframes[:, i] = bihist
Maria@4 158 self.bihist = bihistframes
Maria@4 159
Maria@4 160 def get_pitchbihist(self, filename='test.wav'):
Maria@4 161 self.bihist_from_chroma(filename=filename)
Maria@4 162 return self.bihist
Maria@4 163
Maria@4 164 def get_pitchbihist_from_chroma(self, chroma=[], chromasr=[]):
Maria@4 165 self.chroma = chroma
Maria@4 166 self.chromasr = chromasr
Maria@4 167 self.bihist_from_precomp_chroma(align=False)
Maria@4 168 return self.bihist
Maria@4 169
Maria@4 170
Maria@4 171 if __name__ == '__main__':
Maria@4 172 pb = PitchBihist()
Maria@4 173 pb.get_pitchbihist()