fix timelines and plot sizes in onset and pitch
[aubio.git] / python / aubio / task / pitch.py
1 from aubio.task.task import task
2 from aubio.task.silence import tasksilence
3 from aubio.task.utils import * 
4 from aubio.aubioclass import *
5
6 class taskpitch(task):
7         def __init__(self,input,params=None):
8                 task.__init__(self,input,params=params)
9                 self.shortlist = [0. for i in range(self.params.pitchsmooth)]
10                 if self.params.pitchmode == 'yinfft':
11                         yinthresh = self.params.yinfftthresh
12                 elif self.params.pitchmode == 'yin':
13                         yinthresh = self.params.yinthresh
14                 else:
15                         yinthresh = 0.
16                 self.pitchdet  = pitchdetection(mode=get_pitch_mode(self.params.pitchmode),
17                         bufsize=self.params.bufsize,
18                         hopsize=self.params.hopsize,
19                         channels=self.channels,
20                         samplerate=self.srate,
21                         omode=self.params.omode,
22                         yinthresh=yinthresh)
23
24         def __call__(self):
25                 from aubio.median import short_find
26                 task.__call__(self)
27                 if (aubio_silence_detection(self.myvec(),self.params.silence)==1):
28                         freq = -1.
29                 else:
30                         freq = self.pitchdet(self.myvec)
31                 minpitch = self.params.pitchmin
32                 maxpitch = self.params.pitchmax
33                 if maxpitch and freq > maxpitch : 
34                         freq = -1.
35                 elif minpitch and freq < minpitch :
36                         freq = -1.
37                 if self.params.pitchsmooth:
38                         self.shortlist.append(freq)
39                         self.shortlist.pop(0)
40                         smoothfreq = short_find(self.shortlist,
41                                 len(self.shortlist)/2)
42                         return smoothfreq
43                 else:
44                         return freq
45
46         def compute_all(self):
47                 """ Compute data """
48                 mylist    = []
49                 while(self.readsize==self.params.hopsize):
50                         freq = self()
51                         mylist.append(freq)
52                         if self.params.verbose:
53                                 self.fprint("%s\t%s" % (self.frameread*self.params.step,freq))
54                 return mylist
55
56         def gettruth(self):
57                 """ extract ground truth array in frequency """
58                 import os.path
59                 """ from wavfile.txt """
60                 datafile = self.input.replace('.wav','.txt')
61                 if datafile == self.input: datafile = ""
62                 """ from file.<midinote>.wav """
63                 # FIXME very weak check
64                 floatpit = self.input.split('.')[-2]
65                 if not os.path.isfile(datafile) and len(self.input.split('.')) < 3:
66                         print "no ground truth "
67                         return False,False
68                 elif floatpit:
69                         try:
70                                 self.truth = float(floatpit)
71                                 #print "ground truth found in filename:", self.truth
72                                 tasksil = tasksilence(self.input,params=self.params)
73                                 time,pitch =[],[]
74                                 while(tasksil.readsize==tasksil.params.hopsize):
75                                         tasksil()
76                                         time.append(tasksil.params.step*(tasksil.frameread))
77                                         if not tasksil.issilence:
78                                                 pitch.append(self.truth)
79                                         else:
80                                                 pitch.append(-1.)
81                                 return time,pitch
82                         except ValueError:
83                                 # FIXME very weak check
84                                 if not os.path.isfile(datafile):
85                                         print "no ground truth found"
86                                         return 0,0
87                                 else:
88                                         from aubio.txtfile import read_datafile
89                                         values = read_datafile(datafile)
90                                         time, pitch = [], []
91                                         for i in range(len(values)):
92                                                 time.append(values[i][0])
93                                                 if values[i][1] == 0.0:
94                                                         pitch.append(-1.)
95                                                 else:
96                                                         pitch.append(aubio_freqtomidi(values[i][1]))
97                                         return time,pitch
98
99         def oldeval(self,results):
100                 def mmean(l):
101                         return sum(l)/max(float(len(l)),1)
102
103                 from aubio.median import percental 
104                 timet,pitcht = self.gettruth()
105                 res = []
106                 for i in results:
107                         #print i,self.truth
108                         if i <= 0: pass
109                         else: res.append(self.truth-i)
110                 if not res or len(res) < 3: 
111                         avg = self.truth; med = self.truth 
112                 else:
113                         avg = mmean(res) 
114                         med = percental(res,len(res)/2) 
115                 return self.truth, self.truth-med, self.truth-avg
116
117         def eval(self,pitch,tol=0.5):
118                 timet,pitcht = self.gettruth()
119                 pitch = [aubio_freqtomidi(i) for i in pitch]
120                 for i in range(len(pitch)):
121                         if pitch[i] == "nan" or pitch[i] == -1:
122                                 pitch[i] = -1
123                 time = [ (i+self.params.pitchdelay)*self.params.step for i in range(len(pitch)) ]
124                 #print len(timet),len(pitcht)
125                 #print len(time),len(pitch)
126                 if len(timet) != len(time):
127                         time = time[1:len(timet)+1]
128                         pitch = pitch[1:len(pitcht)+1]
129                         #pitcht = [aubio_freqtomidi(i) for i in pitcht]
130                         for i in range(len(pitcht)):
131                                 if pitcht[i] == "nan" or pitcht[i] == "-inf" or pitcht[i] == -1:
132                                         pitcht[i] = -1
133                 assert len(timet) == len(time)
134                 assert len(pitcht) == len(pitch)
135                 osil, esil, opit, epit, echr = 0, 0, 0, 0, 0
136                 for i in range(len(pitcht)):
137                         if pitcht[i] == -1: # currently silent
138                                 osil += 1 # count a silence
139                                 if pitch[i] <= 0. or pitch[i] == "nan": 
140                                         esil += 1 # found a silence
141                         else:
142                                 opit +=1
143                                 if abs(pitcht[i] - pitch[i]) < tol:
144                                         epit += 1
145                                         echr += 1
146                                 elif abs(pitcht[i] - pitch[i]) % 12. < tol:
147                                         echr += 1
148                                 #else:
149                                 #       print timet[i], pitcht[i], time[i], pitch[i]
150                 #print "origsilence", "foundsilence", "origpitch", "foundpitch", "orig pitchroma", "found pitchchroma"
151                 #print 100.*esil/float(osil), 100.*epit/float(opit), 100.*echr/float(opit)
152                 return osil, esil, opit, epit, echr
153
154         def plot(self,pitch,wplot,oplots,titles,outplot=None):
155                 import Gnuplot
156
157                 time = [ (i+self.params.pitchdelay)*self.params.step for i in range(len(pitch)) ]
158                 pitch = [aubio_freqtomidi(i) for i in pitch]
159                 oplots.append(Gnuplot.Data(time,pitch,with='lines',
160                         title=self.params.pitchmode))
161                 titles.append(self.params.pitchmode)
162
163                         
164         def plotplot(self,wplot,oplots,titles,outplot=None,extension=None,xsize=1.,ysize=1.,multiplot = 1, midi = 1, truth = 1):
165                 from aubio.gnuplot import gnuplot_create , audio_to_array, make_audio_plot
166                 import re
167                 import Gnuplot
168
169                 # check if ground truth exists
170                 if truth:
171                         timet,pitcht = self.gettruth()
172                         if timet and pitcht:
173                                 oplots = [Gnuplot.Data(timet,pitcht,with='lines',
174                                         title='ground truth')] + oplots
175
176                 g = gnuplot_create(outplot=outplot, extension=extension)
177                 g('set title \'%s\'' % (re.sub('.*/','',self.input)))
178                 g('set size %f,%f' % (xsize,ysize) )
179                 g('set multiplot')
180                 # hack to align left axis
181                 g('set lmargin 4')
182                 g('set rmargin 4')
183     # plot waveform
184                 time,data = audio_to_array(self.input)
185                 wplot = [make_audio_plot(time,data)]
186                 g('set origin 0,%f' % (0.7*ysize) )
187                 g('set size %f,%f' % (xsize,0.3*ysize))
188                 #g('set format y "%1f"')
189                 g('set xrange [0:%f]' % max(time)) 
190                 g('set yrange [-1:1]') 
191                 g('set noytics')
192                 g('set y2tics -1,1')
193                 g.xlabel('time (s)',offset=(0,0.7))
194                 g.ylabel('amplitude')
195                 g.plot(*wplot)
196
197                 # default settings for next plots
198                 g('unset title')
199                 g('set format x ""')
200                 g('set format y "%3e"')
201                 g('set tmargin 0')
202                 g.xlabel('')
203                 g('set noclip one') 
204
205                 if not midi:
206                         g('set log y')
207                         #g.xlabel('time (s)')
208                         g.ylabel('f0 (Hz)')
209                         g('set yrange [100:%f]' % self.params.pitchmax) 
210                 else: 
211                         g.ylabel('midi')
212                         g('set yrange [%f:%f]' % (aubio_freqtomidi(self.params.pitchmin), aubio_freqtomidi(self.params.pitchmax)))
213                         g('set y2tics %f,%f' % (round(aubio_freqtomidi(self.params.pitchmin)+.5),12))
214                 
215                 if multiplot:
216                         N = len(oplots)
217                         y = 0.7*ysize # the vertical proportion of the plot taken by onset functions
218                         delta = 0.035 # the constant part of y taken by last plot label and data
219                         for i in range(N):
220                                 # plot pitch detection functions
221                                 g('set size %f,%f' % ( xsize, (y-delta)/N))
222                                 g('set origin 0,%f' % ((N-i-1)*(y-delta)/N + delta ))
223                                 g('set nokey')
224                                 g('set xrange [0:%f]' % max(time))
225                                 g.ylabel(titles[i])
226                                 if i == N-1:
227                                         g('set size %f,%f' % (xsize, (y-delta)/N + delta ) )
228                                         g('set origin 0,0')
229                                         g.xlabel('time (s)', offset=(0,0.7))
230                                         g('set format x')
231                                 g.plot(oplots[i])
232                 else:
233                         g('set key right top')
234                         g.plot(*oplots)
235                 g('unset multiplot')
236