From 764121e7554620c0cf594fb92ed771fe8f07591f Mon Sep 17 00:00:00 2001 From: Thomas Fillon Date: Sat, 15 Jun 2013 14:09:47 +0200 Subject: [PATCH] Add Yaafe wrapper as a new TimeSide analyzer Yaafe wrapper needs a proper Yaafe install (don't forget to set all the environment variables required by Yaafe). Yaafe analyzer uses the Yaafe python engine to which TimeSide sends the audio data during process() and gets back the results when result() is called. The corresponding tests are also included in this commit. Enjoy ! --- tests/test_yaafe.py | 66 ++++++++++++++++++ tests/yaafe_config/yaafeDataFlow | 38 +++++++++++ tests/yaafe_config/yaafeFeaturePlan | 3 + timeside/analyzer/__init__.py | 1 + timeside/analyzer/yaafe.py | 102 ++++++++++++++++++++++++++++ 5 files changed, 210 insertions(+) create mode 100644 tests/test_yaafe.py create mode 100644 tests/yaafe_config/yaafeDataFlow create mode 100644 tests/yaafe_config/yaafeFeaturePlan create mode 100644 timeside/analyzer/yaafe.py diff --git a/tests/test_yaafe.py b/tests/test_yaafe.py new file mode 100644 index 0000000..0f6ccf3 --- /dev/null +++ b/tests/test_yaafe.py @@ -0,0 +1,66 @@ +#! /usr/bin/env python + +from unit_timeside import * +from timeside.decoder import * +from timeside.analyzer import Yaafe +from yaafelib import DataFlow,FeaturePlan + +class TestYaafe(TestCase): + + def setUp(self): + self.sample_rate = 16000 + + def testOnSweepWithFeaturePlan(self): + "runs on sweep and define feature plan manualy" + self.source = os.path.join (os.path.dirname(__file__), "samples", "sweep.wav") + + # Setup Yaafe Analyzer + # Define Yaafe Feature Plan + fp = FeaturePlan(sample_rate=self.sample_rate) + # add feature definitions manually + fp.addFeature('mfcc: MFCC blockSize=512 stepSize=256') + fp.addFeature('mfcc_d1: MFCC blockSize=512 stepSize=256 > Derivate DOrder=1') + fp.addFeature('mfcc_d2: MFCC blockSize=512 stepSize=256 > Derivate DOrder=2') + + # Setup a new Yaafe TimeSide analyzer + # from FeaturePlan + self.analyzer = Yaafe(fp) + + def testOnGuitarWithFeaturePlanFromFile(self): + "runs on guitar and load Yaafe feature plan from file" + self.source = os.path.join (os.path.dirname(__file__), "samples", "guitar.wav") + # Setup Yaafe Analyzer + # Load Yaafe Feature Plan + fp = FeaturePlan(sample_rate=self.sample_rate) + fp_file = os.path.join (os.path.dirname(__file__), "yaafe_config", "yaafeFeaturePlan") + + fp.loadFeaturePlan(fp_file) + # Setup a new Yaafe TimeSide analyzer + # from FeaturePlan + self.analyzer = Yaafe(fp) + + def testOnGuitarWithDataFlow(self): + "runs on guitar and load Yaafe dataflow from file" + self.source = os.path.join (os.path.dirname(__file__), "samples", "guitar.wav") + # Setup Yaafe Analyzer + # Load DataFlow from file + df = DataFlow() + df_file = os.path.join (os.path.dirname(__file__), "yaafe_config", "yaafeDataFlow") + df.load(df_file) + + # Setup a new Yaafe TimeSide analyzer + # from DataFlow + self.analyzer = Yaafe(df) + + def tearDown(self): + decoder = FileDecoder(self.source) + decoder.output_samplerate = self.sample_rate + (decoder | self.analyzer).run() + results = self.analyzer.results() + #print results + #print results.to_yaml() + #print results.to_json() + #print results.to_xml() + +if __name__ == '__main__': + unittest.main(testRunner=TestRunner()) diff --git a/tests/yaafe_config/yaafeDataFlow b/tests/yaafe_config/yaafeDataFlow new file mode 100644 index 0000000..a7191a6 --- /dev/null +++ b/tests/yaafe_config/yaafeDataFlow @@ -0,0 +1,38 @@ +useComponentLibrary yaafe-components +audio := Input Resample="yes" SampleRate="16000" +n1 := FrameTokenizer blockSize="512" stepSize="256" +n2 := FFT FFTLength="512" FFTWindow="Hanning" +n3 := Abs +n4 := MelFilterBank MelMaxFreq="6854.0" MelMinFreq="130.0" MelNbFilters="40" +n5 := Cepstrum CepsIgnoreFirstCoeff="1" CepsNbCoeffs="13" +mfcc := Output normalize="-1" resample="yes" samplerate="16000" version="0.64" yaafedefinition="MFCC blockSize=512 stepSize=256" +n7 := Derivate DO1Len="4" DO2Len="1" DOrder="1" +mfcc_d1 := Output normalize="-1" resample="yes" samplerate="16000" version="0.64" yaafedefinition="MFCC blockSize=512 stepSize=256 > Derivate DOrder=1" +n9 := FrameTokenizer blockSize="1024" stepSize="1024" +n10 := FFT FFTLength="1024" FFTWindow="Hanning" +n11 := Abs +n12 := MelFilterBank MelMaxFreq="6854.0" MelMinFreq="130.0" MelNbFilters="40" +n13 := Cepstrum CepsIgnoreFirstCoeff="1" CepsNbCoeffs="11" +n14 := Output normalize="-1" resample="yes" samplerate="16000" version="0.64" yaafedefinition="MFCC blockSize=1024 stepSize=1024 CepsNbCoeffs=11" +n15 := Derivate DO1Len="4" DO2Len="1" DOrder="1" +n16 := Output normalize="-1" resample="yes" samplerate="16000" version="0.64" yaafedefinition="MFCC blockSize=1024 stepSize=1024 CepsNbCoeffs=11 > Derivate DOrder=1" +n17 := Derivate DO1Len="4" DO2Len="1" DOrder="2" +mfcc_d2 := Output normalize="-1" resample="yes" samplerate="16000" version="0.64" yaafedefinition="MFCC blockSize=1024 stepSize=1024 CepsNbCoeffs=11 > Derivate DOrder=2" +audio > n1 +n1 > n2 +n2 > n3 +n3 > n4 +n4 > n5 +n5 > mfcc +n5 > n7 +n7 > mfcc_d1 +audio > n9 +n9 > n10 +n10 > n11 +n11 > n12 +n12 > n13 +n13 > n14 +n13 > n15 +n15 > n16 +n13 > n17 +n17 > mfcc_d2 diff --git a/tests/yaafe_config/yaafeFeaturePlan b/tests/yaafe_config/yaafeFeaturePlan new file mode 100644 index 0000000..b92c647 --- /dev/null +++ b/tests/yaafe_config/yaafeFeaturePlan @@ -0,0 +1,3 @@ +mfcc: MFCC blockSize=1024 stepSize=1024 CepsNbCoeffs=11 +mfcc_d1: MFCC blockSize=1024 stepSize=1024 CepsNbCoeffs=11 > Derivate DOrder=1 +mfcc_d2: MFCC blockSize=1024 stepSize=1024 CepsNbCoeffs=11 > Derivate DOrder=2 \ No newline at end of file diff --git a/timeside/analyzer/__init__.py b/timeside/analyzer/__init__.py index 4c1c680..0747b3a 100644 --- a/timeside/analyzer/__init__.py +++ b/timeside/analyzer/__init__.py @@ -7,3 +7,4 @@ from aubio_pitch import * from aubio_mfcc import * from aubio_melenergy import * from aubio_specdesc import * +from yaafe import * # TF : add Yaafe analyzer \ No newline at end of file diff --git a/timeside/analyzer/yaafe.py b/timeside/analyzer/yaafe.py new file mode 100644 index 0000000..50c85bd --- /dev/null +++ b/timeside/analyzer/yaafe.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Thomas Fillon + +# This file is part of TimeSide. + +# TimeSide is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. + +# TimeSide is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with TimeSide. If not, see . + +# Author : Thomas Fillon +""" +Module Yaafe Analyzer +Created on Thu Jun 13 16:05:02 2013 + +@author: Thomas Fillon +""" +from timeside.core import Processor, implements, interfacedoc, FixedSizeInputAdapter +from timeside.analyzer.core import * +from timeside.api import IValueAnalyzer +# +from yaafelib import * +# +import numpy + +class Yaafe(Processor): + implements(IValueAnalyzer) + def __init__(self, yaafeSpecification): + # Check arguments + if isinstance(yaafeSpecification,DataFlow): + self.dataFlow = yaafeSpecification + elif isinstance(yaafeSpecification,FeaturePlan): + self.featurePlan = yaafeSpecification + self.dataFlow = self.featurePlan.getDataFlow() + else: + raise TypeError("'%s' Type must be either '%s' or '%s'" % (str(yaafeSpecification),str(DataFlow),str(FeaturePlan))) + + @interfacedoc + def setup(self, channels=None, samplerate=None, blocksize=None, totalframes=None): + super(Yaafe, self).setup(channels, samplerate, blocksize, totalframes) + # Configure a YAAFE engine + self.yaafe_engine = Engine() + self.yaafe_engine.load(self.dataFlow) + self.yaafe_engine.reset() + + @staticmethod + @interfacedoc + def id(): + return "yaafe" + + @staticmethod + @interfacedoc + def name(): + return "Yaafe Descriptor" + + def process(self, frames, eod=False): + # do process things... + # Downmixing to mono and convert to float64 for compatibility with Yaafe + yaafe_frames = frames.sum(axis=-1,dtype=numpy.float64) / frames.shape[-1] + # Reshape for compatibility with Yaafe input format + yaafe_frames.shape = (1,yaafe_frames.shape[0]) + # write audio array on 'audio' input + self.yaafe_engine.writeInput('audio',yaafe_frames) + # process available data + self.yaafe_engine.process() + if eod: + # flush yaafe engine to process remaining data + self.yaafe_engine.flush() + + return frames, eod + + def results(self): + # Get back current container + container = AnalyzerResultContainer() + # Get feature extraction results from yaafe + featNames = self.yaafe_engine.getOutputs().keys() + for featName in featNames: + # Define ID fields + id = 'yaafe_' + featName + name = 'Yaafe ' + featName + unit = '' + # Get results from Yaafe engine + result = AnalyzerResult(id = id, name = name, unit = unit) + result.value = self.yaafe_engine.readOutput(featName) # Read Yaafe Results + # Store results in Container + if len(result.value): + container.add_result(result) + + return container + + + + -- 2.39.5