source: python/aubio/tasks.py @ d45d118

feature/autosinkfeature/cnnfeature/cnn_orgfeature/constantqfeature/crepefeature/crepe_orgfeature/pitchshiftfeature/pydocstringsfeature/timestretchfix/ffmpeg5pitchshiftsamplertimestretchyinfft+
Last change on this file since d45d118 was 1944aaf, checked in by Paul Brossier <piem@altern.org>, 19 years ago

taskpitch: limit frequency range and delay, update gettruth
taskpitch: limit frequency range and delay, update gettruth

  • Property mode set to 100644
File size: 18.0 KB
Line 
1from aubioclass import * 
2
3def get_onset_mode(nvalue):
4        """ utility function to convert a string to aubio_onsetdetection_type """
5        if   nvalue == 'complexdomain' or nvalue == 'complex' :
6                 return aubio_onset_complex
7        elif nvalue == 'hfc'           :
8                 return aubio_onset_hfc
9        elif nvalue == 'phase'         :
10                 return aubio_onset_phase
11        elif nvalue == 'specdiff'      :
12                 return aubio_onset_specdiff
13        elif nvalue == 'energy'        :
14                 return aubio_onset_energy
15        elif nvalue == 'kl'            :
16                 return aubio_onset_kl
17        elif nvalue == 'mkl'           :
18                 return aubio_onset_mkl
19        elif nvalue == 'dual'          :
20                 return 'dual'
21        else:
22                 import sys
23                 print "unknown onset detection function selected"
24                 sys.exit(1)
25
26def get_pitch_mode(nvalue):
27        """ utility function to convert a string to aubio_pitchdetection_type """
28        if   nvalue == 'mcomb'  :
29                 return aubio_pitch_mcomb
30        elif nvalue == 'yin'    :
31                 return aubio_pitch_yin
32        elif nvalue == 'fcomb'  :
33                 return aubio_pitch_fcomb
34        elif nvalue == 'schmitt':
35                 return aubio_pitch_schmitt
36        else:
37                 import sys
38                 print "error: unknown pitch detection function selected"
39                 sys.exit(1)
40
41def check_onset_mode(option, opt, value, parser):
42        """ wrapper function to convert a list of modes to
43                aubio_onsetdetection_type """
44        nvalues = parser.rargs[0].split(',')
45        val =  []
46        for nvalue in nvalues:
47                val.append(get_onset_mode(nvalue))
48                setattr(parser.values, option.dest, val)
49
50def check_pitch_mode(option, opt, value, parser):
51        """ utility function to convert a string to aubio_pitchdetection_type"""
52        nvalues = parser.rargs[0].split(',')
53        val = []
54        for nvalue in nvalues:
55                val.append(get_pitch_mode(nvalue))
56                setattr(parser.values, option.dest, val)
57
58def check_pitchm_mode(option, opt, value, parser):
59        """ utility function to convert a string to aubio_pitchdetection_mode """
60        nvalue = parser.rargs[0]
61        if   nvalue == 'freq'  :
62                 setattr(parser.values, option.dest, aubio_pitchm_freq)
63        elif nvalue == 'midi'  :
64                 setattr(parser.values, option.dest, aubio_pitchm_midi)
65        elif nvalue == 'cent'  :
66                 setattr(parser.values, option.dest, aubio_pitchm_cent)
67        elif nvalue == 'bin'   :
68                 setattr(parser.values, option.dest, aubio_pitchm_bin)
69        else:
70                 import sys
71                 print "error: unknown pitch detection output selected"
72                 sys.exit(1)
73
74class taskparams(object):
75        """ default parameters for task classes """
76        def __init__(self,input=None,output=None):
77                self.silence = -70
78                self.derivate = False
79                self.localmin = False
80                self.delay = 4.
81                self.storefunc = False
82                self.bufsize = 512
83                self.hopsize = 256
84                self.samplerate = 44100
85                self.tol = 0.05
86                self.mintol = 0.0
87                self.step = float(self.hopsize)/float(self.samplerate)
88                self.threshold = 0.1
89                self.onsetmode = 'dual'
90                self.pitchmode = 'yin'
91                self.pitchsmooth = 7
92                self.pitchmin=100.
93                self.pitchmax=1000.
94                self.dcthreshold = -1.
95                self.omode = aubio_pitchm_freq
96                self.verbose   = False
97
98class task(taskparams):
99        """ default template class to apply tasks on a stream """
100        def __init__(self,input,output=None,params=None):
101                """ open the input file and initialize default argument
102                parameters should be set *before* calling this method.
103                """
104                import time
105                self.tic = time.time()
106                if params == None: self.params = taskparams()
107                else: self.params = params
108                self.frameread = 0
109                self.readsize  = self.params.hopsize
110                self.input     = input
111                self.filei     = sndfile(self.input)
112                self.srate     = self.filei.samplerate()
113                self.channels  = self.filei.channels()
114                self.params.step = float(self.params.hopsize)/float(self.srate)
115                self.myvec     = fvec(self.params.hopsize,self.channels)
116                self.output    = output
117
118        def __call__(self):
119                self.readsize = self.filei.read(self.params.hopsize,self.myvec)
120                self.frameread += 1
121               
122        def compute_all(self):
123                """ Compute data """
124                mylist    = []
125                while(self.readsize==self.params.hopsize):
126                        tmp = self()
127                        if tmp: 
128                                mylist.append(tmp)
129                                if self.params.verbose:
130                                        self.fprint(tmp)
131                return mylist
132       
133        def fprint(self,foo):
134                print foo
135
136        def eval(self,results):
137                """ Eval data """
138                pass
139
140        def plot(self):
141                """ Plot data """
142                pass
143
144        def time(self):
145                import time
146                print "CPU time is now %f seconds," % time.clock(),
147                print "task execution took %f seconds" % (time.time() - self.tic)
148
149class tasksilence(task):
150        wassilence = 1
151        issilence  = 1
152        def __call__(self):
153                task.__call__(self)
154                if (aubio_silence_detection(self.myvec(),self.params.silence)==1):
155                        if self.wassilence == 1: self.issilence = 1
156                        else: self.issilence = 2
157                        self.wassilence = 1
158                else: 
159                        if self.wassilence <= 0: self.issilence = 0
160                        else: self.issilence = -1 
161                        self.wassilence = 0
162                if self.issilence == -1:
163                        return max(self.frameread-self.params.delay,0.), -1
164                elif self.issilence == 2:
165                        return max(self.frameread+self.params.delay,0.), 2 
166
167        def fprint(self,foo):
168                print self.params.step*foo[0],
169                if foo[1] == 2: print "OFF"
170                else: print "ON"
171
172class taskpitch(task):
173        def __init__(self,input,params=None):
174                task.__init__(self,input,params=params)
175                self.shortlist = [0. for i in range(self.params.pitchsmooth)]
176                self.pitchdet  = pitchdetection(mode=get_pitch_mode(self.params.pitchmode),
177                        bufsize=self.params.bufsize,
178                        hopsize=self.params.hopsize,
179                        channels=self.channels,
180                        samplerate=self.srate,
181                        omode=self.params.omode)
182
183        def __call__(self):
184                from median import short_find
185                task.__call__(self)
186                if (aubio_silence_detection(self.myvec(),self.params.silence)==1):
187                        freq = -1.
188                else:
189                        freq = self.pitchdet(self.myvec)
190                minpitch = self.params.pitchmin
191                maxpitch = self.params.pitchmax
192                if maxpitch and freq > maxpitch : 
193                        freq = -1.
194                elif minpitch and freq < minpitch :
195                        freq = -1.
196                if self.params.pitchsmooth:
197                        self.shortlist.append(freq)
198                        self.shortlist.pop(0)
199                        smoothfreq = short_find(self.shortlist,
200                                len(self.shortlist)/2)
201                        return smoothfreq
202                else:
203                        return freq
204
205        def compute_all(self):
206                """ Compute data """
207                mylist    = []
208                while(self.readsize==self.params.hopsize):
209                        freq = self()
210                        mylist.append(freq)
211                        if self.params.verbose:
212                                self.fprint("%s\t%s" % (self.frameread*self.params.step,freq))
213                return mylist
214
215        def gettruth(self):
216                """ extract ground truth array in frequency """
217                import os.path
218                """ from wavfile.txt """
219                datafile = self.input.replace('.wav','.txt')
220                if datafile == self.input: datafile = ""
221                """ from file.<midinote>.wav """
222                # FIXME very weak check
223                floatpit = self.input.split('.')[-2]
224
225                if not os.path.isfile(datafile) and not len(self.input.split('.')) < 3:
226                        print "no ground truth "
227                        return False,False
228                elif floatpit:
229                        try:
230                                self.truth = aubio_miditofreq(float(floatpit))
231                                print "ground truth found in filename:", self.truth
232                                tasksil = tasksilence(self.input)
233                                time,pitch =[],[]
234                                while(tasksil.readsize==tasksil.params.hopsize):
235                                        tasksil()
236                                        time.append(tasksil.params.step*tasksil.frameread)
237                                        if not tasksil.issilence:
238                                                pitch.append(self.truth)
239                                        else:
240                                                pitch.append(-1.)
241                                return time,pitch #0,aubio_miditofreq(float(floatpit))
242                        except ValueError:
243                                # FIXME very weak check
244                                if not os.path.isfile(datafile):
245                                        print "no ground truth found"
246                                        return 0,0
247                                else:
248                                        from aubio.txtfile import read_datafile
249                                        values = read_datafile(datafile)
250                                        time, pitch = [], []
251                                        for i in range(len(values)):
252                                                time.append(values[i][0])
253                                                pitch.append(values[i][1])
254                                        return time,pitch
255
256        def eval(self,results):
257                def mmean(l):
258                        return sum(l)/max(float(len(l)),1)
259
260                from median import percental
261                timet,pitcht = self.gettruth()
262                res = []
263                for i in results:
264                        #print i,self.truth
265                        if i <= 0: pass
266                        else: res.append(self.truth-i)
267                if not res or len(res) < 3: 
268                        avg = self.truth; med = self.truth
269                else:
270                        avg = mmean(res) 
271                        med = percental(res,len(res)/2) 
272                return self.truth, self.truth-med, self.truth-avg
273
274        def plot(self,pitch,wplot,oplots,outplot=None):
275                import numarray
276                import Gnuplot
277
278                downtime = self.params.step*numarray.arange(len(pitch))
279                oplots.append(Gnuplot.Data(downtime,pitch,with='lines',
280                        title=self.params.pitchmode))
281
282                       
283        def plotplot(self,wplot,oplots,outplot=None,multiplot = 1):
284                from aubio.gnuplot import gnuplot_init, audio_to_array, make_audio_plot
285                import re
286                import Gnuplot
287                # audio data
288                time,data = audio_to_array(self.input)
289                f = make_audio_plot(time,data)
290
291                # check if ground truth exists
292                timet,pitcht = self.gettruth()
293                if timet and pitcht:
294                        oplots = [Gnuplot.Data(timet,pitcht,with='lines',
295                                title='ground truth')] + oplots
296
297                t = Gnuplot.Data(0,0,with='impulses') 
298
299                g = gnuplot_init(outplot)
300                g('set title \'%s\'' % (re.sub('.*/','',self.input)))
301                g('set multiplot')
302                # hack to align left axis
303                g('set lmargin 15')
304                # plot waveform and onsets
305                g('set size 1,0.3')
306                g('set origin 0,0.7')
307                g('set xrange [0:%f]' % max(time)) 
308                g('set yrange [-1:1]') 
309                g.ylabel('amplitude')
310                g.plot(f)
311                g('unset title')
312                # plot onset detection function
313
314
315                g('set size 1,0.7')
316                g('set origin 0,0')
317                g('set xrange [0:%f]' % max(time))
318                g('set yrange [100:%f]' % self.params.pitchmax) 
319                g('set key right top')
320                g('set noclip one') 
321                g('set format x ""')
322                g('set log y')
323                #g.xlabel('time (s)')
324                g.ylabel('f0 (Hz)')
325                if multiplot:
326                        for i in range(len(oplots)):
327                                # plot onset detection functions
328                                g('set size 1,%f' % (0.7/(len(oplots))))
329                                g('set origin 0,%f' % (float(i)*0.7/(len(oplots))))
330                                g('set xrange [0:%f]' % max(time))
331                                g.plot(oplots[i])
332                else:
333                        g.plot(*oplots)
334                g('unset multiplot')
335
336
337class taskonset(task):
338        def __init__(self,input,output=None,params=None):
339                """ open the input file and initialize arguments
340                parameters should be set *before* calling this method.
341                """
342                task.__init__(self,input,params=params)
343                self.opick = onsetpick(self.params.bufsize,
344                        self.params.hopsize,
345                        self.channels,
346                        self.myvec,
347                        self.params.threshold,
348                        mode=get_onset_mode(self.params.onsetmode),
349                        dcthreshold=self.params.dcthreshold,
350                        derivate=self.params.derivate)
351                self.olist = [] 
352                self.ofunc = []
353                self.maxofunc = 0
354                self.last = 0
355                if self.params.localmin:
356                        self.ovalist   = [0., 0., 0., 0., 0.]
357
358        def __call__(self):
359                task.__call__(self)
360                isonset,val = self.opick.do(self.myvec)
361                if (aubio_silence_detection(self.myvec(),self.params.silence)):
362                        isonset=0
363                if self.params.storefunc:
364                        self.ofunc.append(val)
365                if self.params.localmin:
366                        if val > 0: self.ovalist.append(val)
367                        else: self.ovalist.append(0)
368                        self.ovalist.pop(0)
369                if (isonset == 1):
370                        if self.params.localmin:
371                                # find local minima before peak
372                                i=len(self.ovalist)-1
373                                while self.ovalist[i-1] < self.ovalist[i] and i > 0:
374                                        i -= 1
375                                now = (self.frameread+1-i)
376                        else:
377                                now = self.frameread
378                        # take back delay
379                        if self.params.delay != 0.: now -= self.params.delay
380                        if now < 0 :
381                                now = 0
382                        if self.params.mintol:
383                                # prune doubled
384                                if (now - self.last) > self.params.mintol:
385                                        self.last = now
386                                        return now, val
387                        else:
388                                return now, val
389
390
391        def fprint(self,foo):
392                print self.params.step*foo[0]
393
394        def eval(self,inputdata,ftru,mode='roc',vmode=''):
395                from txtfile import read_datafile
396                from onsetcompare import onset_roc, onset_diffs, onset_rocloc
397                ltru = read_datafile(ftru,depth=0)
398                lres = []
399                for i in range(len(inputdata)): lres.append(inputdata[i][0]*self.params.step)
400                if vmode=='verbose':
401                        print "Running with mode %s" % self.params.onsetmode, 
402                        print " and threshold %f" % self.params.threshold, 
403                        print " on file", self.input
404                #print ltru; print lres
405                if mode == 'local':
406                        l = onset_diffs(ltru,lres,self.params.tol)
407                        mean = 0
408                        for i in l: mean += i
409                        if len(l): mean = "%.3f" % (mean/len(l))
410                        else: mean = "?0"
411                        return l, mean
412                elif mode == 'roc':
413                        self.orig, self.missed, self.merged, \
414                                self.expc, self.bad, self.doubled = \
415                                onset_roc(ltru,lres,self.params.tol)
416                elif mode == 'rocloc':
417                        self.v = {}
418                        self.v['orig'], self.v['missed'], self.v['Tm'], \
419                                self.v['expc'], self.v['bad'], self.v['Td'], \
420                                self.v['l'], self.v['labs'] = \
421                                onset_rocloc(ltru,lres,self.params.tol)
422
423        def plot(self,onsets,ofunc,wplot,oplots,nplot=False):
424                import Gnuplot, Gnuplot.funcutils
425                import aubio.txtfile
426                import os.path
427                import numarray
428                from aubio.onsetcompare import onset_roc
429
430                x1,y1,y1p = [],[],[]
431                oplot = []
432                if self.params.onsetmode in ('mkl','kl'): ofunc[0:10] = [0] * 10
433
434                self.lenofunc = len(ofunc) 
435                self.maxofunc = max(ofunc)
436                # onset detection function
437                downtime = numarray.arange(len(ofunc))*self.params.step
438                oplot.append(Gnuplot.Data(downtime,ofunc,with='lines',title=self.params.onsetmode))
439
440                # detected onsets
441                if not nplot:
442                        for i in onsets:
443                                x1.append(i[0]*self.params.step)
444                                y1.append(self.maxofunc)
445                                y1p.append(-self.maxofunc)
446                        #x1 = numarray.array(onsets)*self.params.step
447                        #y1 = self.maxofunc*numarray.ones(len(onsets))
448                        if x1:
449                                oplot.append(Gnuplot.Data(x1,y1,with='impulses'))
450                                wplot.append(Gnuplot.Data(x1,y1p,with='impulses'))
451
452                oplots.append(oplot)
453
454                # check if ground truth datafile exists
455                datafile = self.input.replace('.wav','.txt')
456                if datafile == self.input: datafile = ""
457                if not os.path.isfile(datafile):
458                        self.title = "" #"(no ground truth)"
459                else:
460                        t_onsets = aubio.txtfile.read_datafile(datafile)
461                        x2 = numarray.array(t_onsets).resize(len(t_onsets))
462                        y2 = self.maxofunc*numarray.ones(len(t_onsets))
463                        wplot.append(Gnuplot.Data(x2,y2,with='impulses'))
464                       
465                        tol = 0.050 
466
467                        orig, missed, merged, expc, bad, doubled = \
468                                onset_roc(x2,x1,tol)
469                        self.title = "GD %2.3f%% FP %2.3f%%" % \
470                                ((100*float(orig-missed-merged)/(orig)),
471                                 (100*float(bad+doubled)/(orig)))
472
473
474        def plotplot(self,wplot,oplots,outplot=None):
475                from aubio.gnuplot import gnuplot_init, audio_to_array, make_audio_plot
476                import re
477                # audio data
478                time,data = audio_to_array(self.input)
479                wplot = [make_audio_plot(time,data)] + wplot
480                self.title = self.input
481                # prepare the plot
482                g = gnuplot_init(outplot)
483
484                g('set multiplot')
485
486                # hack to align left axis
487                g('set lmargin 6')
488                g('set tmargin 0')
489                g('set format x ""')
490                g('set format y ""')
491                g('set noytics')
492
493                for i in range(len(oplots)):
494                        # plot onset detection functions
495                        g('set size 1,%f' % (0.7/(len(oplots))))
496                        g('set origin 0,%f' % (float(i)*0.7/(len(oplots))))
497                        g('set xrange [0:%f]' % (self.lenofunc*self.params.step))
498                        g.plot(*oplots[i])
499
500                g('set tmargin 3.0')
501                g('set xlabel "time (s)" 1,0')
502                g('set format x "%1.1f"')
503
504                g('set title \'%s %s\'' % (re.sub('.*/','',self.input),self.title))
505
506                # plot waveform and onsets
507                g('set size 1,0.3')
508                g('set origin 0,0.7')
509                g('set xrange [0:%f]' % max(time)) 
510                g('set yrange [-1:1]') 
511                g.ylabel('amplitude')
512                g.plot(*wplot)
513               
514                g('unset multiplot')
515
516class taskcut(task):
517        def __init__(self,input,slicetimes,params=None,output=None):
518                """ open the input file and initialize arguments
519                parameters should be set *before* calling this method.
520                """
521                task.__init__(self,input,output=None,params=params)
522                self.newname   = "%s%s%09.5f%s%s" % (self.input.split(".")[0].split("/")[-1],".",
523                                        self.frameread*self.params.step,".",self.input.split(".")[-1])
524                self.fileo      = sndfile(self.newname,model=self.filei)
525                self.myvec      = fvec(self.params.hopsize,self.channels)
526                self.mycopy     = fvec(self.params.hopsize,self.channels)
527                self.slicetimes = slicetimes
528
529        def __call__(self):
530                task.__call__(self)
531                # write to current file
532                if len(self.slicetimes) and self.frameread >= self.slicetimes[0][0]:
533                        self.slicetimes.pop(0)
534                        # write up to 1st zero crossing
535                        zerocross = 0
536                        while ( abs( self.myvec.get(zerocross,0) ) > self.params.zerothres ):
537                                zerocross += 1
538                        writesize = self.fileo.write(zerocross,self.myvec)
539                        fromcross = 0
540                        while (zerocross < self.readsize):
541                                for i in range(self.channels):
542                                        self.mycopy.set(self.myvec.get(zerocross,i),fromcross,i)
543                                        fromcross += 1
544                                        zerocross += 1
545                        del self.fileo
546                        self.fileo = sndfile("%s%s%09.5f%s%s" % 
547                                (self.input.split(".")[0].split("/")[-1],".",
548                                self.frameread*self.params.step,".",self.input.split(".")[-1]),model=self.filei)
549                        writesize = self.fileo.write(fromcross,self.mycopy)
550                else:
551                        writesize = self.fileo.write(self.readsize,self.myvec)
552
553class taskbeat(taskonset):
554        def __init__(self,input,params=None,output=None):
555                """ open the input file and initialize arguments
556                parameters should be set *before* calling this method.
557                """
558                taskonset.__init__(self,input,output=None,params=params)
559                self.btwinlen  = 512**2/self.params.hopsize
560                self.btstep    = self.btwinlen/4
561                self.btoutput  = fvec(self.btstep,self.channels)
562                self.dfframe   = fvec(self.btwinlen,self.channels)
563                self.bt        = beattracking(self.btwinlen,self.channels)
564                self.pos2      = 0
565
566        def __call__(self):
567                taskonset.__call__(self)
568                # write to current file
569                if self.pos2 == self.btstep - 1 : 
570                        self.bt.do(self.dfframe,self.btoutput)
571                        for i in range (self.btwinlen - self.btstep):
572                                self.dfframe.set(self.dfframe.get(i+self.btstep,0),i,0) 
573                        for i in range(self.btwinlen - self.btstep, self.btwinlen): 
574                                self.dfframe.set(0,i,0)
575                        self.pos2 = -1;
576                self.pos2 += 1
577                val = self.opick.pp.getval()
578                self.dfframe.set(val,self.btwinlen - self.btstep + self.pos2,0)
579                i=0
580                for i in range(1,int( self.btoutput.get(0,0) ) ):
581                        if self.pos2 == self.btoutput.get(i,0) and \
582                                aubio_silence_detection(self.myvec(),
583                                        self.params.silence)!=1: 
584                                return self.frameread, 0 
585       
586        def eval(self,results):
587                pass
Note: See TracBrowser for help on using the repository browser.