]> git.parisson.com Git - timeside.git/commitdiff
rename analysis
authoryomguy <yomguy@parisson.com>
Wed, 7 Oct 2009 13:17:34 +0000 (13:17 +0000)
committeryomguy <yomguy@parisson.com>
Wed, 7 Oct 2009 13:17:34 +0000 (13:17 +0000)
28 files changed:
analysis/__init__.py [deleted file]
analysis/api.py [deleted file]
analysis/channels.py [deleted file]
analysis/core.py [deleted file]
analysis/dc.py [deleted file]
analysis/duration.py [deleted file]
analysis/encoding.py [deleted file]
analysis/format.py [deleted file]
analysis/max_level.py [deleted file]
analysis/mean_level.py [deleted file]
analysis/resolution.py [deleted file]
analysis/samplerate.py [deleted file]
analysis/vamp/__init__.py [deleted file]
analysis/vamp/core.py [deleted file]
analyze/__init__.py [new file with mode: 0644]
analyze/api.py [new file with mode: 0644]
analyze/channels.py [new file with mode: 0644]
analyze/core.py [new file with mode: 0644]
analyze/dc.py [new file with mode: 0644]
analyze/duration.py [new file with mode: 0644]
analyze/encoding.py [new file with mode: 0644]
analyze/format.py [new file with mode: 0644]
analyze/max_level.py [new file with mode: 0644]
analyze/mean_level.py [new file with mode: 0644]
analyze/resolution.py [new file with mode: 0644]
analyze/samplerate.py [new file with mode: 0644]
analyze/vamp/__init__.py [new file with mode: 0644]
analyze/vamp/core.py [new file with mode: 0644]

diff --git a/analysis/__init__.py b/analysis/__init__.py
deleted file mode 100644 (file)
index 0c2c597..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- coding: utf-8 -*-
-from timeside.analysis.api import *
-from timeside.analysis.core import *
-from timeside.analysis.channels import *
-from timeside.analysis.format import *
-from timeside.analysis.encoding import *
-from timeside.analysis.resolution import *
-from timeside.analysis.samplerate import *
-from timeside.analysis.duration import *
-from timeside.analysis.max_level import *
-from timeside.analysis.mean_level import *
-from timeside.analysis.dc import *
-
-
diff --git a/analysis/api.py b/analysis/api.py
deleted file mode 100644 (file)
index 634ef2a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.core import *
-
-class IMediaItemAnalyzer(Interface):
-    """Media item analyzer driver interface"""
-
-    def get_id():
-        """Return a short id alphanumeric, lower-case string."""
-
-    def get_name():
-        """Return the analysis name, such as "Mean Level", "Max level",
-        "Total length, etc..
-        """
-
-    def get_unit():
-        """Return the unit of the data such as "dB", "seconds", etc...
-        """
-    
-    def render(media_item, options=None):
-        """Return the result data of the process"""
-            
diff --git a/analysis/channels.py b/analysis/channels.py
deleted file mode 100644 (file)
index 5f1c2ee..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.analysis.core import *
-from timeside.analysis.api import IMediaItemAnalyzer
-import numpy
-
-class ChannelAnalyser(AudioProcessor):
-    """Media item analyzer driver interface"""
-
-    implements(IMediaItemAnalyzer)
-
-    def get_id(self):
-        return "nb_channels"
-
-    def get_name(self):
-        return "Channels"
-
-    def get_unit(self):
-        return ""
-
-    def render(self, media_item, options=None):
-        self.pre_process(media_item)
-        if self.channels == 1:
-            return 'mono'
-        if self.channels == 2:
-            return 'stereo'
-        else:
-            return self.channels
diff --git a/analysis/core.py b/analysis/core.py
deleted file mode 100644 (file)
index 9c2a2ce..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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/>.
-
-# Authors:
-#   Bram de Jong <bram.dejong at domain.com where domain in gmail>
-#   Guillaume Pellerin <yomguy at parisson.com>
-
-from timeside.core import *
-import optparse, math, sys
-import numpy
-import scikits.audiolab as audiolab
-
-class AudioProcessor(Component):
-    
-    def __init__(self):
-        self.fft_size = 2048
-        self.window_function = numpy.ones
-        self.window = self.window_function(self.fft_size)
-        self.spectrum_range = None
-        self.lower = 100
-        self.higher = 22050
-        self.lower_log = math.log10(self.lower)
-        self.higher_log = math.log10(self.higher)
-        self.clip = lambda val, low, high: min(high, max(low, val))
-
-    def pre_process(self, media_item):
-        wav_file = media_item.file.path
-        self.audio_file = audiolab.sndfile(wav_file, 'read')
-        self.frames = self.audio_file.get_nframes()
-        self.samplerate = self.audio_file.get_samplerate()
-        self.channels = self.audio_file.get_channels()
-        self.format = self.audio_file.get_file_format()
-        self.encoding = self.audio_file.get_encoding()
-
-    def get_samples(self):
-        samples = self.audio_file.read_frames(self.frames)
-        return samples
-        
-    def get_mono_samples(self):
-        # convert to mono by selecting left channel only
-        samples = self.get_samples()
-        if self.channels > 1:
-            return samples[:,0]
-        else:
-            return samples
-
-    def read(self, start, size, resize_if_less=False):
-        """ read size samples starting at start, if resize_if_less is True and less than size
-        samples are read, resize the array to size and fill with zeros """
-        
-        # number of zeros to add to start and end of the buffer
-        add_to_start = 0
-        add_to_end = 0
-        
-        if start < 0:
-            # the first FFT window starts centered around zero
-            if size + start <= 0:
-                if resize_if_less:
-                    return numpy.zeros(size)
-                else:
-                    return numpy.array([])
-            else:
-                self.audio_file.seek(0)
-
-                add_to_start = -start # remember: start is negative!
-                to_read = size + start
-
-                if to_read > self.frames:
-                    add_to_end = to_read - self.frames
-                    to_read = self.frames
-        else:
-            self.audio_file.seek(start)
-        
-            to_read = size
-            if start + to_read >= self.frames:
-                to_read = self.frames - start
-                add_to_end = size - to_read
-        
-        try:
-            samples = self.audio_file.read_frames(to_read)
-        except IOError:
-            # this can happen for wave files with broken headers...
-            if resize_if_less:
-                return numpy.zeros(size)
-            else:
-                return numpy.zeros(2)
-
-        # convert to mono by selecting left channel only
-        if self.channels > 1:
-            samples = samples[:,0]
-
-        if resize_if_less and (add_to_start > 0 or add_to_end > 0):
-            if add_to_start > 0:
-                samples = numpy.concatenate((numpy.zeros(add_to_start), samples), axis=1)
-            
-            if add_to_end > 0:
-                samples = numpy.resize(samples, size)
-                samples[size - add_to_end:] = 0
-        
-        return samples
-
-
-    def spectral_centroid(self, seek_point, spec_range=120.0):
-        """ starting at seek_point read fft_size samples, and calculate the spectral centroid """
-        
-        samples = self.read(seek_point - self.fft_size/2, self.fft_size, True)
-
-        samples *= self.window
-        fft = numpy.fft.fft(samples)
-        spectrum = numpy.abs(fft[:fft.shape[0] / 2 + 1]) / float(self.fft_size) # normalized abs(FFT) between 0 and 1
-        length = numpy.float64(spectrum.shape[0])
-        
-        # scale the db spectrum from [- spec_range db ... 0 db] > [0..1]
-        db_spectrum = ((20*(numpy.log10(spectrum + 1e-30))).clip(-spec_range, 0.0) + spec_range)/spec_range
-        
-        energy = spectrum.sum()
-        spectral_centroid = 0
-        
-        if energy > 1e-20:
-            # calculate the spectral centroid
-            
-            if self.spectrum_range == None:
-                self.spectrum_range = numpy.arange(length)
-        
-            spectral_centroid = (spectrum * self.spectrum_range).sum() / (energy * (length - 1)) * self.samplerate * 0.5
-            
-            # clip > log10 > scale between 0 and 1
-            spectral_centroid = (math.log10(self.clip(spectral_centroid, self.lower, self.higher)) - self.lower_log) / (self.higher_log - self.lower_log)
-        
-        return (spectral_centroid, db_spectrum)
-
-
-    def peaks(self, start_seek, end_seek):
-        """ read all samples between start_seek and end_seek, then find the minimum and maximum peak
-        in that range. Returns that pair in the order they were found. So if min was found first,
-        it returns (min, max) else the other way around. """
-        
-        # larger blocksizes are faster but take more mem...
-        # Aha, Watson, a clue, a tradeof!
-        block_size = 4096
-    
-        max_index = -1
-        max_value = -1
-        min_index = -1
-        min_value = 1
-    
-        if end_seek > self.frames:
-            end_seek = self.frames
-    
-        if block_size > end_seek - start_seek:
-            block_size = end_seek - start_seek
-            
-        if block_size <= 1:
-            samples = self.read(start_seek, 1)
-            return samples[0], samples[0]
-        elif block_size == 2:
-            samples = self.read(start_seek, True)
-            return samples[0], samples[1]
-        
-        for i in range(start_seek, end_seek, block_size):
-            samples = self.read(i, block_size)
-    
-            local_max_index = numpy.argmax(samples)
-            local_max_value = samples[local_max_index]
-    
-            if local_max_value > max_value:
-                max_value = local_max_value
-                max_index = local_max_index
-    
-            local_min_index = numpy.argmin(samples)
-            local_min_value = samples[local_min_index]
-            
-            if local_min_value < min_value:
-                min_value = local_min_value
-                min_index = local_min_index
-
-        if min_index < max_index:
-            return (min_value, max_value)
-        else:
-            return (max_value, min_value)
-
-
-        
diff --git a/analysis/dc.py b/analysis/dc.py
deleted file mode 100644 (file)
index c15a6f8..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.analysis.core import *
-from timeside.analysis.api import IMediaItemAnalyzer
-import numpy
-
-class MeanDCShiftAnalyser(AudioProcessor):
-    """Media item analyzer driver interface"""
-
-    implements(IMediaItemAnalyzer)
-
-    def get_id(self):
-        return "dc"
-
-    def get_name(self):
-        return "Mean DC shift"
-
-    def get_unit(self):
-        return "%"
-
-    def render(self, media_item, options=None):
-        self.pre_process(media_item)
-        samples = self.get_mono_samples()
-        return numpy.round(100*numpy.mean(samples),4)
diff --git a/analysis/duration.py b/analysis/duration.py
deleted file mode 100644 (file)
index 9428021..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.analysis.core import *
-from timeside.analysis.api import IMediaItemAnalyzer
-import numpy
-import datetime
-
-class DurationAnalyzer(AudioProcessor):
-    """Media item analyzer driver interface"""
-
-    implements(IMediaItemAnalyzer)
-
-    def get_id(self):
-        return "duration"
-
-    def get_name(self):
-        return "Duration"
-
-    def get_unit(self):
-        return "h:m:s"
-
-    def render(self, media_item, options=None):
-        self.pre_process(media_item)
-        media_time = numpy.round(float(self.frames)/float(self.samplerate),0)
-        return datetime.timedelta(0,media_time)
diff --git a/analysis/encoding.py b/analysis/encoding.py
deleted file mode 100644 (file)
index 897ff04..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.analysis.core import *
-from timeside.analysis.api import IMediaItemAnalyzer
-import numpy
-
-class EncodingAnalyser(AudioProcessor):
-    """Media item analyzer driver interface"""
-
-    implements(IMediaItemAnalyzer)
-
-    def get_id(self):
-        return "encoding"
-
-    def get_name(self):
-        return "Encoding format"
-
-    def get_unit(self):
-        return ""
-
-    def render(self, media_item, options=None):
-        self.pre_process(media_item)
-        return self.encoding
diff --git a/analysis/format.py b/analysis/format.py
deleted file mode 100644 (file)
index 480ce00..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.analysis.core import *
-from timeside.analysis.api import IMediaItemAnalyzer
-import numpy
-
-class FormatAnalyser(AudioProcessor):
-    """Media item analyzer driver interface"""
-
-    implements(IMediaItemAnalyzer)
-
-    def get_id(self):
-        return "format"
-
-    def get_name(self):
-        return "File format"
-
-    def get_unit(self):
-        return ""
-
-    def render(self, media_item, options=None):
-        self.pre_process(media_item)
-        return self.format
diff --git a/analysis/max_level.py b/analysis/max_level.py
deleted file mode 100644 (file)
index a54e460..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.analysis.core import *
-from timeside.analysis.api import IMediaItemAnalyzer
-import numpy
-
-class MaxLevelAnalyzer(AudioProcessor):
-    """Media item analyzer driver interface"""
-
-    implements(IMediaItemAnalyzer)
-
-    def get_id(self):
-        return "max_level"
-
-    def get_name(self):
-        return "Maximum peak level"
-
-    def get_unit(self):
-        return "dB"
-
-    def render(self, media_item, options=None):
-        self.pre_process(media_item)
-        samples = self.get_samples()
-        return numpy.round(20*numpy.log10(numpy.max(samples)),2)
\ No newline at end of file
diff --git a/analysis/mean_level.py b/analysis/mean_level.py
deleted file mode 100644 (file)
index b7696ed..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.analysis.core import *
-from timeside.analysis.api import IMediaItemAnalyzer
-import numpy
-
-class MeanLevelAnalyser(AudioProcessor):
-    """Media item analyzer driver interface"""
-
-    implements(IMediaItemAnalyzer)
-
-    def get_id(self):
-        return "mean_level"
-
-    def get_name(self):
-        return "Mean RMS level"
-
-    def get_unit(self):
-        return "dB"
-
-    def render(self, media_item, options=None):
-        self.pre_process(media_item)
-        samples = self.get_mono_samples()
-        return numpy.round(20*numpy.log10(numpy.mean(numpy.sqrt(numpy.square(samples)))),2)
diff --git a/analysis/resolution.py b/analysis/resolution.py
deleted file mode 100644 (file)
index 98c2901..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.analysis.core import *
-from timeside.analysis.api import IMediaItemAnalyzer
-import numpy
-
-class ResolutionAnalyser(AudioProcessor):
-    """Media item analyzer driver interface"""
-
-    implements(IMediaItemAnalyzer)
-
-    def get_id(self):
-        return "resolution"
-
-    def get_name(self):
-        return "Resolution"
-
-    def get_unit(self):
-        return "bits"
-
-    def render(self, media_item, options=None):
-        self.pre_process(media_item)
-        if '8' in self.encoding:
-            return 8
-        if '16' in self.encoding:
-            return 16
-        if '24' in self.encoding:
-            return 24
-        if '32' in self.encoding:
-            return 32
-        else:
-            return ''
\ No newline at end of file
diff --git a/analysis/samplerate.py b/analysis/samplerate.py
deleted file mode 100644 (file)
index 0970fcb..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.analysis.core import *
-from timeside.analysis.api import IMediaItemAnalyzer
-import numpy
-
-class SampleRateAnalyzer(AudioProcessor):
-    """Media item analyzer driver interface"""
-
-    implements(IMediaItemAnalyzer)
-
-    def get_id(self):
-        return "samplerate"
-
-    def get_name(self):
-        return "Samplerate"
-
-    def get_unit(self):
-        return "Hz"
-
-    def render(self, media_item, options=None):
-        self.pre_process(media_item)
-        return self.samplerate
diff --git a/analysis/vamp/__init__.py b/analysis/vamp/__init__.py
deleted file mode 100644 (file)
index 1dc0d67..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from timeside.analysis.vamp.core import *
diff --git a/analysis/vamp/core.py b/analysis/vamp/core.py
deleted file mode 100644 (file)
index bc32def..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# 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: Guillaume Pellerin <yomguy@parisson.com>
-
-from timeside.core import *
-from tempfile import NamedTemporaryFile
-from timeside.analysis.api import IMediaItemAnalyzer
-import os
-import random
-import subprocess
-import signal
-import time
-
-class VampCoreAnalyzer:
-    """Parent class for Vamp plugin drivers"""
-
-    def __init__(self):
-        self.vamp_path = '/usr/lib/vamp/'
-        # needs vamp-examples package
-        self.host = 'vamp-simple-host'
-        self.buffer_size = 0xFFFF
-               
-    def get_id(self):
-        return "vamp_plugins"
-
-    def get_name(self):
-        return "Vamp plugins"
-
-    def get_unit(self):
-        return ""
-
-    def get_plugins_list(self):
-        if os.path.exists(self.vamp_path):
-            args = ' --list-outputs'
-            command = self.host + args
-            #tmp_file = NamedTemporaryFile()
-            data = self.core_process(command, self.buffer_size)
-            text = ''
-            plugins = []
-            for chunk in data:
-                text = text + chunk
-            lines = text.split('\n')
-            for line in lines:
-                if line != '':
-                    struct = line.split(':')
-                    struct = struct[1:]
-                    plugins.append(struct)
-            return plugins
-        else:
-            return []
-
-    def get_wav_path(self, media_item):
-        return settings.MEDIA_ROOT + '/' + media_item.file
-        #return media_item
-        
-    def render(self, plugin, media_item):
-        self.wavFile = self.get_wav_path(media_item)
-        args = ' -s ' + ':'.join(plugin) + ' ' + str(self.wavFile)
-        command = command = self.host + args
-        data = self.core_process(command, self.buffer_size)
-        string = ''
-        values = {}
-        for chunk in data:
-            string = string + chunk
-        lines = string.split('\n')
-        for line in lines:
-            if line != '':
-                struct = line.split(':')
-                values[struct[0]] = struct[1]
-        return values
-
-    def core_process(self, command, buffer_size):
-        """Encode and stream audio data through a generator"""
-        
-        __chunk = 0
-
-        try:
-            proc = subprocess.Popen(command,
-                    shell = True,
-                    bufsize = buffer_size,
-                    stdin = subprocess.PIPE,
-                    stdout = subprocess.PIPE,
-                    close_fds = True)
-        except:
-            raise VampProcessError('Command failure:', command, proc)
-            
-        # Core processing
-        while True:
-            __chunk = proc.stdout.read(buffer_size)
-            status = proc.poll()
-            if status != None and status != 0:
-                raise VampProcessError('Command failure:', command, proc)
-            if len(__chunk) == 0:
-                break
-            yield __chunk
-        
-
-class VampProcessError(TimeSideError):
-
-    def __init__(self, message, command, subprocess):
-        self.message = message
-        self.command = str(command)
-        self.subprocess = subprocess
-
-    def __str__(self):
-        if self.subprocess.stderr != None:
-            error = self.subprocess.stderr.read()
-        else:
-            error = ''
-        return "%s ; command: %s; error: %s" % (self.message,
-                                                self.command,
-                                                error)
diff --git a/analyze/__init__.py b/analyze/__init__.py
new file mode 100644 (file)
index 0000000..0c2c597
--- /dev/null
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+from timeside.analysis.api import *
+from timeside.analysis.core import *
+from timeside.analysis.channels import *
+from timeside.analysis.format import *
+from timeside.analysis.encoding import *
+from timeside.analysis.resolution import *
+from timeside.analysis.samplerate import *
+from timeside.analysis.duration import *
+from timeside.analysis.max_level import *
+from timeside.analysis.mean_level import *
+from timeside.analysis.dc import *
+
+
diff --git a/analyze/api.py b/analyze/api.py
new file mode 100644 (file)
index 0000000..634ef2a
--- /dev/null
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.core import *
+
+class IMediaItemAnalyzer(Interface):
+    """Media item analyzer driver interface"""
+
+    def get_id():
+        """Return a short id alphanumeric, lower-case string."""
+
+    def get_name():
+        """Return the analysis name, such as "Mean Level", "Max level",
+        "Total length, etc..
+        """
+
+    def get_unit():
+        """Return the unit of the data such as "dB", "seconds", etc...
+        """
+    
+    def render(media_item, options=None):
+        """Return the result data of the process"""
+            
diff --git a/analyze/channels.py b/analyze/channels.py
new file mode 100644 (file)
index 0000000..5f1c2ee
--- /dev/null
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.analysis.core import *
+from timeside.analysis.api import IMediaItemAnalyzer
+import numpy
+
+class ChannelAnalyser(AudioProcessor):
+    """Media item analyzer driver interface"""
+
+    implements(IMediaItemAnalyzer)
+
+    def get_id(self):
+        return "nb_channels"
+
+    def get_name(self):
+        return "Channels"
+
+    def get_unit(self):
+        return ""
+
+    def render(self, media_item, options=None):
+        self.pre_process(media_item)
+        if self.channels == 1:
+            return 'mono'
+        if self.channels == 2:
+            return 'stereo'
+        else:
+            return self.channels
diff --git a/analyze/core.py b/analyze/core.py
new file mode 100644 (file)
index 0000000..9c2a2ce
--- /dev/null
@@ -0,0 +1,199 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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/>.
+
+# Authors:
+#   Bram de Jong <bram.dejong at domain.com where domain in gmail>
+#   Guillaume Pellerin <yomguy at parisson.com>
+
+from timeside.core import *
+import optparse, math, sys
+import numpy
+import scikits.audiolab as audiolab
+
+class AudioProcessor(Component):
+    
+    def __init__(self):
+        self.fft_size = 2048
+        self.window_function = numpy.ones
+        self.window = self.window_function(self.fft_size)
+        self.spectrum_range = None
+        self.lower = 100
+        self.higher = 22050
+        self.lower_log = math.log10(self.lower)
+        self.higher_log = math.log10(self.higher)
+        self.clip = lambda val, low, high: min(high, max(low, val))
+
+    def pre_process(self, media_item):
+        wav_file = media_item.file.path
+        self.audio_file = audiolab.sndfile(wav_file, 'read')
+        self.frames = self.audio_file.get_nframes()
+        self.samplerate = self.audio_file.get_samplerate()
+        self.channels = self.audio_file.get_channels()
+        self.format = self.audio_file.get_file_format()
+        self.encoding = self.audio_file.get_encoding()
+
+    def get_samples(self):
+        samples = self.audio_file.read_frames(self.frames)
+        return samples
+        
+    def get_mono_samples(self):
+        # convert to mono by selecting left channel only
+        samples = self.get_samples()
+        if self.channels > 1:
+            return samples[:,0]
+        else:
+            return samples
+
+    def read(self, start, size, resize_if_less=False):
+        """ read size samples starting at start, if resize_if_less is True and less than size
+        samples are read, resize the array to size and fill with zeros """
+        
+        # number of zeros to add to start and end of the buffer
+        add_to_start = 0
+        add_to_end = 0
+        
+        if start < 0:
+            # the first FFT window starts centered around zero
+            if size + start <= 0:
+                if resize_if_less:
+                    return numpy.zeros(size)
+                else:
+                    return numpy.array([])
+            else:
+                self.audio_file.seek(0)
+
+                add_to_start = -start # remember: start is negative!
+                to_read = size + start
+
+                if to_read > self.frames:
+                    add_to_end = to_read - self.frames
+                    to_read = self.frames
+        else:
+            self.audio_file.seek(start)
+        
+            to_read = size
+            if start + to_read >= self.frames:
+                to_read = self.frames - start
+                add_to_end = size - to_read
+        
+        try:
+            samples = self.audio_file.read_frames(to_read)
+        except IOError:
+            # this can happen for wave files with broken headers...
+            if resize_if_less:
+                return numpy.zeros(size)
+            else:
+                return numpy.zeros(2)
+
+        # convert to mono by selecting left channel only
+        if self.channels > 1:
+            samples = samples[:,0]
+
+        if resize_if_less and (add_to_start > 0 or add_to_end > 0):
+            if add_to_start > 0:
+                samples = numpy.concatenate((numpy.zeros(add_to_start), samples), axis=1)
+            
+            if add_to_end > 0:
+                samples = numpy.resize(samples, size)
+                samples[size - add_to_end:] = 0
+        
+        return samples
+
+
+    def spectral_centroid(self, seek_point, spec_range=120.0):
+        """ starting at seek_point read fft_size samples, and calculate the spectral centroid """
+        
+        samples = self.read(seek_point - self.fft_size/2, self.fft_size, True)
+
+        samples *= self.window
+        fft = numpy.fft.fft(samples)
+        spectrum = numpy.abs(fft[:fft.shape[0] / 2 + 1]) / float(self.fft_size) # normalized abs(FFT) between 0 and 1
+        length = numpy.float64(spectrum.shape[0])
+        
+        # scale the db spectrum from [- spec_range db ... 0 db] > [0..1]
+        db_spectrum = ((20*(numpy.log10(spectrum + 1e-30))).clip(-spec_range, 0.0) + spec_range)/spec_range
+        
+        energy = spectrum.sum()
+        spectral_centroid = 0
+        
+        if energy > 1e-20:
+            # calculate the spectral centroid
+            
+            if self.spectrum_range == None:
+                self.spectrum_range = numpy.arange(length)
+        
+            spectral_centroid = (spectrum * self.spectrum_range).sum() / (energy * (length - 1)) * self.samplerate * 0.5
+            
+            # clip > log10 > scale between 0 and 1
+            spectral_centroid = (math.log10(self.clip(spectral_centroid, self.lower, self.higher)) - self.lower_log) / (self.higher_log - self.lower_log)
+        
+        return (spectral_centroid, db_spectrum)
+
+
+    def peaks(self, start_seek, end_seek):
+        """ read all samples between start_seek and end_seek, then find the minimum and maximum peak
+        in that range. Returns that pair in the order they were found. So if min was found first,
+        it returns (min, max) else the other way around. """
+        
+        # larger blocksizes are faster but take more mem...
+        # Aha, Watson, a clue, a tradeof!
+        block_size = 4096
+    
+        max_index = -1
+        max_value = -1
+        min_index = -1
+        min_value = 1
+    
+        if end_seek > self.frames:
+            end_seek = self.frames
+    
+        if block_size > end_seek - start_seek:
+            block_size = end_seek - start_seek
+            
+        if block_size <= 1:
+            samples = self.read(start_seek, 1)
+            return samples[0], samples[0]
+        elif block_size == 2:
+            samples = self.read(start_seek, True)
+            return samples[0], samples[1]
+        
+        for i in range(start_seek, end_seek, block_size):
+            samples = self.read(i, block_size)
+    
+            local_max_index = numpy.argmax(samples)
+            local_max_value = samples[local_max_index]
+    
+            if local_max_value > max_value:
+                max_value = local_max_value
+                max_index = local_max_index
+    
+            local_min_index = numpy.argmin(samples)
+            local_min_value = samples[local_min_index]
+            
+            if local_min_value < min_value:
+                min_value = local_min_value
+                min_index = local_min_index
+
+        if min_index < max_index:
+            return (min_value, max_value)
+        else:
+            return (max_value, min_value)
+
+
+        
diff --git a/analyze/dc.py b/analyze/dc.py
new file mode 100644 (file)
index 0000000..c15a6f8
--- /dev/null
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.analysis.core import *
+from timeside.analysis.api import IMediaItemAnalyzer
+import numpy
+
+class MeanDCShiftAnalyser(AudioProcessor):
+    """Media item analyzer driver interface"""
+
+    implements(IMediaItemAnalyzer)
+
+    def get_id(self):
+        return "dc"
+
+    def get_name(self):
+        return "Mean DC shift"
+
+    def get_unit(self):
+        return "%"
+
+    def render(self, media_item, options=None):
+        self.pre_process(media_item)
+        samples = self.get_mono_samples()
+        return numpy.round(100*numpy.mean(samples),4)
diff --git a/analyze/duration.py b/analyze/duration.py
new file mode 100644 (file)
index 0000000..9428021
--- /dev/null
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.analysis.core import *
+from timeside.analysis.api import IMediaItemAnalyzer
+import numpy
+import datetime
+
+class DurationAnalyzer(AudioProcessor):
+    """Media item analyzer driver interface"""
+
+    implements(IMediaItemAnalyzer)
+
+    def get_id(self):
+        return "duration"
+
+    def get_name(self):
+        return "Duration"
+
+    def get_unit(self):
+        return "h:m:s"
+
+    def render(self, media_item, options=None):
+        self.pre_process(media_item)
+        media_time = numpy.round(float(self.frames)/float(self.samplerate),0)
+        return datetime.timedelta(0,media_time)
diff --git a/analyze/encoding.py b/analyze/encoding.py
new file mode 100644 (file)
index 0000000..897ff04
--- /dev/null
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.analysis.core import *
+from timeside.analysis.api import IMediaItemAnalyzer
+import numpy
+
+class EncodingAnalyser(AudioProcessor):
+    """Media item analyzer driver interface"""
+
+    implements(IMediaItemAnalyzer)
+
+    def get_id(self):
+        return "encoding"
+
+    def get_name(self):
+        return "Encoding format"
+
+    def get_unit(self):
+        return ""
+
+    def render(self, media_item, options=None):
+        self.pre_process(media_item)
+        return self.encoding
diff --git a/analyze/format.py b/analyze/format.py
new file mode 100644 (file)
index 0000000..480ce00
--- /dev/null
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.analysis.core import *
+from timeside.analysis.api import IMediaItemAnalyzer
+import numpy
+
+class FormatAnalyser(AudioProcessor):
+    """Media item analyzer driver interface"""
+
+    implements(IMediaItemAnalyzer)
+
+    def get_id(self):
+        return "format"
+
+    def get_name(self):
+        return "File format"
+
+    def get_unit(self):
+        return ""
+
+    def render(self, media_item, options=None):
+        self.pre_process(media_item)
+        return self.format
diff --git a/analyze/max_level.py b/analyze/max_level.py
new file mode 100644 (file)
index 0000000..a54e460
--- /dev/null
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.analysis.core import *
+from timeside.analysis.api import IMediaItemAnalyzer
+import numpy
+
+class MaxLevelAnalyzer(AudioProcessor):
+    """Media item analyzer driver interface"""
+
+    implements(IMediaItemAnalyzer)
+
+    def get_id(self):
+        return "max_level"
+
+    def get_name(self):
+        return "Maximum peak level"
+
+    def get_unit(self):
+        return "dB"
+
+    def render(self, media_item, options=None):
+        self.pre_process(media_item)
+        samples = self.get_samples()
+        return numpy.round(20*numpy.log10(numpy.max(samples)),2)
\ No newline at end of file
diff --git a/analyze/mean_level.py b/analyze/mean_level.py
new file mode 100644 (file)
index 0000000..b7696ed
--- /dev/null
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.analysis.core import *
+from timeside.analysis.api import IMediaItemAnalyzer
+import numpy
+
+class MeanLevelAnalyser(AudioProcessor):
+    """Media item analyzer driver interface"""
+
+    implements(IMediaItemAnalyzer)
+
+    def get_id(self):
+        return "mean_level"
+
+    def get_name(self):
+        return "Mean RMS level"
+
+    def get_unit(self):
+        return "dB"
+
+    def render(self, media_item, options=None):
+        self.pre_process(media_item)
+        samples = self.get_mono_samples()
+        return numpy.round(20*numpy.log10(numpy.mean(numpy.sqrt(numpy.square(samples)))),2)
diff --git a/analyze/resolution.py b/analyze/resolution.py
new file mode 100644 (file)
index 0000000..98c2901
--- /dev/null
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.analysis.core import *
+from timeside.analysis.api import IMediaItemAnalyzer
+import numpy
+
+class ResolutionAnalyser(AudioProcessor):
+    """Media item analyzer driver interface"""
+
+    implements(IMediaItemAnalyzer)
+
+    def get_id(self):
+        return "resolution"
+
+    def get_name(self):
+        return "Resolution"
+
+    def get_unit(self):
+        return "bits"
+
+    def render(self, media_item, options=None):
+        self.pre_process(media_item)
+        if '8' in self.encoding:
+            return 8
+        if '16' in self.encoding:
+            return 16
+        if '24' in self.encoding:
+            return 24
+        if '32' in self.encoding:
+            return 32
+        else:
+            return ''
\ No newline at end of file
diff --git a/analyze/samplerate.py b/analyze/samplerate.py
new file mode 100644 (file)
index 0000000..0970fcb
--- /dev/null
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.analysis.core import *
+from timeside.analysis.api import IMediaItemAnalyzer
+import numpy
+
+class SampleRateAnalyzer(AudioProcessor):
+    """Media item analyzer driver interface"""
+
+    implements(IMediaItemAnalyzer)
+
+    def get_id(self):
+        return "samplerate"
+
+    def get_name(self):
+        return "Samplerate"
+
+    def get_unit(self):
+        return "Hz"
+
+    def render(self, media_item, options=None):
+        self.pre_process(media_item)
+        return self.samplerate
diff --git a/analyze/vamp/__init__.py b/analyze/vamp/__init__.py
new file mode 100644 (file)
index 0000000..1dc0d67
--- /dev/null
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from timeside.analysis.vamp.core import *
diff --git a/analyze/vamp/core.py b/analyze/vamp/core.py
new file mode 100644 (file)
index 0000000..bc32def
--- /dev/null
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# 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: Guillaume Pellerin <yomguy@parisson.com>
+
+from timeside.core import *
+from tempfile import NamedTemporaryFile
+from timeside.analysis.api import IMediaItemAnalyzer
+import os
+import random
+import subprocess
+import signal
+import time
+
+class VampCoreAnalyzer:
+    """Parent class for Vamp plugin drivers"""
+
+    def __init__(self):
+        self.vamp_path = '/usr/lib/vamp/'
+        # needs vamp-examples package
+        self.host = 'vamp-simple-host'
+        self.buffer_size = 0xFFFF
+               
+    def get_id(self):
+        return "vamp_plugins"
+
+    def get_name(self):
+        return "Vamp plugins"
+
+    def get_unit(self):
+        return ""
+
+    def get_plugins_list(self):
+        if os.path.exists(self.vamp_path):
+            args = ' --list-outputs'
+            command = self.host + args
+            #tmp_file = NamedTemporaryFile()
+            data = self.core_process(command, self.buffer_size)
+            text = ''
+            plugins = []
+            for chunk in data:
+                text = text + chunk
+            lines = text.split('\n')
+            for line in lines:
+                if line != '':
+                    struct = line.split(':')
+                    struct = struct[1:]
+                    plugins.append(struct)
+            return plugins
+        else:
+            return []
+
+    def get_wav_path(self, media_item):
+        return settings.MEDIA_ROOT + '/' + media_item.file
+        #return media_item
+        
+    def render(self, plugin, media_item):
+        self.wavFile = self.get_wav_path(media_item)
+        args = ' -s ' + ':'.join(plugin) + ' ' + str(self.wavFile)
+        command = command = self.host + args
+        data = self.core_process(command, self.buffer_size)
+        string = ''
+        values = {}
+        for chunk in data:
+            string = string + chunk
+        lines = string.split('\n')
+        for line in lines:
+            if line != '':
+                struct = line.split(':')
+                values[struct[0]] = struct[1]
+        return values
+
+    def core_process(self, command, buffer_size):
+        """Encode and stream audio data through a generator"""
+        
+        __chunk = 0
+
+        try:
+            proc = subprocess.Popen(command,
+                    shell = True,
+                    bufsize = buffer_size,
+                    stdin = subprocess.PIPE,
+                    stdout = subprocess.PIPE,
+                    close_fds = True)
+        except:
+            raise VampProcessError('Command failure:', command, proc)
+            
+        # Core processing
+        while True:
+            __chunk = proc.stdout.read(buffer_size)
+            status = proc.poll()
+            if status != None and status != 0:
+                raise VampProcessError('Command failure:', command, proc)
+            if len(__chunk) == 0:
+                break
+            yield __chunk
+        
+
+class VampProcessError(TimeSideError):
+
+    def __init__(self, message, command, subprocess):
+        self.message = message
+        self.command = str(command)
+        self.subprocess = subprocess
+
+    def __str__(self):
+        if self.subprocess.stderr != None:
+            error = self.subprocess.stderr.read()
+        else:
+            error = ''
+        return "%s ; command: %s; error: %s" % (self.message,
+                                                self.command,
+                                                error)