From 614c5a228a8449d679f49189086069531dabce8f Mon Sep 17 00:00:00 2001 From: Guillaume Pellerin Date: Mon, 15 Dec 2014 00:28:04 +0100 Subject: [PATCH] Add EchoNest identifier based on libcodegen and echoprint --- timeside/__init__.py | 1 + .../analyzer/externals/echonest_identifier.py | 112 ++++++++++++++++++ timeside/tools/package.py | 26 +++- 3 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 timeside/analyzer/externals/echonest_identifier.py diff --git a/timeside/__init__.py b/timeside/__init__.py index c8a813c..ca1d6a3 100644 --- a/timeside/__init__.py +++ b/timeside/__init__.py @@ -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 index 0000000..c3da4cb --- /dev/null +++ b/timeside/analyzer/externals/echonest_identifier.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2014 Guillaume Pellerin + +# This file is part of TimeSide. + +# TimeSide is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. + +# TimeSide is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with TimeSide. If not, see . + +# Author: Thomas Fillon + +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([]) diff --git a/timeside/tools/package.py b/timeside/tools/package.py index a25a8b5..d73dc79 100644 --- a/timeside/tools/package.py +++ b/timeside/tools/package.py @@ -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 + -- 2.39.5