]> git.parisson.com Git - timeside.git/commitdiff
Move aubio analyzers in timeside.analyzer.aubio sub-package
authorThomas Fillon <thomas@parisson.com>
Wed, 14 May 2014 10:35:00 +0000 (12:35 +0200)
committerThomas Fillon <thomas@parisson.com>
Wed, 14 May 2014 10:35:00 +0000 (12:35 +0200)
timeside/analyzer/aubio/__init__.py [new file with mode: 0644]
timeside/analyzer/aubio/aubio_melenergy.py [new file with mode: 0644]
timeside/analyzer/aubio/aubio_mfcc.py [new file with mode: 0644]
timeside/analyzer/aubio/aubio_pitch.py [new file with mode: 0644]
timeside/analyzer/aubio/aubio_specdesc.py [new file with mode: 0644]
timeside/analyzer/aubio/aubio_temporal.py [new file with mode: 0644]

diff --git a/timeside/analyzer/aubio/__init__.py b/timeside/analyzer/aubio/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/timeside/analyzer/aubio/aubio_melenergy.py b/timeside/analyzer/aubio/aubio_melenergy.py
new file mode 100644 (file)
index 0000000..9fd7a4c
--- /dev/null
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2013 Paul Brossier <piem@piem.org>
+
+# 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 <http://www.gnu.org/licenses/>.
+
+# Author: Paul Brossier <piem@piem.org>
+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 (file)
index 0000000..94ef749
--- /dev/null
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2013 Paul Brossier <piem@piem.org>
+
+# 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 <http://www.gnu.org/licenses/>.
+
+# Author: Paul Brossier <piem@piem.org>
+
+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 (file)
index 0000000..4ef757d
--- /dev/null
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2013 Paul Brossier <piem@piem.org>
+
+# 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 <http://www.gnu.org/licenses/>.
+
+# Author: Paul Brossier <piem@piem.org>
+
+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 (file)
index 0000000..40fbb63
--- /dev/null
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2013 Paul Brossier <piem@piem.org>
+
+# 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 <http://www.gnu.org/licenses/>.
+
+# Author: Paul Brossier <piem@piem.org>
+
+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 (file)
index 0000000..870844f
--- /dev/null
@@ -0,0 +1,163 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2013 Paul Brossier <piem@piem.org>
+
+# 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 <http://www.gnu.org/licenses/>.
+
+# Author: Paul Brossier <piem@piem.org>
+
+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)