gboolean gst_lv2_check_required_features (const LilvPlugin * lv2plugin) { LilvNodes *required_features = lilv_plugin_get_required_features (lv2plugin); if (required_features) { LilvIter *i; gint j; gboolean missing = FALSE; for (i = lilv_nodes_begin (required_features); !lilv_nodes_is_end (required_features, i); i = lilv_nodes_next (required_features, i)) { const LilvNode *required_feature = lilv_nodes_get (required_features, i); const char *required_feature_uri = lilv_node_as_uri (required_feature); missing = TRUE; for (j = 0; lv2_features[j]; j++) { if (!strcmp (lv2_features[j]->URI, required_feature_uri)) { missing = FALSE; break; } } if (missing) { GST_FIXME ("lv2 plugin %s needs host feature: %s", lilv_node_as_uri (lilv_plugin_get_uri (lv2plugin)), required_feature_uri); break; } } lilv_nodes_free (required_features); return (!missing); } return TRUE; }
bool LV2World::isPluginSupported (const LilvPlugin* plugin) { // Required features support LilvNodes* nodes = lilv_plugin_get_required_features (plugin); LILV_FOREACH (nodes, iter, nodes) { const LilvNode* node (lilv_nodes_get (nodes, iter)); if (! isFeatureSupported (CharPointer_UTF8 (lilv_node_as_uri (node)))) { return false; // Feature not supported } } lilv_nodes_free (nodes); nodes = nullptr; // Check this plugin's port types are supported const uint32 numPorts = lilv_plugin_get_num_ports (plugin); for (uint32 i = 0; i < numPorts; ++i) { // const LilvPort* port (lilv_plugin_get_port_by_index (plugin, i)); // nothing here yet (or ever) } return true; }
LV2Effect::LV2Effect(const LilvPlugin *data, const std::set<wxString> & categories) : mValid(true), mCategories(categories), mMidiInput(0), mLatencyPortIndex(-1) { // We don't support any features at all, so if the plugin requires // any we skip it. LilvNodes *req = lilv_plugin_get_required_features(data); size_t nFeatures = lilv_nodes_size(req); lilv_nodes_free(req); if (nFeatures > 0) { mValid = false; return; } mData = data; pluginName = GetString(lilv_plugin_get_name(mData), true); fInBuffer = NULL; fOutBuffer = NULL; mLength = 0; // Allocate buffers for the port indices and the default control values int numPorts = lilv_plugin_get_num_ports(mData); float *minimumValues = new float [numPorts]; float *maximumValues = new float [numPorts]; float *defaultValues = new float [numPorts]; // Retrieve the port ranges for all ports (some values may be NaN) lilv_plugin_get_port_ranges_float(mData, minimumValues, maximumValues, defaultValues); // Get info about all ports for (int i = 0; i < numPorts; i++) { const LilvPort *port = lilv_plugin_get_port_by_index(mData, i); LV2Port internalPort; internalPort.mIndex = lilv_port_get_index(mData, port); // Get the port name LilvNode *tmpName = lilv_port_get_name(mData, port); internalPort.mName = GetString(tmpName); lilv_node_free(tmpName); // Get the scale points LilvScalePoints* points = lilv_port_get_scale_points(mData, port); LILV_FOREACH(scale_points, j, points) { const LilvScalePoint *point = lilv_scale_points_get(points, j); internalPort.mScaleValues.Add(lilv_node_as_float(lilv_scale_point_get_value(point))); internalPort.mScaleLabels.Add(GetString(lilv_scale_point_get_label(point))); } lilv_scale_points_free(points); // Get the groups LilvNodes *groups = lilv_port_get_value(mData, port, gPortGroup); if (groups) { LilvNode *group = lilv_nodes_get_first(groups); wxString uri = GetString(group); wxString label; const LilvNode *name = lilv_world_get(gWorld, group, gName, NULL); if (name) { label = GetString(name); } else { // Shouldn't happen, but provide something label = uri; } lilv_nodes_free(groups); // Check for new group if (mPortGroups.find(uri) == mPortGroups.end()) { mPortGroups[uri] = LV2PortGroup(label); } #if 0 // Get subgroup // // LLL: This isn't right...must find or construct a plugin with // subgroups. LilvNodes *subgroup = lilv_node_get_value(mData, port, gSubGroupOf); if (subgroups) { LilvNode *subgroup = lilv_nodes_get_first(subgroups); wxString uri = GetString(subgroup); const LilvNode *subgroup = lilv_world_get(gWorld, group, gSubGroupOf, NULL); wxString label = GetString(name); lilv_nodes_free(subgroup); } else #endif { mRootGroup.AddSubGroup(mPortGroups[uri]); } mPortGroups[uri].AddParameter(i); } else { mRootGroup.AddParameter(i); } // Get the port type if (lilv_port_is_a(mData, port, gAudioPortClass)) { if (lilv_port_is_a(mData, port, gInputPortClass)) { mAudioInputs.Add(internalPort); } else if (lilv_port_is_a(mData, port, gOutputPortClass)) { mAudioOutputs.Add(internalPort); } } else if (lilv_port_is_a(mData, port, gControlPortClass) && lilv_port_is_a(mData, port, gInputPortClass)) { internalPort.mControlBuffer = float(1.0); internalPort.mMin = minimumValues[i]; internalPort.mMax = maximumValues[i]; internalPort.mDefault = defaultValues[i]; if (isfinite(defaultValues[i])) { internalPort.mControlBuffer = defaultValues[i]; } else if (isfinite(minimumValues[i])) { internalPort.mControlBuffer = minimumValues[i]; } else if (isfinite(maximumValues[i])) { internalPort.mControlBuffer = maximumValues[i]; } if (lilv_port_has_property(mData, port, gPortToggled)) { internalPort.mToggle = true; } else if (lilv_port_has_property(mData, port, gPortIsInteger)) { internalPort.mInteger = true; } else if (lilv_port_has_property(mData, port, gPortIsSampleRate)) { internalPort.mSampleRate = true; } else if (lilv_port_has_property(mData, port, gPortIsEnumeration)) { internalPort.mEnumeration = true; } mControlInputs.Add(internalPort); } else if (lilv_port_is_a(mData, port, gControlPortClass) && lilv_port_is_a(mData, port, gOutputPortClass)) { // If there is more than one latency port, the plugin is invalid if (lilv_port_has_property(mData, port, gPortIsLatency)) { if (mLatencyPortIndex >= 0) { mValid = false; continue; } mLatencyPortIndex = i; } else if (!lilv_port_has_property(mData, port, gPortIsOptional)) { mControlOutputs.Add(internalPort); } } else if (lilv_port_is_a(mData, port, gMidiPortClass) && lilv_port_is_a(mData, port, gInputPortClass)) { // If there is more than one MIDI input port, the plugin is invalid if (mMidiInput) { mValid = false; continue; } mMidiInput = new LV2Port(internalPort); } else { // Unknown port type, we set the invalid flag // mValid = false; } } delete [] minimumValues; delete [] maximumValues; delete [] defaultValues; // MIDI synths may not have any audio inputs. if (mMidiInput && mAudioInputs.GetCount() > 0) { mValid = false; } // Determine whether the plugin is a generator, effect or analyser // depending on the number of ports of each type (not completely accurate, // but works most of the time) int flags = PLUGIN_EFFECT; if (mAudioInputs.GetCount() == 0) { flags |= INSERT_EFFECT; } else if (mAudioOutputs.GetCount() == 0) { flags |= ANALYZE_EFFECT; } else { flags |= PROCESS_EFFECT; } SetEffectFlags(flags); }
Lv2Plugin *Lv2PluginCache::getPlugin(const char *uri, const char *preset, Lv2State *state) { // create a unique key for uri/preset/state std::string keyString(uri); if(preset) { keyString.append(":"); keyString.append(preset); } std::size_t hash = std::hash<std::string>()(keyString); if(state) { hash = hash ^ (state->getHash() << 1); } // count tells how many instances of this type of plugin int count = ++instanceCount[hash]; // return cached plugin instance if available int key = hash + count; Lv2Plugin *cachedPlugin = findObject(key); if(cachedPlugin) { cachedPlugin->restore(); return cachedPlugin; } // look in cache for plugin type by uri std::string uriString(uri); const LilvPlugin *lilvPlugin = pluginMap[uriString]; if(!lilvPlugin) { // find lv2 plugin in lilv LilvNode *lilvUri = lilv_new_uri(world, uri); lilvPlugin = lilv_plugins_get_by_uri(plugins, lilvUri); lilv_node_free(lilvUri); if(!lilvPlugin) { throw std::logic_error(std::string("Plugin is not installed on this system: ") + uriString); } // check that required features are supported LilvNodes* features = lilv_plugin_get_required_features(lilvPlugin); for (LilvIter* f = lilv_nodes_begin(features); !lilv_nodes_is_end(features, f); f = lilv_nodes_next(features, f)) { const char* featureUri = lilv_node_as_uri(lilv_nodes_get(features, f)); if(!supported[featureUri]) { throw std::logic_error(std::string("Plugin ") + uriString + " requires unsupported feature: " + featureUri); } } lilv_nodes_free(features); pluginMap[uriString] = lilvPlugin; } // create worker if required Lv2Worker *worker = 0; if (lilv_plugin_has_feature(lilvPlugin, lv2Constants.lv2WorkerSchedule) && lilv_plugin_has_extension_data(lilvPlugin, lv2Constants.lv2WorkerInterface)) { worker = new Lv2Worker(); ((LV2_Worker_Schedule*)lv2Features[5]->data)->handle = worker; } // instantiate LilvInstance *instance = lilv_plugin_instantiate(lilvPlugin, sampleRate, lv2Features); // connect worker with plugin instance if(worker) { worker->setInstance(instance); } // create plugin object Lv2Plugin *plugin = new Lv2Plugin(lilvPlugin, instance, lv2Constants, worker); // restore baseline default state LilvState *defaultState = lilv_state_new_from_world(world, &map, lilv_plugin_get_uri(lilvPlugin)); lilv_state_restore(defaultState, instance, setPortValue, plugin, 0, lv2Features); // find and restore preset if(preset) { LilvNodes* presets = lilv_plugin_get_related(lilvPlugin, lv2Constants.lv2Presets); LilvNode *myPreset = 0; LILV_FOREACH(nodes, i, presets) { const LilvNode* presetNode = lilv_nodes_get(presets, i); lilv_world_load_resource(world, presetNode); LilvNodes* labels = lilv_world_find_nodes(world, presetNode, lv2Constants.lv2RdfsLabel, NULL); if (labels) { const LilvNode* label = lilv_nodes_get_first(labels); const char *labelString = lilv_node_as_string(label); // TODO: free? if(!strcmp(labelString, preset)) { myPreset = lilv_node_duplicate(presetNode); } lilv_nodes_free(labels); } } lilv_nodes_free(presets); if(myPreset) { LilvState* presetState = lilv_state_new_from_world(world, &map, myPreset); lilv_state_restore(presetState, instance, setPortValue, plugin, 0, NULL); // lilv_state_free(state); // TODO } else { throw std::logic_error(std::string("Plugin ") + uriString + " has no such preset: " + preset); } } // restore state else if(state) { // TODO: what if state is requested on a plugin that doesn't support?
static double bench(const LilvPlugin* p, uint32_t sample_count, uint32_t block_size) { URITable uri_table; uri_table_init(&uri_table); LV2_URID_Map map = { &uri_table, uri_table_map }; LV2_Feature map_feature = { LV2_URID_MAP_URI, &map }; LV2_URID_Unmap unmap = { &uri_table, uri_table_unmap }; LV2_Feature unmap_feature = { LV2_URID_UNMAP_URI, &unmap }; const LV2_Feature* features[] = { &map_feature, &unmap_feature, NULL }; float* const buf = (float*)calloc(block_size * 2, sizeof(float)); float* const in = buf; float* const out = buf + block_size; if (!buf) { fprintf(stderr, "Out of memory\n"); return 0.0; } LV2_Atom_Sequence seq = { { sizeof(LV2_Atom_Sequence_Body), uri_table_map(&uri_table, LV2_ATOM__Sequence) }, { 0, 0 } }; const char* uri = lilv_node_as_string(lilv_plugin_get_uri(p)); LilvNodes* required = lilv_plugin_get_required_features(p); LILV_FOREACH(nodes, i, required) { const LilvNode* feature = lilv_nodes_get(required, i); if (!lilv_node_equals(feature, urid_map)) { fprintf(stderr, "<%s> requires feature <%s>, skipping\n", uri, lilv_node_as_uri(feature)); free(buf); uri_table_destroy(&uri_table); return 0.0; } } LilvInstance* instance = lilv_plugin_instantiate(p, 48000.0, features); if (!instance) { fprintf(stderr, "Failed to instantiate <%s>\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); free(buf); uri_table_destroy(&uri_table); return 0.0; } float* controls = (float*)calloc( lilv_plugin_get_num_ports(p), sizeof(float)); lilv_plugin_get_port_ranges_float(p, NULL, NULL, controls); const uint32_t n_ports = lilv_plugin_get_num_ports(p); for (uint32_t index = 0; index < n_ports; ++index) { const LilvPort* port = lilv_plugin_get_port_by_index(p, index); if (lilv_port_is_a(p, port, lv2_ControlPort)) { lilv_instance_connect_port(instance, index, &controls[index]); } else if (lilv_port_is_a(p, port, lv2_AudioPort) || lilv_port_is_a(p, port, lv2_CVPort)) { if (lilv_port_is_a(p, port, lv2_InputPort)) { lilv_instance_connect_port(instance, index, in); } else if (lilv_port_is_a(p, port, lv2_OutputPort)) { lilv_instance_connect_port(instance, index, out); } else { fprintf(stderr, "<%s> port %d neither input nor output, skipping\n", uri, index); lilv_instance_free(instance); free(buf); free(controls); uri_table_destroy(&uri_table); return 0.0; } } else if (lilv_port_is_a(p, port, atom_AtomPort)) { lilv_instance_connect_port(instance, index, &seq); } else { fprintf(stderr, "<%s> port %d has unknown type, skipping\n", uri, index); lilv_instance_free(instance); free(buf); free(controls); uri_table_destroy(&uri_table); return 0.0; } } lilv_instance_activate(instance); struct timespec ts = bench_start(); for (uint32_t i = 0; i < (sample_count / block_size); ++i) { lilv_instance_run(instance, block_size); } const double elapsed = bench_end(&ts); lilv_instance_deactivate(instance); lilv_instance_free(instance); uri_table_destroy(&uri_table); if (full_output) { printf("%d %d ", block_size, sample_count); } printf("%lf %s\n", elapsed, uri); free(buf); free(controls); return elapsed; }