def value_integer(self, subject, subject_name): value = 0 if is_integer(lilv.lilv_node_as_string(subject)): value = lilv.lilv_node_as_int(subject) else: value = lilv.lilv_node_as_float(subject) if fmod(value, 1.0) == 0.0: self.warnings.append("port '%s' has integer property but %s value is float" % (self.portname, subject_name)) else: self.register_error("has integer property but %s value has non-zero decimals", subject_name) value = int(value) return value
def fill_port_info(port): # base data portname = lilv.lilv_node_as_string(port.get_name()) or "" if not portname: portname = "_%i" % index errors.append("port with index %i has no name" % index) portsymbol = lilv.lilv_node_as_string(port.get_symbol()) or "" if not portsymbol: portsymbol = "_%i" % index errors.append("port with index %i has no symbol" % index) # check for duplicate names if portname in portsymbols: warnings.append("port name '%s' is not unique" % portname) else: portnames.append(portname) # check for duplicate symbols if portsymbol in portsymbols: errors.append("port symbol '%s' is not unique" % portsymbol) else: portsymbols.append(portsymbol) # short name psname = lilv.lilv_nodes_get_first(port.get_value(ns_lv2core.shortname.me)) if psname is not None: psname = lilv.lilv_node_as_string(psname) or "" if not psname: psname = get_short_port_name(portname) warnings.append("port '%s' has no short name" % portname) elif len(psname) > 16: psname = psname[:16] errors.append("port '%s' short name has more than 16 characters" % portname) # port types types = [typ.rsplit("#",1)[-1].replace("Port","",1) for typ in get_port_data(port, ns_rdf.type_)] if "Atom" in types \ and port.supports_event(ns_midi.MidiEvent.me) \ and lilv.Nodes(port.get_value(ns_atom.bufferType.me)).get_first() == ns_atom.Sequence: types.append("MIDI") # port properties properties = [typ.rsplit("#",1)[-1] for typ in get_port_data(port, ns_lv2core.portProperty)] # data ranges = {} scalepoints = [] # unit block ulabel = "" urender = "" usymbol = "" # control and cv must contain ranges, might contain scale points if "Control" in types or "CV" in types: isInteger = "integer" in properties if isInteger and "CV" in types: errors.append("port '%s' has integer property and CV type" % portname) xdefault = lilv.lilv_nodes_get_first(port.get_value(ns_lv2core.default.me)) xminimum = lilv.lilv_nodes_get_first(port.get_value(ns_lv2core.minimum.me)) xmaximum = lilv.lilv_nodes_get_first(port.get_value(ns_lv2core.maximum.me)) if xminimum is not None and xmaximum is not None: if isInteger: if is_integer(lilv.lilv_node_as_string(xminimum)): ranges['minimum'] = lilv.lilv_node_as_int(xminimum) else: ranges['minimum'] = lilv.lilv_node_as_float(xminimum) if fmod(ranges['minimum'], 1.0) == 0.0: warnings.append("port '%s' has integer property but minimum value is float" % portname) else: errors.append("port '%s' has integer property but minimum value has non-zero decimals" % portname) ranges['minimum'] = int(ranges['minimum']) if is_integer(lilv.lilv_node_as_string(xmaximum)): ranges['maximum'] = lilv.lilv_node_as_int(xmaximum) else: ranges['maximum'] = lilv.lilv_node_as_float(xmaximum) if fmod(ranges['maximum'], 1.0) == 0.0: warnings.append("port '%s' has integer property but maximum value is float" % portname) else: errors.append("port '%s' has integer property but maximum value has non-zero decimals" % portname) ranges['maximum'] = int(ranges['maximum']) else: ranges['minimum'] = lilv.lilv_node_as_float(xminimum) ranges['maximum'] = lilv.lilv_node_as_float(xmaximum) if is_integer(lilv.lilv_node_as_string(xminimum)): warnings.append("port '%s' minimum value is an integer" % portname) if is_integer(lilv.lilv_node_as_string(xmaximum)): warnings.append("port '%s' maximum value is an integer" % portname) if ranges['minimum'] >= ranges['maximum']: ranges['maximum'] = ranges['minimum'] + (1 if isInteger else 0.1) errors.append("port '%s' minimum value is equal or higher than its maximum" % portname) if xdefault is not None: if isInteger: if is_integer(lilv.lilv_node_as_string(xdefault)): ranges['default'] = lilv.lilv_node_as_int(xdefault) else: ranges['default'] = lilv.lilv_node_as_float(xdefault) if fmod(ranges['default'], 1.0) == 0.0: warnings.append("port '%s' has integer property but default value is float" % portname) else: errors.append("port '%s' has integer property but default value has non-zero decimals" % portname) ranges['default'] = int(ranges['default']) else: ranges['default'] = lilv.lilv_node_as_float(xdefault) if is_integer(lilv.lilv_node_as_string(xdefault)): warnings.append("port '%s' default value is an integer" % portname) testmin = ranges['minimum'] testmax = ranges['maximum'] if "sampleRate" in properties: testmin *= 48000 testmax *= 48000 if not (testmin <= ranges['default'] <= testmax): ranges['default'] = ranges['minimum'] errors.append("port '%s' default value is out of bounds" % portname) else: ranges['default'] = ranges['minimum'] if "Input" in types: errors.append("port '%s' is missing default value" % portname) else: if isInteger: ranges['minimum'] = 0 ranges['maximum'] = 1 ranges['default'] = 0 else: ranges['minimum'] = -1.0 if "CV" in types else 0.0 ranges['maximum'] = 1.0 ranges['default'] = 0.0 if "CV" not in types: errors.append("port '%s' is missing value ranges" % portname) nodes = port.get_scale_points() if nodes is not None: it = lilv.lilv_scale_points_begin(nodes) while not lilv.lilv_scale_points_is_end(nodes, it): sp = lilv.lilv_scale_points_get(nodes, it) it = lilv.lilv_scale_points_next(nodes, it) if sp is None: continue label = lilv.lilv_scale_point_get_label(sp) value = lilv.lilv_scale_point_get_value(sp) if label is None: errors.append("a port scalepoint is missing its label") continue label = lilv.lilv_node_as_string(label) or "" if not label: errors.append("a port scalepoint is missing its label") continue if value is None: errors.append("port scalepoint '%s' is missing its value" % label) continue if isInteger: if is_integer(lilv.lilv_node_as_string(value)): value = lilv.lilv_node_as_int(value) else: value = lilv.lilv_node_as_float(value) if fmod(value, 1.0) == 0.0: warnings.append("port '%s' has integer property but scalepoint '%s' value is float" % (portname, label)) else: errors.append("port '%s' has integer property but scalepoint '%s' value has non-zero decimals" % (portname, label)) value = int(value) else: if is_integer(lilv.lilv_node_as_string(value)): warnings.append("port '%s' scalepoint '%s' value is an integer" % (portname, label)) value = lilv.lilv_node_as_float(value) if ranges['minimum'] <= value <= ranges['maximum']: scalepoints.append({'label': label, 'value': value}) else: errors.append(("port scalepoint '%s' has an out-of-bounds value:\n" % label) + ("%d < %d < %d" if isInteger else "%f < %f < %f") % (ranges['minimum'], value, ranges['maximum'])) if "enumeration" in properties and len(scalepoints) <= 1: errors.append("port '%s' wants to use enumeration but doesn't have enough values" % portname) properties.remove("enumeration") # control ports might contain unit if "Control" in types: # unit uunit = lilv.lilv_nodes_get_first(port.get_value(ns_units.unit.me)) if uunit is not None: uuri = lilv.lilv_node_as_uri(uunit) # using pre-existing lv2 unit if uuri is not None and uuri.startswith("http://lv2plug.in/ns/"): uuri = uuri.replace("http://lv2plug.in/ns/extensions/units#","",1) alnum = uuri.isalnum() if not alnum: errors.append("port '%s' has wrong lv2 unit uri" % portname) uuri = uuri.rsplit("#",1)[-1].rsplit("/",1)[-1] ulabel, urender, usymbol = get_port_unit(uuri) if alnum and not (ulabel and urender and usymbol): errors.append("port '%s' has unknown lv2 unit (our bug?, data is '%s', '%s', '%s')" % (portname, ulabel, urender, usymbol)) # using custom unit else: xlabel = world.find_nodes(uunit, ns_rdfs .label.me, None).get_first() xrender = world.find_nodes(uunit, ns_units.render.me, None).get_first() xsymbol = world.find_nodes(uunit, ns_units.symbol.me, None).get_first() if xlabel.me is not None: ulabel = xlabel.as_string() else: errors.append("port '%s' has custom unit with no label" % portname) if xrender.me is not None: urender = xrender.as_string() else: errors.append("port '%s' has custom unit with no render" % portname) if xsymbol.me is not None: usymbol = xsymbol.as_string() else: errors.append("port '%s' has custom unit with no symbol" % portname) return (types, { 'name' : portname, 'symbol' : portsymbol, 'ranges' : ranges, 'units' : { 'label' : ulabel, 'render': urender, 'symbol': usymbol, } if "Control" in types and ulabel and urender and usymbol else {}, 'designation': (get_port_data(port, ns_lv2core.designation) or [None])[0], 'properties' : properties, 'rangeSteps' : (get_port_data(port, ns_mod.rangeSteps) or get_port_data(port, ns_pprops.rangeSteps) or [None])[0], "scalePoints": scalepoints, 'shortname' : psname, })
def get_pedalboard_info(bundle): # lilv wants the last character as the separator if not bundle.endswith(os.sep): bundle += os.sep # Create our own unique lilv world # We'll load a single bundle and get all plugins from it world = lilv.World() # this is needed when loading specific bundles instead of load_all # (these functions are not exposed via World yet) lilv.lilv_world_load_specifications(world.me) lilv.lilv_world_load_plugin_classes(world.me) # convert bundle string into a lilv node bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle) # load the bundle world.load_bundle(bundlenode) # free bundlenode, no longer needed lilv.lilv_node_free(bundlenode) # get all plugins in the bundle plugins = world.get_all_plugins() # make sure the bundle includes 1 and only 1 plugin (the pedalboard) if plugins.size() != 1: raise Exception('get_pedalboard_info(%s) - bundle has 0 or > 1 plugin'.format(bundle)) # no indexing in python-lilv yet, just get the first item plugin = None for p in plugins: plugin = p break if plugin is None: raise Exception('get_pedalboard_info(%s) - failed to get plugin, you are using an old lilv!'.format(bundle)) # define the needed stuff ns_rdf = NS(world, lilv.LILV_NS_RDF) ns_lv2core = NS(world, lilv.LILV_NS_LV2) ns_ingen = NS(world, "http://drobilla.net/ns/ingen#") ns_modpedal = NS(world, "http://moddevices.com/ns/modpedal#") # check if the plugin is a pedalboard def fill_in_type(node): return node.as_string() plugin_types = [i for i in LILV_FOREACH(plugin.get_value(ns_rdf.type_), fill_in_type)] if "http://moddevices.com/ns/modpedal#Pedalboard" not in plugin_types: raise Exception('get_pedalboard_info(%s) - plugin has no mod:Pedalboard type'.format(bundle)) # let's get all the info now ingenarcs = [] ingenblocks = [] info = { 'name' : plugin.get_name().as_string(), 'uri' : plugin.get_uri().as_string(), 'author': plugin.get_author_name().as_string() or "", # Might be empty 'hardware': { # we save this info later 'audio': { 'ins' : 0, 'outs': 0 }, 'cv': { 'ins' : 0, 'outs': 0 }, 'midi': { 'ins' : 0, 'outs': 0 } }, 'size': { 'width' : plugin.get_value(ns_modpedal.width).get_first().as_int(), 'height': plugin.get_value(ns_modpedal.height).get_first().as_int(), }, 'screenshot' : os.path.basename(plugin.get_value(ns_modpedal.screenshot).get_first().as_string()), 'thumbnail' : os.path.basename(plugin.get_value(ns_modpedal.thumbnail).get_first().as_string()), 'connections': [], # we save this info later 'plugins' : [] # we save this info later } # connections arcs = plugin.get_value(ns_ingen.arc) it = arcs.begin() while not arcs.is_end(it): arc = arcs.get(it) it = arcs.next(it) if arc.me is None: continue head = lilv.lilv_world_get(world.me, arc.me, ns_ingen.head.me, None) tail = lilv.lilv_world_get(world.me, arc.me, ns_ingen.tail.me, None) if head is None or tail is None: continue ingenarcs.append({ "source": lilv.lilv_uri_to_path(lilv.lilv_node_as_string(tail)).replace(bundle,"",1), "target": lilv.lilv_uri_to_path(lilv.lilv_node_as_string(head)).replace(bundle,"",1) }) # hardware ports handled_port_uris = [] ports = plugin.get_value(ns_lv2core.port) it = ports.begin() while not ports.is_end(it): port = ports.get(it) it = ports.next(it) if port.me is None: continue # check if we already handled this port port_uri = port.as_uri() if port_uri in handled_port_uris: continue handled_port_uris.append(port_uri) # get types port_types = lilv.lilv_world_find_nodes(world.me, port.me, ns_rdf.type_.me, None) if port_types is None: continue portDir = "" # input or output portType = "" # atom, audio or cv it2 = lilv.lilv_nodes_begin(port_types) while not lilv.lilv_nodes_is_end(port_types, it2): port_type = lilv.lilv_nodes_get(port_types, it2) it2 = lilv.lilv_nodes_next(port_types, it2) if port_type is None: continue port_type_uri = lilv.lilv_node_as_uri(port_type) if port_type_uri == "http://lv2plug.in/ns/lv2core#InputPort": portDir = "input" elif port_type_uri == "http://lv2plug.in/ns/lv2core#OutputPort": portDir = "output" elif port_type_uri == "http://lv2plug.in/ns/lv2core#AudioPort": portType = "audio" elif port_type_uri == "http://lv2plug.in/ns/lv2core#CVPort": portType = "cv" elif port_type_uri == "http://lv2plug.in/ns/ext/atom#AtomPort": portType = "atom" if not (portDir or portType): continue if portType == "audio": if portDir == "input": info['hardware']['audio']['ins'] += 1 else: info['hardware']['audio']['outs'] += 1 elif portType == "atom": if portDir == "input": info['hardware']['midi']['ins'] += 1 else: info['hardware']['midi']['outs'] += 1 elif portType == "cv": if portDir == "input": info['hardware']['cv']['ins'] += 1 else: info['hardware']['cv']['outs'] += 1 # plugins blocks = plugin.get_value(ns_ingen.block) it = blocks.begin() while not blocks.is_end(it): block = blocks.get(it) it = blocks.next(it) if block.me is None: continue protouri1 = lilv.lilv_world_get(world.me, block.me, ns_lv2core.prototype.me, None) protouri2 = lilv.lilv_world_get(world.me, block.me, ns_ingen.prototype.me, None) if protouri1 is not None: proto = protouri1 elif protouri2 is not None: proto = protouri2 else: continue uri = lilv.lilv_node_as_uri(proto) microver = lilv.lilv_world_get(world.me, block.me, ns_lv2core.microVersion.me, None) minorver = lilv.lilv_world_get(world.me, block.me, ns_lv2core.minorVersion.me, None) ingenblocks.append({ "uri": uri, "x": lilv.lilv_node_as_float(lilv.lilv_world_get(world.me, block.me, ns_ingen.canvasX.me, None)), "y": lilv.lilv_node_as_float(lilv.lilv_world_get(world.me, block.me, ns_ingen.canvasY.me, None)), "microVersion": lilv.lilv_node_as_int(microver) if microver else 0, "minorVersion": lilv.lilv_node_as_int(minorver) if minorver else 0, }) info['connections'] = ingenarcs info['plugins'] = ingenblocks return info
def get_pedalboard_info(bundle): # lilv wants the last character as the separator bundle = os.path.abspath(bundle) if not bundle.endswith(os.sep): bundle += os.sep # Create our own unique lilv world # We'll load a single bundle and get all plugins from it world = lilv.World() # this is needed when loading specific bundles instead of load_all # (these functions are not exposed via World yet) lilv.lilv_world_load_specifications(world.me) lilv.lilv_world_load_plugin_classes(world.me) # convert bundle string into a lilv node bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle) # load the bundle world.load_bundle(bundlenode) # free bundlenode, no longer needed lilv.lilv_node_free(bundlenode) # get all plugins in the bundle plugins = world.get_all_plugins() # make sure the bundle includes 1 and only 1 plugin (the pedalboard) if plugins.size() != 1: raise Exception( 'get_pedalboard_info(%s) - bundle has 0 or > 1 plugin'.format( bundle)) # no indexing in python-lilv yet, just get the first item plugin = None for p in plugins: plugin = p break if plugin is None: raise Exception( 'get_pedalboard_info(%s) - failed to get plugin, you are using an old lilv!' .format(bundle)) # define the needed stuff ns_rdf = NS(world, lilv.LILV_NS_RDF) ns_lv2core = NS(world, lilv.LILV_NS_LV2) ns_ingen = NS(world, "http://drobilla.net/ns/ingen#") ns_modpedal = NS(world, "http://moddevices.com/ns/modpedal#") # check if the plugin is a pedalboard def fill_in_type(node): return node.as_string() plugin_types = [ i for i in LILV_FOREACH(plugin.get_value(ns_rdf.type_), fill_in_type) ] if "http://moddevices.com/ns/modpedal#Pedalboard" not in plugin_types: raise Exception( 'get_pedalboard_info(%s) - plugin has no mod:Pedalboard type'. format(bundle)) # let's get all the info now ingenarcs = [] ingenblocks = [] info = { 'name': plugin.get_name().as_string(), 'uri': plugin.get_uri().as_string(), 'author': plugin.get_author_name().as_string() or "", # Might be empty 'hardware': { # we save this info later 'audio': { 'ins': 0, 'outs': 0 }, 'cv': { 'ins': 0, 'outs': 0 }, 'midi': { 'ins': 0, 'outs': 0 } }, 'size': { 'width': plugin.get_value(ns_modpedal.width).get_first().as_int(), 'height': plugin.get_value(ns_modpedal.height).get_first().as_int(), }, 'screenshot': os.path.basename( plugin.get_value(ns_modpedal.screenshot).get_first().as_string() or ""), 'thumbnail': os.path.basename( plugin.get_value(ns_modpedal.thumbnail).get_first().as_string() or ""), 'connections': [], # we save this info later 'plugins': [] # we save this info later } # connections arcs = plugin.get_value(ns_ingen.arc) it = arcs.begin() while not arcs.is_end(it): arc = arcs.get(it) it = arcs.next(it) if arc.me is None: continue head = lilv.lilv_world_get(world.me, arc.me, ns_ingen.head.me, None) tail = lilv.lilv_world_get(world.me, arc.me, ns_ingen.tail.me, None) if head is None or tail is None: continue ingenarcs.append({ "source": lilv.lilv_uri_to_path(lilv.lilv_node_as_string(tail)).replace( bundle, "", 1), "target": lilv.lilv_uri_to_path(lilv.lilv_node_as_string(head)).replace( bundle, "", 1) }) # hardware ports handled_port_uris = [] ports = plugin.get_value(ns_lv2core.port) it = ports.begin() while not ports.is_end(it): port = ports.get(it) it = ports.next(it) if port.me is None: continue # check if we already handled this port port_uri = port.as_uri() if port_uri in handled_port_uris: continue if port_uri.endswith("/control_in") or port_uri.endswith( "/control_out"): continue handled_port_uris.append(port_uri) # get types port_types = lilv.lilv_world_find_nodes(world.me, port.me, ns_rdf.type_.me, None) if port_types is None: continue portDir = "" # input or output portType = "" # atom, audio or cv it2 = lilv.lilv_nodes_begin(port_types) while not lilv.lilv_nodes_is_end(port_types, it2): port_type = lilv.lilv_nodes_get(port_types, it2) it2 = lilv.lilv_nodes_next(port_types, it2) if port_type is None: continue port_type_uri = lilv.lilv_node_as_uri(port_type) if port_type_uri == "http://lv2plug.in/ns/lv2core#InputPort": portDir = "input" elif port_type_uri == "http://lv2plug.in/ns/lv2core#OutputPort": portDir = "output" elif port_type_uri == "http://lv2plug.in/ns/lv2core#AudioPort": portType = "audio" elif port_type_uri == "http://lv2plug.in/ns/lv2core#CVPort": portType = "cv" elif port_type_uri == "http://lv2plug.in/ns/ext/atom#AtomPort": portType = "atom" if not (portDir or portType): continue if portType == "audio": if portDir == "input": info['hardware']['audio']['ins'] += 1 else: info['hardware']['audio']['outs'] += 1 elif portType == "atom": if portDir == "input": info['hardware']['midi']['ins'] += 1 else: info['hardware']['midi']['outs'] += 1 elif portType == "cv": if portDir == "input": info['hardware']['cv']['ins'] += 1 else: info['hardware']['cv']['outs'] += 1 # plugins blocks = plugin.get_value(ns_ingen.block) it = blocks.begin() while not blocks.is_end(it): block = blocks.get(it) it = blocks.next(it) if block.me is None: continue protouri1 = lilv.lilv_world_get(world.me, block.me, ns_lv2core.prototype.me, None) protouri2 = lilv.lilv_world_get(world.me, block.me, ns_ingen.prototype.me, None) if protouri1 is not None: proto = protouri1 elif protouri2 is not None: proto = protouri2 else: continue instance = lilv.lilv_uri_to_path(lilv.lilv_node_as_string( block.me)).replace(bundle, "", 1) uri = lilv.lilv_node_as_uri(proto) enabled = lilv.lilv_world_get(world.me, block.me, ns_ingen.enabled.me, None) minorver = lilv.lilv_world_get(world.me, block.me, ns_lv2core.minorVersion.me, None) microver = lilv.lilv_world_get(world.me, block.me, ns_lv2core.microVersion.me, None) ingenblocks.append({ "instance": instance, "uri": uri, "x": lilv.lilv_node_as_float( lilv.lilv_world_get(world.me, block.me, ns_ingen.canvasX.me, None)), "y": lilv.lilv_node_as_float( lilv.lilv_world_get(world.me, block.me, ns_ingen.canvasY.me, None)), "enabled": lilv.lilv_node_as_bool(enabled) if enabled is not None else False, "minorVersion": lilv.lilv_node_as_int(minorver) if minorver else 0, "microVersion": lilv.lilv_node_as_int(microver) if microver else 0, }) info['connections'] = ingenarcs info['plugins'] = ingenblocks return info