annotate scripts/PitchBihist.py @ 105:edd82eb89b4b branch-tests tip

Merge
author Maria Panteli
date Sun, 15 Oct 2017 13:36:59 +0100
parents 0e70021f251e
children
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
Maria@4 12 class PitchBihist:
Maria@4 13 def __init__(self, win2sec=8):
Maria@4 14 self.win2sec = win2sec
Maria@37 15 self.hop2sec = 0.5
Maria@37 16 self.melody_sr = 44100. / 128.
Maria@4 17
Maria@36 18
Maria@36 19 def hz_to_cents(self, freq_Hz, ref_Hz=32.703, n_cents=1200):
Maria@36 20 """ convert frequency values from Hz to cents
Maria@36 21 reference frequency at C1
Maria@36 22 """
Maria@36 23 freq_cents = np.round(n_cents * np.log2(freq_Hz/ref_Hz))
Maria@36 24 return freq_cents
Maria@36 25
Maria@36 26
Maria@36 27 def wrap_to_octave(self, cents, octave_length=1200):
Maria@36 28 """ wrap to a single octave 0-1200
Maria@36 29 """
Maria@36 30 octave_cents = cents % octave_length
Maria@36 31 return octave_cents
Maria@36 32
Maria@36 33
Maria@36 34 def get_melody_from_file(self, melodia_file, stop_sec=None):
Maria@36 35 if not os.path.exists(melodia_file):
Maria@36 36 return []
Maria@36 37 data = np.loadtxt(melodia_file, delimiter=',')
Maria@36 38 times, freqs = (data[:, 0], data[:, 1])
Maria@36 39 if stop_sec is not None:
Maria@36 40 stop_idx = np.where(times < stop_sec)[0]
Maria@36 41 times, freqs = times[stop_idx], freqs[stop_idx]
Maria@36 42 freqs[freqs<=0] = np.nan
Maria@36 43 melody = freqs
Maria@38 44 return melody
Maria@36 45
Maria@36 46
Maria@36 47 def get_melody_matrix(self, melody):
Maria@36 48 n_bins = 60
Maria@36 49 n_frames = len(melody)
Maria@36 50 melody_cents = self.hz_to_cents(melody, n_cents=n_bins)
Maria@36 51 melody_octave = self.wrap_to_octave(melody_cents, octave_length=n_bins)
Maria@36 52 melody_matrix = np.zeros((n_bins, n_frames))
Maria@36 53 for time, pitch in enumerate(melody_octave):
Maria@36 54 if not np.isnan(pitch):
Maria@36 55 melody_matrix[int(pitch), time] = 1
Maria@36 56 return melody_matrix
Maria@36 57
Maria@36 58
Maria@37 59 def bihistogram(self, spec, spec_sr=None, winsec=0.5, align=True):
Maria@37 60 if spec_sr is None:
Maria@37 61 # assume spec is melody_matrix with default sr
Maria@37 62 spec_sr = self.melody_sr
Maria@37 63 win = int(round(winsec * spec_sr))
Maria@36 64 ker = np.concatenate([np.zeros((win, 1)), np.ones((win+1, 1))], axis=0)
Maria@37 65 spec = spec.T # transpose to have frames as rows in convolution
Maria@36 66
Maria@36 67 # energy threshold
Maria@36 68 thr = 0.3*np.max(spec)
Maria@36 69 spec[spec < max(thr, 0)] = 0
Maria@36 70
Maria@36 71 # transitions via convolution
Maria@36 72 tra = scipy.signal.convolve2d(spec, ker, mode='same')
Maria@36 73 tra[spec > 0] = 0
Maria@36 74
Maria@36 75 # multiply with original
Maria@36 76 B = np.dot(tra.T, spec)
Maria@36 77
Maria@36 78 # normalize to [0, 1]
Maria@36 79 mxB = np.max(B)
Maria@36 80 mnB = np.min(B)
Maria@36 81 if mxB != mnB:
Maria@36 82 B = (B - mnB)/float(mxB-mnB)
Maria@36 83
Maria@36 84 # circshift to highest?
Maria@36 85 if align:
Maria@36 86 ref = np.argmax(np.sum(spec, axis=0))
Maria@36 87 B = np.roll(B, -ref, axis=0)
Maria@36 88 B = np.roll(B, -ref, axis=1)
Maria@36 89 return B
Maria@36 90
Maria@36 91
Maria@39 92 def second_frame_decomposition(self, norigframes):
Maria@39 93 win2 = int(round(self.win2sec * self.melody_sr))
Maria@39 94 hop2 = int(round(self.hop2sec * self.melody_sr))
Maria@39 95 print win2, hop2, norigframes
Maria@39 96 if norigframes<=win2:
Maria@39 97 nframes = 1
Maria@39 98 win2 = norigframes
Maria@39 99 else:
Maria@39 100 nframes = np.int(1+np.floor((norigframes-win2)/float(hop2)))
Maria@39 101 return nframes, win2, hop2
Maria@39 102
Maria@39 103
Maria@38 104 def bihist_from_melodia(self, filename='sample_melodia.csv', secondframedecomp=True, stop_sec=None):
Maria@38 105 melody = self.get_melody_from_file(filename, stop_sec=stop_sec)
Maria@38 106 if len(melody) == 0:
Maria@38 107 return []
Maria@38 108 melody_matrix = self.get_melody_matrix(melody)
Maria@38 109 bihist = []
Maria@38 110 if secondframedecomp:
Maria@38 111 nbins, norigframes = melody_matrix.shape
Maria@39 112 nframes, win2, hop2 = self.second_frame_decomposition(norigframes)
Maria@38 113 bihistframes = np.empty((nbins*nbins, nframes))
Maria@38 114 for i in range(nframes): # loop over all 8-sec frames
Maria@38 115 frame = melody_matrix[:, (i*hop2):(i*hop2+win2)]
Maria@38 116 bihist = self.bihistogram(frame)
Maria@38 117 bihist = np.reshape(bihist, -1)
Maria@38 118 bihistframes[:, i] = bihist
Maria@38 119 bihist = bihistframes
Maria@38 120 else:
Maria@38 121 bihist = self.bihistogram(melody_matrix)
Maria@38 122 return bihist
Maria@38 123
Maria@38 124
Maria@4 125 if __name__ == '__main__':
Maria@4 126 pb = PitchBihist()
Maria@38 127 melody = np.concatenate([660 * np.ones(250), 440 * np.ones(500)])
Maria@38 128 melody_matrix = pb.get_melody_matrix(melody)
Maria@38 129 pb.bihistogram(melody_matrix, align=True)
Maria@38 130