Exemple #1
0
    def __inner_init_default__(self):
        if self.__initialized:
            return

        sourceEdt = _c.determineEdt(self.dataref)

        if not sourceEdt.isIntermediate():
            _essentia.VectorInput.__init__(self, self.dataref, str(sourceEdt))
            self.__initialized = True
            return

        if sourceEdt == _c.Edt.LIST_EMPTY or \
           sourceEdt == _c.Edt.LIST_INTEGER or \
           sourceEdt == _c.Edt.LIST_REAL or \
           sourceEdt == _c.Edt.LIST_MIXED:
            self.dataref = _c.convertData(self.dataref, _c.Edt.VECTOR_REAL)
            _essentia.VectorInput.__init__(self, self.dataref, _c.Edt.VECTOR_REAL)
            self.__initialized = True
            return

        if sourceEdt == _c.Edt.LIST_LIST_REAL or sourceEdt == _c.Edt.LIST_LIST_INTEGER:
            self.dataref = _c.convertData(self.dataref, _c.Edt.MATRIX_REAL)
            _essentia.VectorInput.__init__(self, self.dataref, _c.Edt.MATRIX_REAL)
            self.__initialized = True
            return

        raise TypeError('Unable to initialize VectorInput because it is '+\
                        'being connected to None or a Pool, and the '+\
                        'VectorInput\'s data consists of an unsupported Pool '+\
                        'type: '+str(sourceEdt))
    def __inner_init_default__(self):
        if self.__initialized:
            return

        sourceEdt = _c.determineEdt(self.dataref)

        if not sourceEdt.isIntermediate():
            _essentia.VectorInput.__init__(self, self.dataref, str(sourceEdt))
            self.__initialized = True
            return

        if sourceEdt == _c.Edt.LIST_EMPTY or \
           sourceEdt == _c.Edt.LIST_INTEGER or \
           sourceEdt == _c.Edt.LIST_REAL or \
           sourceEdt == _c.Edt.LIST_MIXED:
            self.dataref = _c.convertData(self.dataref, _c.Edt.VECTOR_REAL)
            _essentia.VectorInput.__init__(self, self.dataref,
                                           _c.Edt.VECTOR_REAL)
            self.__initialized = True
            return

        if sourceEdt == _c.Edt.LIST_LIST_REAL or sourceEdt == _c.Edt.LIST_LIST_INTEGER:
            self.dataref = _c.convertData(self.dataref, _c.Edt.MATRIX_REAL)
            _essentia.VectorInput.__init__(self, self.dataref,
                                           _c.Edt.MATRIX_REAL)
            self.__initialized = True
            return

        raise TypeError('Unable to initialize VectorInput because it is '+\
                        'being connected to None or a Pool, and the '+\
                        'VectorInput\'s data consists of an unsupported Pool '+\
                        'type: '+str(sourceEdt))
Exemple #3
0
        def compute(self, *args):
            inputNames = self.inputNames()

            if len(args) != len(inputNames):
                raise ValueError(name+'.compute requires '+str(len(inputNames))+' argument(s), '+str(len(args))+' given')

            # we have to make some exceptions for YamlOutput and PoolAggregator
            # because they expect cpp Pools
            if name in ('YamlOutput', 'PoolAggregator', 'SvmClassifier', 'PCA', 'GaiaTransform'):
                args = (args[0].cppPool,)

            # verify that all types match and do any necessary conversions
            result = []

            convertedArgs = []
            for i in range(len(inputNames)):
                goalType = _c.Edt(self.inputType(inputNames[i]))
                try:
                    convertedData = _c.convertData(args[i], goalType)
                except TypeError:
                    raise TypeError('Error cannot convert argument %s to %s' \
                          %(str(_c.determineEdt(args[i])), str(goalType)))

                convertedArgs.append(convertedData)

            results = self.__compute__(*convertedArgs)

            # we have to make an exceptional case for YamlInput, because we need
            # to wrap the Pool that it outputs w/ our python Pool from common.py
            if name in ('YamlInput', 'PoolAggregator', 'SvmClassifier', 'PCA', 'GaiaTransform', 'Extractor'):
                return _c.Pool(results)

            else:
                return results
Exemple #4
0
        def compute(self, *args):
            inputNames = self.inputNames()

            if len(args) != len(inputNames):
                raise ValueError(name+'.compute requires '+str(len(inputNames))+' argument(s), '+str(len(args))+' given')

            # we have to make some exceptions for YamlOutput and PoolAggregator
            # because they expect cpp Pools
            if name in ('YamlOutput', 'PoolAggregator', 'SvmClassifier', 'PCA', 'GaiaTransform'):
                args = (args[0].cppPool,)

            # verify that all types match and do any necessary conversions
            result = []

            convertedArgs = []
            for i in range(len(inputNames)):
                goalType = _c.Edt(self.inputType(inputNames[i]))
                try:
                    convertedData = _c.convertData(args[i], goalType)
                except TypeError:
                    raise TypeError('Error cannot convert argument %s to %s' \
                          %(str(_c.determineEdt(args[i])), str(goalType)))

                convertedArgs.append(convertedData)

            results = self.__compute__(*convertedArgs)

            # we have to make an exceptional case for YamlInput, because we need
            # to wrap the Pool that it outputs w/ our python Pool from common.py
            if name in ('YamlInput', 'PoolAggregator', 'SvmClassifier', 'PCA', 'GaiaTransform', 'Extractor'):
                return _c.Pool(results)

            else:
                return results
Exemple #5
0
        def configure(self, **kwargs):
            # verify that all types match and do any necessary conversions
            for name, val in kwargs.iteritems():
                goalType = self.paramType(name)
                try:
                    convertedVal = _c.convertData(val, goalType)
                except TypeError: # as e: # catching exception as sth is only
                                          #available as from python 2.6
                    raise TypeError('Error cannot convert parameter %s to %s'\
                                    %(str(_c.determineEdt(val)),str(goalType))) #\''+name+'\' parameter: '+str(e))

                kwargs[name] = convertedVal

            self.__configure__(**kwargs)
Exemple #6
0
        def compute(self, *args):
            inputNames = self.inputNames()

            if len(args) != len(inputNames):
                raise ValueError(name+'.compute requires '+str(len(inputNames))+' argument(s), '+str(len(args))+' given')

            # we have to make some exceptions for YamlOutput and PoolAggregator
            # because they expect cpp Pools
            if name in ('YamlOutput', 'PoolAggregator', 'SvmClassifier', 'PCA', 'GaiaTransform'):
                args = (args[0].cppPool,)

            # verify that all types match and do any necessary conversions
            result = []

            convertedArgs = []

            for i in range(len(inputNames)):
                arg = args[i]

                if type(args[i]).__module__ == 'numpy':
                    if not args[i].flags['C_CONTIGUOUS']:
                        arg = copy(args[i])

                goalType = _c.Edt(self.inputType(inputNames[i]))

                try:
                    convertedData = _c.convertData(arg, goalType)
                except TypeError:
                    raise TypeError('Error cannot convert argument %s to %s' \
                          %(str(_c.determineEdt(arg)), str(goalType)))

                convertedArgs.append(convertedData)

            results = self.__compute__(*convertedArgs)

            # we have to make an exceptional case for YamlInput, because we need
            # to wrap the Pool that it outputs w/ our python Pool from common.py
            if name in ('YamlInput', 'PoolAggregator', 'SvmClassifier', 'PCA', 'GaiaTransform', 'Extractor'):
                return _c.Pool(results)

            # MusicExtractor and FreesoundExtractor output two pools
            if name in ('MusicExtractor', 'FreesoundExtractor'):
                return (_c.Pool(results[0]), _c.Pool(results[1]))

            # In the case of MetadataReader, the 7th output is also a Pool
            if name in ('MetadataReader'):
                return results[:7] + (_c.Pool(results[7]),) + results[8:]

            else:
                return results
Exemple #7
0
        def configure(self, **kwargs):
            # verify that all types match and do any necessary conversions
            for name, val in kwargs.iteritems():
                goalType = self.paramType(name)
                try:
                    convertedVal = _c.convertData(val, goalType)
                except TypeError: # as e: # catching exception as sth is only
                                          #available as from python 2.6
                    raise TypeError('Error cannot convert parameter %s to %s'\
                                    %(str(_c.determineEdt(val)),str(goalType))) #\''+name+'\' parameter: '+str(e))

                kwargs[name] = convertedVal

            self.__configure__(**kwargs)
Exemple #8
0
def translate(composite_algo, output_filename, dot_graph=False):
    '''Takes in a class that is derived from essentia.streaming.CompositeBase and an output-filename
       and writes output-filename.h and output-filename.cpp versions of the given class.'''

    if not inspect.isclass(composite_algo):
        raise TypeError('"composite_algo" argument must be a class')

    if not streaming.CompositeBase in inspect.getmro(composite_algo):
        raise TypeError(
            '"composite_algo" argument must inherit from essentia.streaming.CompositeBase'
        )

    param_names, _, _, default_values = inspect.getargspec(
        composite_algo.__init__)
    param_names.remove('self')

    # these marker objects are used to track where config params travel in the network
    marker_objs = {}
    if not default_values and param_names:  # python vars have no type so we cannot know what type they are!!
        raise TypeError('"composite_algo" arguments must have default values')
    if param_names:
        for param_name, value in zip(param_names, default_values):
            marker_objs[param_name] = MarkerObject(value)

    ### Before we call their function we need to neuter all of the configure methods of each
    ### streaming algorithm so that our markers won't cause the configure method to vomit

    configure_log = {}

    def dummy_configure(self, **kwargs):
        lbl = 0
        algo_name = self.name() + '_' + str(lbl)

        # increment lbl to generate a unique name for inner algo
        lowered_algo_names = [name.lower() for name in configure_log.keys()]
        while algo_name.lower() in lowered_algo_names:
            algo_name = algo_name[:algo_name.index('_') + 1] + str(lbl)
            lbl += 1

        # algo_name is now unique

        configure_log[algo_name] = {}
        configure_log[algo_name]['instance'] = self
        configure_log[algo_name]['parameters'] = kwargs

        # We need to actually call the internal configure method because algorithms like silencerate
        # need to be configured so we can use its outputs. However we can't use our marker objects,
        # so we remove the marker objects that don't have a default value associated with them, and
        # for those that do have a default value, we use that value instead of the MarkerObject
        # itself
        kwargs_no_markers = dict(kwargs)

        for key, value in kwargs.iteritems():
            if value in marker_objs.values():
                if value.default_value == None:
                    del kwargs_no_markers[key]
                else:
                    kwargs_no_markers[key] = value.default_value

        self.real_configure(**kwargs_no_markers)

    # iterate over all streaming_algos
    streaming_algos = inspect.getmembers( streaming,
                                          lambda obj: inspect.isclass(obj) and \
                                                      _essentia.StreamingAlgorithm in inspect.getmro(obj) )
    streaming_algos = [member[1] for member in streaming_algos]
    for algo in streaming_algos:
        algo.real_configure = algo.configure
        algo.configure = dummy_configure

    ### Now generate an instance of their composite algorithm ###

    algo_inst = composite_algo(**marker_objs)

    # overwrite the dummy configure with the real configure method, so
    # translate can be called several times in the same file for a different
    # compositebase without entering in an infinite loop
    for algo in streaming_algos:
        algo.configure = algo.real_configure

    ### Do some checking on their network ###
    for algo in [logitem['instance'] for logitem in configure_log.values()]:
        if isinstance(algo, streaming.VectorInput):
            raise TypeError(
                'essentia.streaming.VectorInput algorithms are not allowed for translatable composite algorithms'
            )

        if isinstance(algo, streaming.AudioLoader) or \
           isinstance(algo, streaming.EasyLoader) or \
           isinstance(algo, streaming.MonoLoader) or \
           isinstance(algo, streaming.EqloudLoader):
            raise TypeError(
                'No type of AudioLoader is allowed for translatable composite algorithms'
            )

        if isinstance(algo, streaming.AudioWriter) or \
           isinstance(algo, streaming.MonoWriter):
            raise TypeError(
                'No type of AudioWriter is allowed for translatable composite algorithms'
            )

        if isinstance(algo, streaming.FileOutput):
            raise TypeError(
                'essentia.streaming.FileOutput algorithms are not allowed for translatable composite algorithms'
            )

    def sort_by_key(configure_log):
        # sort algorithms and conf values:
        sitems = configure_log.items()
        sitems.sort()
        sorted_algos = []
        sorted_params = []
        for k, v in sitems:
            sorted_params.append(v)
            sorted_algos.append(k)
        return sorted_algos, sorted_params

    sorted_algos, sorted_params = sort_by_key(configure_log)

    ### generate .h code ###

    h_code = '''// Generated automatically by essentia::translate

#ifndef STREAMING_''' + composite_algo.__name__.upper() + '''
#define STREAMING_''' + composite_algo.__name__.upper() + '''

#include "streamingalgorithmcomposite.h"

class ''' + composite_algo.__name__ + ''' : public essentia::streaming::AlgorithmComposite {
 protected:
'''

    for algo_name in sorted_algos:
        h_code += '  essentia::streaming::Algorithm* _' + algo_name.lower(
        ) + ';\n'

    h_code += '''
 public:
  ''' + composite_algo.__name__ + '''();

  ~''' + composite_algo.__name__ + '''() {
'''
    for algo_name in sorted_algos:
        h_code += '    delete _' + algo_name.lower() + ';\n'

    h_code += '''  }

  void declareParameters() {
'''
    if param_names:
        for param_name, default_value in zip(param_names, default_values):
            h_code += '    declareParameter("' + param_name + '", "", "", '

            if isinstance(default_value, basestring):
                h_code += '"' + default_value + '"'
            else:
                h_code += str(default_value)

            h_code += ');\n'

    h_code += '''  }

  void configure();
  void createInnerNetwork();
  void reset();

  static const char* name;
  static const char* version;
  static const char* description;
};
#endif
'''

    ### Generate .cpp code ###

    cpp_code = '''// Generated automatically by essentia::translate

#include "''' + output_filename + '''.h"
#include "algorithmfactory.h"
#include "taskqueue.h"

using namespace std;
using namespace essentia;
using namespace essentia::streaming;

const char* ''' + composite_algo.__name__ + '''::name = "''' + composite_algo.__name__ + '''";
const char* ''' + composite_algo.__name__ + '''::version = "1.0";
const char* ''' + composite_algo.__name__ + '''::description = DOC("");\n\n'''

    ################################
    # CONSTRUCTOR
    ################################
    cpp_code += composite_algo.__name__ + '''::''' + composite_algo.__name__ + '''(): '''
    for algo_name in sorted_algos:
        cpp_code += '_' + algo_name.lower() + '(0), '
    cpp_code = cpp_code[:-2] + ''' {
  setName("''' + composite_algo.__name__ + '''");
  declareParameters();
  AlgorithmFactory& factory = AlgorithmFactory::instance();\n\n'''

    # create inner algorithms
    for algo_name in sorted_algos:
        cpp_code += '  _' + algo_name.lower(
        ) + ' = factory.create("' + algo_name[:algo_name.rindex('_')] + '");\n'

    cpp_code += '}\n\n'

    ################################
    # INNER NETWORK
    ################################
    # declaration of inputs and output and connecting the network should not be
    # done in the constructor, as there are algos like silencerate which
    # inputs/outputs depend on the configuration parameters. Hence, it is safer to
    # do it in the configure() function

    cpp_code += 'void ' + composite_algo.__name__ + '::createInnerNetwork() {\n'

    # declare inputs
    for input_alias, connector in algo_inst.inputs.iteritems():
        input_owner_name = None
        input_name = None

        for algo_name, properties in zip(
                sorted_algos, sorted_params):  #configure_log.iteritems():
            if properties['instance'] == connector.input_algo:
                input_owner_name = algo_name
                input_name = connector.name
                break

        if not input_owner_name:
            raise RuntimeError('Could not determine owner of the \'' +
                               input_alias + '\' input')

        cpp_code += '  declareInput(_' + input_owner_name.lower(
        ) + '->input("' + input_name + '"), "' + input_alias + '", "");\n'

    cpp_code += '\n'

    # declare outputs
    aliases, connectors = sort_by_key(algo_inst.outputs)
    for output_alias, connector in zip(aliases, connectors):
        output_owner_name = None
        output_name = None

        for algo_name, properties in zip(
                sorted_algos, sorted_params):  #configure_log.iteritems():
            if properties['instance'] == connector.output_algo:
                output_owner_name = algo_name
                output_name = connector.name
                break

        if not output_owner_name:
            raise RuntimeError('Could not determine owner of the \'' +
                               output_alias + '\' output')

        cpp_code += '  declareOutput(_' + output_owner_name.lower(
        ) + '->output("' + output_name + '"), "' + output_alias + '", "");\n'

    cpp_code += '\n'

    # make connections
    for algo_name, properties in zip(
            sorted_algos, sorted_params):  #configure_log.iteritems():
        for left_connector, right_connectors in properties[
                'instance'].connections.iteritems():
            for right_connector in right_connectors:
                if isinstance(right_connector, streaming._StreamConnector):
                    cpp_code += '  connect( _'+\
                                inner_algo_name(left_connector.output_algo, configure_log).lower() + \
                                '->output("'+left_connector.name+'"), _' + \
                                inner_algo_name(right_connector.input_algo, configure_log).lower() + \
                                '->input("'+right_connector.name+'") );\n'

                elif isinstance(right_connector, types.NoneType):
                    cpp_code += '  connect( _'+\
                                inner_algo_name(left_connector.output_algo, configure_log).lower() + \
                                '->output("'+left_connector.name+'"), NOWHERE );\n'

    cpp_code = cpp_code[:-1]
    cpp_code += '''
}\n\n'''

    ################################
    # CONFIGURE
    ################################

    cpp_code += 'void ' + composite_algo.__name__ + '::configure() {\n'

    # configure method

    # create local variable for every composite parameter
    for composite_param_name in param_names:
        param_edt = find_edt(composite_param_name,
                             marker_objs[composite_param_name], configure_log)
        cpp_code += '  '+edt_cpp_code[param_edt]+' '+composite_param_name + \
                    ' = parameter("'+composite_param_name+'").to' + \
                    edt_parameter_code[param_edt]+'();\n'

    cpp_code += '\n'

    # configure inner algorithms
    for algo_name, properties in zip(
            sorted_algos, sorted_params):  #configure_log.iteritems():
        # skip if inner algorithm wasn't configured explicitly
        if not properties['parameters']: continue

        for param_name, value in properties['parameters'].iteritems():
            type = common.determineEdt(value)
            if 'LIST' in str(type) or 'VECTOR' in str(type):
                if type in [common.Edt.VECTOR_STRING]:
                    cpp_code += '  const char* ' + param_name + '[]  = {'
                    for s in value:
                        cpp_code += '\"' + s + '\"' + ','
                elif type in [common.Edt.VECTOR_REAL, common.Edt.LIST_REAL]:
                    cpp_code += '  Real ' + param_name + '[] = {'
                    for f in value:
                        cpp_code += str(f) + ','
                elif type in [common.Edt.VECTOR_INT, common.Edt.LIST_INT]:
                    cpp_code += '  int' + param_name + '[] = {'
                    for i in value:
                        cpp_code += str(i) + ','
                cpp_code = cpp_code[:-1] + '};\n'

        cpp_code += '  _' + algo_name.lower() + '->configure('

        for param_name, value in properties['parameters'].iteritems():
            if isinstance(value, MarkerObject):
                # figure out which composite param it is
                composite_param_name = None
                for marker_name, marker_obj in marker_objs.iteritems():
                    if marker_obj == value:
                        composite_param_name = marker_name
                        break

                if not composite_param_name:
                    raise RuntimeError(
                        'Could not determine which composite parameter to use to configure inner algorithm \''
                        + algo_name + '\'s parameter \'' + param_name + '\'')

                cpp_code += '"' + param_name + '", ' + composite_param_name + ', '

            else:
                type = common.determineEdt(value)
                if 'LIST' in str(type) or 'VECTOR' in str(type):
                    if type in [common.Edt.VECTOR_STRING]:
                        cpp_code += '"' + param_name + '", ' + 'arrayToVector<string>(' + param_name + ')  '
                    elif type in [
                            common.Edt.VECTOR_REAL, common.Edt.LIST_REAL
                    ]:
                        cpp_code += '"' + param_name + '", ' + 'arrayToVector<Real>(' + param_name + ')  '
                    elif type in [common.Edt.VECTOR_INT, common.Edt.LIST_INT]:
                        cpp_code += '"' + param_name + '", ' + 'arrayToVector<int>(' + param_name + ')  '
                elif isinstance(value, basestring):
                    cpp_code += '"' + param_name + '", "' + value + '", '
                elif isinstance(value, bool):
                    if value: cpp_code += '"' + param_name + '", true, '
                    else: cpp_code += '"' + param_name + '", false, '
                else:
                    cpp_code += '"' + param_name + '", ' + str(value) + ', '

        cpp_code = cpp_code[:-2] + ');\n'
    cpp_code += '  createInnerNetwork();\n}\n\n'

    ################################
    # RESET
    ################################

    cpp_code += 'void ' + composite_algo.__name__ + '::reset() {\n'
    for algo_name in sorted_algos:
        cpp_code += '  _' + algo_name.lower() + '->reset();\n'
    cpp_code += '}\n\n'

    ################################
    # DESTRUCTOR
    ################################
    # see h_code. Each algo from the composite is deleted separately instead of
    # calling deleteNetwork
    #    cpp_code += composite_algo.__name__+'''::~'''+composite_algo.__name__+'''() {
    #  deleteNetwork(_''' + input_owner_name.lower() + ''');
    #}'''
    #    cpp_code +='\n'
    #
    ################################
    # end of cpp code
    ################################

    if dot_graph:
        ### generate .dot code ###
        dot_code = 'digraph ' + output_filename + ' {\n'
        dot_code += '    rankdir=LR\n'  # if instead of top-down left-right is prefered
        # general formatting options:
        dot_code += '    node [color=black, fontname=Verdana, weight=1, fontsize=8, shape=Mrecord]\n'
        dot_code += '    edge [color=black, style=solid, weight=1, arrowhead="dotnormal", arrowtail="dot", arrowsize=1, fontsize=6]\n'

        # for each input generate nodes
        for name in algo_inst.inputs.keys():
            dot_code += '    ' + name + ' [label="' + name + '"];\n'

        dot_code += generate_dot_cluster(configure_log,
                                         composite_algo.__name__, algo_inst)

        # for each output generate nodes
        for name in algo_inst.outputs.keys():
            dot_code += '    ' + name + ' [label="' + name + '"];\n'

        dot_code += '}'

    ### Write files ###

    f = open(output_filename + '.h', 'w')
    f.write(h_code)
    f.close()

    f = open(output_filename + '.cpp', 'w')
    f.write(cpp_code)
    f.close()

    if dot_graph:
        f = open(output_filename + '.dot', 'w')
        f.write(dot_code)
        f.close()
Exemple #9
0
def translate(composite_algo, output_filename, dot_graph=False):
    '''Takes in a class that is derived from essentia.streaming.CompositeBase and an output-filename
       and writes output-filename.h and output-filename.cpp versions of the given class.'''

    if not inspect.isclass(composite_algo):
        raise TypeError('"composite_algo" argument must be a class')

    if not streaming.CompositeBase in inspect.getmro(composite_algo):
        raise TypeError('"composite_algo" argument must inherit from essentia.streaming.CompositeBase')

    param_names, _, _, default_values = inspect.getargspec(composite_algo.__init__)
    param_names.remove('self')

    # these marker objects are used to track where config params travel in the network
    marker_objs = {}
    if not default_values and param_names: # python vars have no type so we cannot know what type they are!!
        raise TypeError('"composite_algo" arguments must have default values')
    if param_names:
        for param_name, value in zip(param_names, default_values):
            marker_objs[param_name] = MarkerObject(value)

    ### Before we call their function we need to neuter all of the configure methods of each
    ### streaming algorithm so that our markers won't cause the configure method to vomit

    configure_log = {}

    def dummy_configure(self, **kwargs):
        lbl = 0
        algo_name = self.name()+'_'+str(lbl)

        # increment lbl to generate a unique name for inner algo
        lowered_algo_names = [name.lower() for name in configure_log.keys()]
        while algo_name.lower() in lowered_algo_names:
            algo_name = algo_name[:algo_name.index('_')+1] + str(lbl)
            lbl +=1

        # algo_name is now unique

        configure_log[algo_name] = {}
        configure_log[algo_name]['instance'] = self
        configure_log[algo_name]['parameters'] = kwargs

        # We need to actually call the internal configure method because algorithms like silencerate
        # need to be configured so we can use its outputs. However we can't use our marker objects,
        # so we remove the marker objects that don't have a default value associated with them, and
        # for those that do have a default value, we use that value instead of the MarkerObject
        # itself
        kwargs_no_markers = dict(kwargs)

        for key, value in kwargs.iteritems():
            if value in marker_objs.values():
                if value.default_value == None:
                    del kwargs_no_markers[key]
                else:
                    kwargs_no_markers[key] = value.default_value

        self.real_configure(**kwargs_no_markers)

    # iterate over all streaming_algos
    streaming_algos = inspect.getmembers( streaming,
                                          lambda obj: inspect.isclass(obj) and \
                                                      _essentia.StreamingAlgorithm in inspect.getmro(obj) )
    streaming_algos = [member[1] for member in streaming_algos]
    for algo in streaming_algos:
        algo.real_configure = algo.configure
        algo.configure = dummy_configure

    ### Now generate an instance of their composite algorithm ###

    algo_inst = composite_algo(**marker_objs)

    # overwrite the dummy configure with the real configure method, so
    # translate can be called several times in the same file for a different
    # compositebase without entering in an infinite loop
    for algo in streaming_algos:
        algo.configure = algo.real_configure

    ### Do some checking on their network ###
    for algo in [ logitem['instance'] for logitem in configure_log.values() ]:
        if isinstance(algo, streaming.VectorInput):
            raise TypeError('essentia.streaming.VectorInput algorithms are not allowed for translatable composite algorithms')

        if isinstance(algo, streaming.AudioLoader) or \
           isinstance(algo, streaming.EasyLoader) or \
           isinstance(algo, streaming.MonoLoader) or \
           isinstance(algo, streaming.EqloudLoader):
            raise TypeError('No type of AudioLoader is allowed for translatable composite algorithms')

        if isinstance(algo, streaming.AudioWriter) or \
           isinstance(algo, streaming.MonoWriter):
            raise TypeError('No type of AudioWriter is allowed for translatable composite algorithms')

        if isinstance(algo, streaming.FileOutput):
            raise TypeError('essentia.streaming.FileOutput algorithms are not allowed for translatable composite algorithms')


    def sort_by_key(configure_log):
        # sort algorithms and conf values:
        sitems = configure_log.items()
        sitems.sort()
        sorted_algos = []
        sorted_params= []
        for k,v in sitems:
            sorted_params.append(v)
            sorted_algos.append(k)
        return sorted_algos, sorted_params

    sorted_algos, sorted_params = sort_by_key(configure_log)

    ### generate .h code ###

    h_code = '''// Generated automatically by essentia::translate

#ifndef STREAMING_''' + composite_algo.__name__.upper() + '''
#define STREAMING_''' + composite_algo.__name__.upper()+ '''

#include "streamingalgorithmcomposite.h"

class '''+composite_algo.__name__+''' : public essentia::streaming::AlgorithmComposite {
 protected:
'''

    for algo_name in sorted_algos:
        h_code += '  essentia::streaming::Algorithm* _'+algo_name.lower()+';\n'

    h_code += '''
 public:
  '''+composite_algo.__name__+'''();

  ~'''+composite_algo.__name__+'''() {
'''
    for algo_name in sorted_algos:
        h_code += '    delete _'+algo_name.lower()+';\n'

    h_code += '''  }

  void declareParameters() {
'''
    if param_names:
        for param_name, default_value in zip(param_names, default_values):
            h_code += '    declareParameter("'+param_name+'", "", "", '

            if isinstance(default_value, basestring): h_code += '"'+default_value+'"'
            else:                                     h_code += str(default_value)

            h_code += ');\n'

    h_code += '''  }

  void configure();
  void createInnerNetwork();
  void reset();

  static const char* name;
  static const char* version;
  static const char* description;
};
#endif
'''

    ### Generate .cpp code ###

    cpp_code = '''// Generated automatically by essentia::translate

#include "'''+output_filename+'''.h"
#include "algorithmfactory.h"
#include "taskqueue.h"

using namespace std;
using namespace essentia;
using namespace essentia::streaming;

const char* '''+composite_algo.__name__+'''::name = "'''+composite_algo.__name__+'''";
const char* '''+composite_algo.__name__+'''::version = "1.0";
const char* '''+composite_algo.__name__+'''::description = DOC("");\n\n'''


################################
# CONSTRUCTOR
################################
    cpp_code += composite_algo.__name__+'''::'''+composite_algo.__name__+'''(): '''
    for algo_name in sorted_algos: cpp_code += '_' + algo_name.lower() + '(0), '
    cpp_code = cpp_code[:-2] + ''' {
  setName("''' + composite_algo.__name__ + '''");
  declareParameters();
  AlgorithmFactory& factory = AlgorithmFactory::instance();\n\n'''

    # create inner algorithms
    for algo_name in sorted_algos:
        cpp_code += '  _'+algo_name.lower()+' = factory.create("'+algo_name[:algo_name.rindex('_')]+'");\n'

    cpp_code+='}\n\n'

################################
# INNER NETWORK
################################
# declaration of inputs and output and connecting the network should not be
# done in the constructor, as there are algos like silencerate which
# inputs/outputs depend on the configuration parameters. Hence, it is safer to
# do it in the configure() function

    cpp_code += 'void ' + composite_algo.__name__ + '::createInnerNetwork() {\n'

    # declare inputs
    for input_alias, connector in algo_inst.inputs.iteritems():
        input_owner_name = None
        input_name = None

        for algo_name, properties in zip(sorted_algos, sorted_params): #configure_log.iteritems():
            if properties['instance'] == connector.input_algo:
                input_owner_name = algo_name
                input_name = connector.name
                break

        if not input_owner_name:
            raise RuntimeError('Could not determine owner of the \''+input_alias+'\' input')

        cpp_code += '  declareInput(_'+input_owner_name.lower()+'->input("'+input_name+'"), "'+input_alias+'", "");\n'

    cpp_code += '\n'

    # declare outputs
    aliases, connectors = sort_by_key(algo_inst.outputs)
    for output_alias, connector in zip(aliases, connectors):
        output_owner_name = None
        output_name = None

        for algo_name, properties in zip(sorted_algos, sorted_params): #configure_log.iteritems():
            if properties['instance'] == connector.output_algo:
                output_owner_name = algo_name
                output_name = connector.name
                break

        if not output_owner_name:
            raise RuntimeError('Could not determine owner of the \''+output_alias+'\' output')

        cpp_code += '  declareOutput(_'+output_owner_name.lower()+'->output("'+output_name+'"), "'+output_alias+'", "");\n'

    cpp_code += '\n'

    # make connections
    for algo_name, properties in zip(sorted_algos, sorted_params): #configure_log.iteritems():
        for left_connector, right_connectors in properties['instance'].connections.iteritems():
            for right_connector in right_connectors:
                if isinstance(right_connector, streaming._StreamConnector):
                    cpp_code += '  connect( _'+\
                                inner_algo_name(left_connector.output_algo, configure_log).lower() + \
                                '->output("'+left_connector.name+'"), _' + \
                                inner_algo_name(right_connector.input_algo, configure_log).lower() + \
                                '->input("'+right_connector.name+'") );\n'

                elif isinstance(right_connector, types.NoneType):
                    cpp_code += '  connect( _'+\
                                inner_algo_name(left_connector.output_algo, configure_log).lower() + \
                                '->output("'+left_connector.name+'"), NOWHERE );\n'

    cpp_code = cpp_code[:-1]
    cpp_code += '''
}\n\n'''


################################
# CONFIGURE
################################

    cpp_code += 'void '+composite_algo.__name__+'::configure() {\n'

    # configure method

    # create local variable for every composite parameter
    for composite_param_name in param_names:
        param_edt = find_edt(composite_param_name, marker_objs[composite_param_name], configure_log)
        cpp_code += '  '+edt_cpp_code[param_edt]+' '+composite_param_name + \
                    ' = parameter("'+composite_param_name+'").to' + \
                    edt_parameter_code[param_edt]+'();\n'

    cpp_code += '\n'

    # configure inner algorithms
    for algo_name, properties in zip(sorted_algos, sorted_params): #configure_log.iteritems():
        # skip if inner algorithm wasn't configured explicitly
        if not properties['parameters']: continue

        for param_name, value in properties['parameters'].iteritems():
            type = common.determineEdt(value)
            if 'LIST' in str(type) or 'VECTOR' in str(type):
                if type in [common.Edt.VECTOR_STRING]:
                    cpp_code += '  const char* ' + param_name + '[]  = {'
                    for s in value: cpp_code += '\"' + s + '\"' + ','
                elif type in[common.Edt.VECTOR_REAL, common.Edt.LIST_REAL]:
                    cpp_code += '  Real ' + param_name + '[] = {'
                    for f in value: cpp_code +=  str(f) + ','
                elif type in [common.Edt.VECTOR_INT, common.Edt.LIST_INT]:
                    cpp_code += '  int' + param_name + '[] = {'
                    for i in value: cpp_code +=  str(i) + ','
                cpp_code = cpp_code[:-1]+'};\n'

        cpp_code += '  _'+algo_name.lower()+'->configure('

        for param_name, value in properties['parameters'].iteritems():
            if isinstance(value, MarkerObject):
                # figure out which composite param it is
                composite_param_name = None
                for marker_name, marker_obj in marker_objs.iteritems():
                    if marker_obj == value:
                        composite_param_name = marker_name
                        break

                if not composite_param_name:
                    raise RuntimeError('Could not determine which composite parameter to use to configure inner algorithm \''+algo_name+'\'s parameter \''+param_name+'\'')

                cpp_code += '"'+param_name+'", '+composite_param_name+', '

            else:
                type = common.determineEdt(value)
                if 'LIST' in str(type) or 'VECTOR' in str(type):
                    if type in [common.Edt.VECTOR_STRING]:
                        cpp_code += '"'+param_name+'", '+'arrayToVector<string>(' + param_name + ')  '
                    elif type in[common.Edt.VECTOR_REAL, common.Edt.LIST_REAL]:
                        cpp_code += '"'+param_name+'", '+'arrayToVector<Real>(' + param_name + ')  '
                    elif type in [common.Edt.VECTOR_INT, common.Edt.LIST_INT]:
                        cpp_code += '"'+param_name+'", '+'arrayToVector<int>(' + param_name + ')  '
                elif isinstance(value, basestring):
                    cpp_code += '"'+param_name+'", "'+value+'", '
                elif isinstance(value, bool):
                    if value: cpp_code += '"'+param_name+'", true, '
                    else:     cpp_code += '"'+param_name+'", false, '
                else:
                    cpp_code += '"'+param_name+'", '+str(value)+', '

        cpp_code = cpp_code[:-2] + ');\n'
    cpp_code += '  createInnerNetwork();\n}\n\n'

################################
# RESET
################################

    cpp_code += 'void '+composite_algo.__name__+'::reset() {\n'
    for algo_name in sorted_algos:
        cpp_code += '  _' + algo_name.lower() + '->reset();\n'
    cpp_code += '}\n\n'

################################
# DESTRUCTOR
################################
# see h_code. Each algo from the composite is deleted separately instead of
# calling deleteNetwork
#    cpp_code += composite_algo.__name__+'''::~'''+composite_algo.__name__+'''() {
#  deleteNetwork(_''' + input_owner_name.lower() + ''');
#}'''
#    cpp_code +='\n'
#
################################
# end of cpp code
################################

    if dot_graph:
        ### generate .dot code ###
        dot_code  = 'digraph ' + output_filename +' {\n'
        dot_code += '    rankdir=LR\n' # if instead of top-down left-right is prefered
        # general formatting options:
        dot_code += '    node [color=black, fontname=Verdana, weight=1, fontsize=8, shape=Mrecord]\n'
        dot_code += '    edge [color=black, style=solid, weight=1, arrowhead="dotnormal", arrowtail="dot", arrowsize=1, fontsize=6]\n'

        # for each input generate nodes
        for name in algo_inst.inputs.keys():
            dot_code += '    '+name+' [label="'+name+'"];\n'

        dot_code += generate_dot_cluster(configure_log, composite_algo.__name__, algo_inst)

        # for each output generate nodes
        for name in algo_inst.outputs.keys():
            dot_code += '    '+name+' [label="'+name+'"];\n'

        dot_code += '}'

    ### Write files ###

    f = open(output_filename+'.h', 'w')
    f.write(h_code)
    f.close()

    f = open(output_filename+'.cpp', 'w')
    f.write(cpp_code)
    f.close()

    if dot_graph:
        f = open(output_filename+'.dot', 'w')
        f.write(dot_code)
        f.close()