From 01df5f5c2696e891df183dad37a1e4a36a8dfe81 Mon Sep 17 00:00:00 2001 From: Thomas Fillon Date: Wed, 14 May 2014 12:35:00 +0200 Subject: [PATCH] Move aubio analyzers in timeside.analyzer.aubio sub-package --- timeside/analyzer/aubio/__init__.py | 0 timeside/analyzer/aubio/aubio_melenergy.py | 83 +++++++++++ timeside/analyzer/aubio/aubio_mfcc.py | 85 +++++++++++ timeside/analyzer/aubio/aubio_pitch.py | 101 +++++++++++++ timeside/analyzer/aubio/aubio_specdesc.py | 97 ++++++++++++ timeside/analyzer/aubio/aubio_temporal.py | 163 +++++++++++++++++++++ 6 files changed, 529 insertions(+) create mode 100644 timeside/analyzer/aubio/__init__.py create mode 100644 timeside/analyzer/aubio/aubio_melenergy.py create mode 100644 timeside/analyzer/aubio/aubio_mfcc.py create mode 100644 timeside/analyzer/aubio/aubio_pitch.py create mode 100644 timeside/analyzer/aubio/aubio_specdesc.py create mode 100644 timeside/analyzer/aubio/aubio_temporal.py diff --git a/timeside/analyzer/aubio/__init__.py b/timeside/analyzer/aubio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/timeside/analyzer/aubio/aubio_melenergy.py b/timeside/analyzer/aubio/aubio_melenergy.py new file mode 100644 index 0000000..9fd7a4c --- /dev/null +++ b/timeside/analyzer/aubio/aubio_melenergy.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Paul Brossier + +# 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: Paul Brossier +from __future__ import absolute_import + +from ...core import implements, interfacedoc +from ..core import Analyzer +from ...api import IAnalyzer +from ..preprocessors import downmix_to_mono, frames_adapter + +from aubio import filterbank, pvoc + + +class AubioMelEnergy(Analyzer): + + """Aubio Mel Energy analyzer""" + implements(IAnalyzer) + + def __init__(self): + super(AubioMelEnergy, self).__init__() + self.input_blocksize = 1024 + self.input_stepsize = self.input_blocksize / 4 + + @interfacedoc + def setup(self, channels=None, samplerate=None, + blocksize=None, totalframes=None): + super(AubioMelEnergy, self).setup( + channels, samplerate, blocksize, totalframes) + self.n_filters = 40 + self.n_coeffs = 13 + self.pvoc = pvoc(self.input_blocksize, self.input_stepsize) + self.melenergy = filterbank(self.n_filters, self.input_blocksize) + self.melenergy.set_mel_coeffs_slaney(samplerate) + self.block_read = 0 + self.melenergy_results = [] + + @staticmethod + @interfacedoc + def id(): + return "aubio_melenergy" + + @staticmethod + @interfacedoc + def name(): + return "Mel Energy (aubio)" + + @staticmethod + @interfacedoc + def unit(): + return "" + + @downmix_to_mono + @frames_adapter + def process(self, frames, eod=False): + + fftgrain = self.pvoc(frames) + self.melenergy_results.append(self.melenergy(fftgrain)) + self.block_read += 1 + return frames, eod + + def post_process(self): + melenergy = self.new_result(data_mode='value', time_mode='framewise') + melenergy.parameters = dict(n_filters=self.n_filters, + n_coeffs=self.n_coeffs) + melenergy.data_object.value = self.melenergy_results + self.process_pipe.results.add(melenergy) diff --git a/timeside/analyzer/aubio/aubio_mfcc.py b/timeside/analyzer/aubio/aubio_mfcc.py new file mode 100644 index 0000000..94ef749 --- /dev/null +++ b/timeside/analyzer/aubio/aubio_mfcc.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Paul Brossier + +# 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: Paul Brossier + +from timeside.core import implements, interfacedoc +from timeside.analyzer.core import Analyzer +from timeside.api import IAnalyzer +from timeside.analyzer.preprocessors import downmix_to_mono, frames_adapter + +import numpy +from aubio import mfcc, pvoc + + +class AubioMfcc(Analyzer): + + """Aubio MFCC analyzer""" + implements(IAnalyzer) + + def __init__(self): + super(AubioMfcc, self).__init__() + self.input_blocksize = 1024 + self.input_stepsize = self.input_blocksize / 4 + + @interfacedoc + def setup(self, channels=None, samplerate=None, + blocksize=None, totalframes=None): + super(AubioMfcc, self).setup( + channels, samplerate, blocksize, totalframes) + self.n_filters = 40 + self.n_coeffs = 13 + self.pvoc = pvoc(self.input_blocksize, self.input_stepsize) + self.mfcc = mfcc(self.input_blocksize, + self.n_filters, + self.n_coeffs, + samplerate) + self.block_read = 0 + self.mfcc_results = numpy.zeros([self.n_coeffs, ]) + + @staticmethod + @interfacedoc + def id(): + return "aubio_mfcc" + + @staticmethod + @interfacedoc + def name(): + return "MFCC (aubio)" + + @staticmethod + @interfacedoc + def unit(): + return "" + + @downmix_to_mono + @frames_adapter + def process(self, frames, eod=False): + 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): + mfcc = self.new_result(data_mode='value', time_mode='framewise') + mfcc.parameters = dict(n_filters=self.n_filters, + n_coeffs=self.n_coeffs) + mfcc.data_object.value = self.mfcc_results + self.process_pipe.results.add(mfcc) diff --git a/timeside/analyzer/aubio/aubio_pitch.py b/timeside/analyzer/aubio/aubio_pitch.py new file mode 100644 index 0000000..4ef757d --- /dev/null +++ b/timeside/analyzer/aubio/aubio_pitch.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Paul Brossier + +# 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: Paul Brossier + +from timeside.core import implements, interfacedoc +from timeside.analyzer.core import Analyzer +from timeside.api import IAnalyzer +from timeside.analyzer.preprocessors import downmix_to_mono, frames_adapter +from aubio import pitch +import numpy as np + + +class AubioPitch(Analyzer): + + """Aubio Pitch estimation analyzer""" + implements(IAnalyzer) # TODO check if needed with inheritance + + def __init__(self): + super(AubioPitch, self).__init__() + self.input_blocksize = 2048 + self.input_stepsize = self.input_blocksize / 2 + + @interfacedoc + def setup(self, channels=None, samplerate=None, + blocksize=None, totalframes=None): + super(AubioPitch, self).setup(channels, + samplerate, + blocksize, + totalframes) + self.aubio_pitch = pitch( + "default", self.input_blocksize, self.input_stepsize, + samplerate) + self.aubio_pitch.set_unit("freq") + self.block_read = 0 + self.pitches = [] + self.pitch_confidences = [] + + @staticmethod + @interfacedoc + def id(): + return "aubio_pitch" + + @staticmethod + @interfacedoc + def name(): + return "f0 (aubio)" + + @staticmethod + @interfacedoc + def unit(): + return "Hz" + + def __str__(self): + return "pitch values" + + @downmix_to_mono + @frames_adapter + def process(self, frames, eod=False): + #time = self.block_read * self.input_stepsize * 1. / self.samplerate() + self.pitches += [self.aubio_pitch(frames)[0]] + self.pitch_confidences += [ + np.nan_to_num(self.aubio_pitch.get_confidence())] + self.block_read += 1 + return frames, eod + + def post_process(self): + pitch = self.new_result(data_mode='value', time_mode='framewise') + + # parameters : None # TODO check with Piem "default" and "freq" in + # setup + + pitch.id_metadata.id += '.' + "pitch" + pitch.id_metadata.name += ' ' + "pitch" + pitch.id_metadata.unit = "Hz" + pitch.data_object.value = self.pitches + self.process_pipe.results.add(pitch) + + pitch_confidence = self.new_result( + data_mode='value', time_mode='framewise') + pitch_confidence.id_metadata.id += '.' + "pitch_confidence" + pitch_confidence.id_metadata.name += ' ' + "pitch confidence" + pitch_confidence.id_metadata.unit = None + pitch_confidence.data_object.value = self.pitch_confidences + self.process_pipe.results.add(pitch_confidence) diff --git a/timeside/analyzer/aubio/aubio_specdesc.py b/timeside/analyzer/aubio/aubio_specdesc.py new file mode 100644 index 0000000..40fbb63 --- /dev/null +++ b/timeside/analyzer/aubio/aubio_specdesc.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Paul Brossier + +# 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: Paul Brossier + +from timeside.core import Processor, implements, interfacedoc, FixedSizeInputAdapter +from timeside.analyzer.core import Analyzer +from timeside.api import IAnalyzer +from timeside.analyzer.preprocessors import downmix_to_mono, frames_adapter + +from aubio import specdesc, pvoc + + +class AubioSpecdesc(Analyzer): + + """Aubio Spectral Descriptors collection analyzer""" + implements(IAnalyzer) + + def __init__(self): + super(AubioSpecdesc, self).__init__() + self.input_blocksize = 1024 + self.input_stepsize = self.input_blocksize / 4 + + @interfacedoc + def setup(self, channels=None, samplerate=None, + blocksize=None, totalframes=None): + super( + AubioSpecdesc, + self).setup( + channels, + samplerate, + blocksize, + totalframes) + self.block_read = 0 + self.pvoc = pvoc(self.input_blocksize, self.input_stepsize) + self.methods = [ + 'default', 'energy', 'hfc', 'complex', 'phase', 'specdiff', 'kl', + 'mkl', 'specflux', 'centroid', 'slope', 'rolloff', 'spread', 'skewness', + 'kurtosis', 'decrease'] + self.specdesc = {} + self.specdesc_results = {} + for method in self.methods: + self.specdesc[method] = specdesc(method, self.input_blocksize) + self.specdesc_results[method] = [] + + @staticmethod + @interfacedoc + def id(): + return "aubio_specdesc" + + @staticmethod + @interfacedoc + def name(): + return "Spectral Descriptor (aubio)" + + @staticmethod + @interfacedoc + def unit(): + return "" + + @downmix_to_mono + @frames_adapter + def process(self, frames, eod=False): + 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): + + # For each method store results in container + for method in self.methods: + res_specdesc = self.new_result(data_mode='value', + time_mode='framewise') + # Set metadata + res_specdesc.id_metadata.id += '.' + method + res_specdesc.id_metadata.name = ' ' + method + res_specdesc.data_object.value = self.specdesc_results[method] + + self.process_pipe.results.add(res_specdesc) diff --git a/timeside/analyzer/aubio/aubio_temporal.py b/timeside/analyzer/aubio/aubio_temporal.py new file mode 100644 index 0000000..870844f --- /dev/null +++ b/timeside/analyzer/aubio/aubio_temporal.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Paul Brossier + +# 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: Paul Brossier + +from timeside.core import implements, interfacedoc +from timeside.analyzer.core import Analyzer +from timeside.api import IAnalyzer +from timeside.analyzer.preprocessors import downmix_to_mono, frames_adapter +from aubio import onset, tempo + +import numpy + + +class AubioTemporal(Analyzer): + + """Aubio Temporal analyzer""" + implements(IAnalyzer) + + def __init__(self): + super(AubioTemporal, self).__init__() + self.input_blocksize = 1024 + self.input_stepsize = 256 + + @interfacedoc + def setup(self, + channels=None, + samplerate=None, + blocksize=None, + totalframes=None): + super(AubioTemporal, self).setup( + channels, samplerate, blocksize, totalframes) + self.o = onset( + "default", self.input_blocksize, self.input_stepsize, samplerate) + self.t = tempo( + "default", self.input_blocksize, self.input_stepsize, samplerate) + self.block_read = 0 + self.onsets = [] + self.beats = [] + self.beat_confidences = [] + + @staticmethod + @interfacedoc + def id(): + return "aubio_temporal" + + @staticmethod + @interfacedoc + def name(): + return "onsets (aubio)" + + @staticmethod + @interfacedoc + def unit(): + return "" + + def __str__(self): + return "%s %s" % (str(self.value), self.unit()) + + @downmix_to_mono + @frames_adapter + def process(self, frames, eod=False): + if self.o(frames): + self.onsets += [self.o.get_last_s()] + if self.t(frames): + self.beats += [self.t.get_last_s()] + self.beat_confidences += [self.t.get_confidence()] + self.block_read += 1 + return frames, eod + + def post_process(self): + + #--------------------------------- + # Onsets: Event (time, "Onset") + #--------------------------------- + onsets = self.new_result(data_mode='label', time_mode='event') + onsets.id_metadata.id += '.' + 'onset' + onsets.id_metadata.name += ' ' + 'Onset' + onsets.id_metadata.unit = 's' + onsets.data_object.time = self.onsets + onsets.data_object.label = numpy.ones(len(self.onsets)) + onsets.label_metadata.label = {1: 'Onset'} + + self.process_pipe.results.add(onsets) + + #--------------------------------- + # Onset Rate: Segment (time, duration, value) + #--------------------------------- + onsetrate = self.new_result(data_mode='value', time_mode='segment') + onsetrate.id_metadata.id += '.' + "onset_rate" + onsetrate.id_metadata.name = " " + "Onset Rate" + onsetrate.id_metadata.unit = "bpm" + if len(self.onsets) > 1: + periods = numpy.diff(self.onsets) + periods = numpy.append(periods, periods[-1]) + onsetrate.data_object.time = self.onsets + onsetrate.data_object.duration = periods + onsetrate.data_object.value = 60. / periods + else: + onsetrate.data_object.value = [] + onsetrate.data_object.time = [] + + self.process_pipe.results.add(onsetrate) + + #--------------------------------- + # Beats: Event (time, "Beat") + #--------------------------------- + beats = self.new_result(data_mode='label', time_mode='event') + beats.id_metadata.id += '.' + "beat" + beats.id_metadata.name += " " + "Beats" + beats.id_metadata.unit = "s" + beats.data_object.time = self.beats + beats.data_object.label = numpy.ones(len(self.beats)) + beats.label_metadata.label = {1: 'Beat'} + + self.process_pipe.results.add(beats) + + #--------------------------------- + # Beat confidences: Event (time, value) + #--------------------------------- + beat_confidences = self.new_result( + data_mode='value', time_mode='event') + beat_confidences.id_metadata.id += '.' + "beat_confidence" + beat_confidences.id_metadata.name += " " + "Beat confidences" + beat_confidences.id_metadata.unit = None + beat_confidences.data_object.time = self.beats + beat_confidences.data_object.value = self.beat_confidences + + self.process_pipe.results.add(beat_confidences) + + #--------------------------------- + # BPM: Segment (time, duration, value) + #--------------------------------- + bpm = self.new_result(data_mode='value', time_mode='segment') + bpm.id_metadata.id += '.' + "bpm" + bpm.id_metadata.name += ' ' + "bpm" + bpm.id_metadata.unit = "bpm" + if len(self.beats) > 1: + periods = numpy.diff(self.beats) + periods = numpy.append(periods, periods[-1]) + bpm.data_object.time = self.beats + bpm.data_object.duration = periods + bpm.data_object.value = 60. / periods + else: + bpm.data_object.value = [] + + self.process_pipe.results.add(bpm) -- 2.39.5