From 7c339a95e8074a793c7a6b7c33fabce9bc6b3989 Mon Sep 17 00:00:00 2001 From: Thomas Fillon Date: Fri, 15 Nov 2013 16:39:58 +0100 Subject: [PATCH] Analyzer: Decorates some analyzers with preprocessors for downmixing and frames blocksize and stepsize adapation + Add some functionnalities for performing a test for a module inside module's main() --- tests/__init__.py | 0 tests/unit_timeside.py | 4 +++- timeside/analyzer/aubio_melenergy.py | 16 ++++++++-------- timeside/analyzer/aubio_mfcc.py | 13 +++++++------ timeside/analyzer/aubio_pitch.py | 11 ++++++----- timeside/analyzer/aubio_specdesc.py | 13 +++++++------ timeside/analyzer/aubio_temporal.py | 16 +++++++++------- timeside/analyzer/odf.py | 16 ++++++++++++++-- timeside/analyzer/preprocessors.py | 22 ++++++++++++++++++++-- timeside/analyzer/spectrogram.py | 10 +++++----- timeside/analyzer/waveform.py | 5 ++++- 11 files changed, 83 insertions(+), 43 deletions(-) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit_timeside.py b/tests/unit_timeside.py index f31ac4c..01a1b76 100644 --- a/tests/unit_timeside.py +++ b/tests/unit_timeside.py @@ -142,4 +142,6 @@ class TestRunner: self.stream.writeln("OK") return result - +def runTestModule(module): + suite = unittest.loader.TestLoader().loadTestsFromModule(module) + TestRunner().run(suite) diff --git a/timeside/analyzer/aubio_melenergy.py b/timeside/analyzer/aubio_melenergy.py index e862934..451a0ae 100644 --- a/timeside/analyzer/aubio_melenergy.py +++ b/timeside/analyzer/aubio_melenergy.py @@ -22,7 +22,7 @@ from timeside.core import implements, interfacedoc from timeside.analyzer.core import Analyzer from timeside.api import IAnalyzer -from utils import downsample_blocking +from preprocessors import downmix_to_mono, frames_adapter import numpy from aubio import filterbank, pvoc @@ -47,7 +47,7 @@ class AubioMelEnergy(Analyzer): self.melenergy = filterbank(self.n_filters, self.input_blocksize) self.melenergy.set_mel_coeffs_slaney(samplerate) self.block_read = 0 - self.melenergy_results = numpy.zeros([self.n_filters, ]) + self.melenergy_results = [] @staticmethod @interfacedoc @@ -64,13 +64,13 @@ class AubioMelEnergy(Analyzer): def unit(): return "" + @downmix_to_mono + @frames_adapter def process(self, frames, eod=False): - for samples in downsample_blocking(frames, self.input_stepsize): - # TODO : check pourquoi on utilise pas le blocksize ? - fftgrain = self.pvoc(samples) - self.melenergy_results = numpy.vstack( - [self.melenergy_results, self.melenergy(fftgrain)]) - self.block_read += 1 + + fftgrain = self.pvoc(frames) + self.melenergy_results.append(self.melenergy(fftgrain)) + self.block_read += 1 return frames, eod def post_process(self): diff --git a/timeside/analyzer/aubio_mfcc.py b/timeside/analyzer/aubio_mfcc.py index 2fa8c23..0b44368 100644 --- a/timeside/analyzer/aubio_mfcc.py +++ b/timeside/analyzer/aubio_mfcc.py @@ -22,7 +22,7 @@ from timeside.core import implements, interfacedoc from timeside.analyzer.core import Analyzer from timeside.api import IAnalyzer -from utils import downsample_blocking +from preprocessors import downmix_to_mono, frames_adapter import numpy from aubio import mfcc, pvoc @@ -66,12 +66,13 @@ class AubioMfcc(Analyzer): def unit(): return "" + @downmix_to_mono + @frames_adapter def process(self, frames, eod=False): - for samples in downsample_blocking(frames, self.input_stepsize): - fftgrain = self.pvoc(samples) - coeffs = self.mfcc(fftgrain) - self.mfcc_results = numpy.vstack((self.mfcc_results, coeffs)) - self.block_read += 1 + fftgrain = self.pvoc(frames) + coeffs = self.mfcc(fftgrain) + self.mfcc_results = numpy.vstack((self.mfcc_results, coeffs)) + self.block_read += 1 return frames, eod def post_process(self): diff --git a/timeside/analyzer/aubio_pitch.py b/timeside/analyzer/aubio_pitch.py index b358bb3..46ad3d3 100644 --- a/timeside/analyzer/aubio_pitch.py +++ b/timeside/analyzer/aubio_pitch.py @@ -22,7 +22,7 @@ from timeside.core import Processor, implements, interfacedoc, FixedSizeInputAdapter from timeside.analyzer.core import Analyzer from timeside.api import IAnalyzer -from utils import downsample_blocking +from preprocessors import downmix_to_mono, frames_adapter from aubio import pitch @@ -65,11 +65,12 @@ class AubioPitch(Analyzer): def __str__(self): return "pitch values" + @downmix_to_mono + @frames_adapter def process(self, frames, eod=False): - for samples in downsample_blocking(frames, self.input_stepsize): - #time = self.block_read * self.input_stepsize * 1. / self.samplerate() - self.pitches += [self.p(samples)[0]] - self.block_read += 1 + #time = self.block_read * self.input_stepsize * 1. / self.samplerate() + self.pitches += [self.p(frames)[0]] + self.block_read += 1 return frames, eod def post_process(self): diff --git a/timeside/analyzer/aubio_specdesc.py b/timeside/analyzer/aubio_specdesc.py index ce4d048..931969d 100644 --- a/timeside/analyzer/aubio_specdesc.py +++ b/timeside/analyzer/aubio_specdesc.py @@ -22,7 +22,7 @@ from timeside.core import Processor, implements, interfacedoc, FixedSizeInputAdapter from timeside.analyzer.core import Analyzer from timeside.api import IAnalyzer -from utils import downsample_blocking +from preprocessors import downmix_to_mono, frames_adapter from aubio import specdesc, pvoc @@ -72,12 +72,13 @@ class AubioSpecdesc(Analyzer): def unit(): return "" + @downmix_to_mono + @frames_adapter def process(self, frames, eod=False): - for samples in downsample_blocking(frames, self.input_stepsize): - fftgrain = self.pvoc(samples) - for method in self.methods: - self.specdesc_results[method] += [ - self.specdesc[method](fftgrain)[0]] + fftgrain = self.pvoc(frames) + for method in self.methods: + self.specdesc_results[method] += [ + self.specdesc[method](fftgrain)[0]] return frames, eod def post_process(self): diff --git a/timeside/analyzer/aubio_temporal.py b/timeside/analyzer/aubio_temporal.py index 71d5f60..27d1c3d 100644 --- a/timeside/analyzer/aubio_temporal.py +++ b/timeside/analyzer/aubio_temporal.py @@ -22,7 +22,7 @@ from timeside.core import implements, interfacedoc from timeside.analyzer.core import Analyzer from timeside.api import IAnalyzer -from utils import downsample_blocking +from preprocessors import downmix_to_mono, frames_adapter from aubio import onset, tempo import numpy @@ -70,13 +70,15 @@ class AubioTemporal(Analyzer): def __str__(self): return "%s %s" % (str(self.value), self.unit()) + + @downmix_to_mono + @frames_adapter def process(self, frames, eod=False): - for samples in downsample_blocking(frames, self.input_stepsize): - if self.o(samples): - self.onsets += [self.o.get_last_s()] - if self.t(samples): - self.beats += [self.t.get_last_s()] - self.block_read += 1 + if self.o(frames): + self.onsets += [self.o.get_last_s()] + if self.t(frames): + self.beats += [self.t.get_last_s()] + self.block_read += 1 return frames, eod def post_process(self): diff --git a/timeside/analyzer/odf.py b/timeside/analyzer/odf.py index fab3e70..42eb0bc 100644 --- a/timeside/analyzer/odf.py +++ b/timeside/analyzer/odf.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with TimeSide. If not, see . -# Author: Paul Brossier +# Author: Thomas Fillon from timeside.core import implements, interfacedoc from timeside.analyzer.core import Analyzer @@ -74,11 +74,23 @@ class OnsetDetectionFunction(Analyzer): #spectrogram = self.pipe._results[self.parents()[0].id] # Low-pass filtering of the spectrogram amplitude along the time axis - S = signal.lfilter(signal.hann(15), 1, abs(spectrogram), axis=0) + S = signal.lfilter(signal.hann(15)[8:], 1, abs(spectrogram), axis=0) + + + import matplotlib.pyplot as plt +# plt.figure() +# plt.imshow(np.log10(abs(spectrogram)), origin='lower', aspect='auto', interpolation='nearest') + + # Clip small value to a minimal threshold np.maximum(S, 1e-9, out=S) S = np.log10(S) + +# plt.figure() +# plt.imshow(S,origin='lower', aspect='auto', interpolation='nearest') +# plt.show() + # S[S<1e-3]=0 np.maximum(S, 1e-3, out=S) diff --git a/timeside/analyzer/preprocessors.py b/timeside/analyzer/preprocessors.py index c96a236..d1dd8b3 100644 --- a/timeside/analyzer/preprocessors.py +++ b/timeside/analyzer/preprocessors.py @@ -18,12 +18,22 @@ # along with TimeSide. If not, see . # # Author : Thomas fillon +''' + Collections of preprocessors to use as decorators for the analyzers process + Preprocessors process the (frame, eod) arguments in order to handle various + preprocessing such as : + - Downmixing to mono + - Adapt the frames to match the input_blocksize and input_stepsize + of the analyzer +''' def downmix_to_mono(process_func): ''' Pre-processing decorator that downmixes frames from multi-channel to mono - Downmix by averaging all channels + + Downmix is achieved by averaging all channels + >>> @downmix_to_mono ... def process(analyzer,frames,eod): ... print 'Frames, eod inside process :' @@ -66,7 +76,9 @@ def downmix_to_mono(process_func): def frames_adapter(process_func): ''' - Pre-processing decorator that adapt frames to match blocksize and stepsize + Pre-processing decorator that adapt frames to match input_blocksize and + input_stepsize of the decorated analyzer + >>> @frames_adapter ... def process(analyzer,frames,eod): ... analyzer.frames.append(frames) @@ -164,5 +176,11 @@ def frames_adapter(process_func): if __name__ == "__main__": + # Run doctest import doctest doctest.testmod() + + # Run unittest from test_analyzer_preprocessors + from tests import test_analyzer_preprocessors + from tests.unit_timeside import runTestModule + runTestModule(test_analyzer_preprocessors) diff --git a/timeside/analyzer/spectrogram.py b/timeside/analyzer/spectrogram.py index adc2e55..9728c6a 100644 --- a/timeside/analyzer/spectrogram.py +++ b/timeside/analyzer/spectrogram.py @@ -22,7 +22,7 @@ from timeside.core import implements, interfacedoc from timeside.analyzer.core import Analyzer from timeside.api import IAnalyzer -from utils import downsample_blocking +from preprocessors import downmix_to_mono, frames_adapter import numpy as np @@ -62,11 +62,11 @@ class Spectrogram(Analyzer): def unit(): return "" + @downmix_to_mono + @frames_adapter def process(self, frames, eod=False): - for samples in downsample_blocking(frames, self.input_stepsize): - #time = self.block_read * self.input_stepsize * 1. / self.samplerate() - self.values.append(np.abs(np.fft.rfft(samples, self.FFT_SIZE))) - return frames, eod + self.values.append(np.abs(np.fft.rfft(frames, self.FFT_SIZE))) + return frames, eod def post_process(self): spectrogram = self.new_result(data_mode='value', time_mode='framewise') diff --git a/timeside/analyzer/waveform.py b/timeside/analyzer/waveform.py index eb8b09f..db55043 100644 --- a/timeside/analyzer/waveform.py +++ b/timeside/analyzer/waveform.py @@ -22,14 +22,15 @@ from timeside.core import implements, interfacedoc from timeside.analyzer.core import Analyzer from timeside.api import IAnalyzer -from utils import downsample_blocking import numpy as np +from preprocessors import downmix_to_mono, frames_adapter class Waveform(Analyzer): implements(IAnalyzer) # TODO check if needed with inheritance def __init__(self): + super(Waveform, self).__init__() self.input_blocksize = 2048 self.input_stepsize = self.input_blocksize / 2 @@ -57,6 +58,8 @@ class Waveform(Analyzer): def unit(): return "" + @downmix_to_mono + @frames_adapter def process(self, frames, eod=False): for samples in downsample_blocking(frames, self.input_blocksize): self.values.append(samples) -- 2.39.5