]> git.parisson.com Git - timeside.git/commitdiff
Add EchoNest identifier based on libcodegen and echoprint echoprint
authorGuillaume Pellerin <yomguy@parisson.com>
Sun, 14 Dec 2014 23:28:04 +0000 (00:28 +0100)
committerGuillaume Pellerin <yomguy@parisson.com>
Sun, 14 Dec 2014 23:28:04 +0000 (00:28 +0100)
timeside/__init__.py
timeside/analyzer/externals/echonest_identifier.py [new file with mode: 0644]
timeside/tools/package.py

index c8a813c1e64846cc9f29816facc677df906121c7..ca1d6a38cc18caf8b4a70c8b55a62c27a7f40e13 100644 (file)
@@ -33,6 +33,7 @@ from .tools import package as ts_package
 _WITH_AUBIO = ts_package.check_aubio()
 _WITH_YAAFE = ts_package.check_yaafe()
 _WITH_VAMP = ts_package.check_vamp()
+_WITH_ECHOPRINT = ts_package.check_echoprint()
 
 
 _packages_with_processors = ['decoder', 'analyzer', 'encoder', 'grapher', 'fx']
diff --git a/timeside/analyzer/externals/echonest_identifier.py b/timeside/analyzer/externals/echonest_identifier.py
new file mode 100644 (file)
index 0000000..c3da4cb
--- /dev/null
@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2014 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: Thomas Fillon <thomas@parisson.com>
+
+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
+import numpy as np
+import echoprint
+import requests
+import struct
+import os
+import json
+from threading import Thread
+
+
+class EchoNestIdentifier(Analyzer):
+    """EchoNest identifier based on libcodegen and echoprint
+
+    Example:
+
+        file_decoder = get_processor('file_decoder')
+        echo_analyzer = get_processor('echonest_identifier')
+        decoder = file_decoder(uri=uri, start=0, duration=20)
+        analyzer = echo_analyzer(start=0)
+        decoder.output_samplerate = 11025
+        pipe = (decoder | analyzer)
+        pipe.run()
+        songs = analyzer.metadata['songs']
+        if songs:
+            print songs[0]['artist_name'] + ' - ' + songs[0]['title']
+        else:
+            print 'Unknown'
+
+    """
+
+    implements(IAnalyzer)
+
+    needs = ['https://github.com/yomguy/python-echoprint',]
+    api_url = 'http://developer.echonest.com/api/v4/song/identify'
+    api_key = '6O3QX1BEQVY0JDDU5'
+    proxy = None
+
+    def __init__(self, api_key=None, start=-1):
+        super(EchoNestIdentifier, self).__init__()
+
+        if api_key:
+            self.api_key = api_key
+        self.start = start
+        if os.environ.has_key('HTTP_PROXY'):
+            self.proxy = os.environ['HTTP_PROXY']
+
+        self.metadata = None
+        self.samples = np.array([])
+
+    @staticmethod
+    @interfacedoc
+    def id():
+        return "echonest_identifier"
+
+    @staticmethod
+    @interfacedoc
+    def name():
+        return "EchoNest Identifier"
+
+    @staticmethod
+    @interfacedoc
+    def unit():
+        return ""
+
+    @downmix_to_mono
+    def process(self, frames, eod=False):
+        if frames.size:
+            self.samples = np.append(self.samples, frames)
+        return frames, eod
+
+    def post_process(self):
+        if np.count_nonzero(self.samples):
+            self.samples = self.samples[5*11025:]
+            print self.samples
+            if self.start == -1:
+                data = echoprint.codegen(self.samples.astype(np.float))
+            else:
+                data = echoprint.codegen(self.samples.astype(np.float), int(self.start))
+
+            r = requests.post(self.api_url,
+                data={'query': json.dumps(data), 'api_key': self.api_key,
+                      'version': data['version']},
+                headers={'content-type': 'application/x-www-form-urlencoded'},
+                proxies={'http': self.proxy}
+            )
+            self.metadata = r.json()['response']
+
+        self.samples = np.array([])
index a25a8b51288ee5b5754515ec1609d8dec2c3b163..d73dc79b93db85d9b17fe415546f5e1702b904b4 100644 (file)
@@ -48,7 +48,7 @@ def import_module_with_exceptions(name, package=None):
     """Wrapper around importlib.import_module to import TimeSide subpackage
     and ignoring ImportError if Aubio, Yaafe and Vamp Host are not available"""
 
-    from timeside import _WITH_AUBIO, _WITH_YAAFE, _WITH_VAMP
+    from timeside import _WITH_AUBIO, _WITH_YAAFE, _WITH_VAMP, _WITH_ECHOPRINT
 
     if name.count('.server.'):
         # TODO:
@@ -70,6 +70,9 @@ def import_module_with_exceptions(name, package=None):
         elif str(e).count('aubio') and not _WITH_AUBIO:
             # Ignore Aubio ImportError
             return
+        elif str(e).count('echoprint') and not _WITH_ECHOPRINT:
+            # Ignore EchoPrint ImportError
+            return
         elif str(e).count('DJANGO_SETTINGS_MODULE'):
             # Ignore module requiring DJANGO_SETTINGS_MODULE in environnement
             return
@@ -85,7 +88,7 @@ def check_aubio():
     try:
         import aubio
     except ImportError:
-        warnings.warn('Aubio librairy is not available', ImportWarning,
+        warnings.warn('Aubio library is not available', ImportWarning,
                       stacklevel=2)
         _WITH_AUBIO = False
     else:
@@ -100,7 +103,7 @@ def check_yaafe():
     try:
         import yaafelib
     except ImportError:
-        warnings.warn('Yaafe librairy is not available', ImportWarning,
+        warnings.warn('Yaafe library is not available', ImportWarning,
                       stacklevel=2)
         _WITH_YAAFE = False
     else:
@@ -123,3 +126,20 @@ def check_vamp():
         del vamp_plugin
 
     return _WITH_VAMP
+
+
+def check_echoprint():
+    "Check EchoPrint availability"
+
+    try:
+        import echoprint
+    except ImportError:
+        warnings.warn('EchoPrint library is not available', ImportWarning,
+                      stacklevel=2)
+        _WITH_ECHOPRINT = False
+    else:
+        _WITH_ECHOPRINT = True
+        del echoprint
+
+    return _WITH_ECHOPRINT
+