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); }
const LV2PortGroup& LV2Effect::GetPortGroups() { if (!mPortGroupsRetrieved) { // Find all port groups with ports in them. char portGroupQuery[] = "PREFIX : <http://lv2plug.in/ns/lv2core#>\n" "PREFIX pg: <http://ll-plugins.nongnu.org/lv2/ext/portgroups#>\n" "SELECT ?index, ?uri, ?label WHERE {\n" "<> :port ?port.\n" "?port :index ?index.\n" "?port pg:membership ?ms.\n" "?ms pg:group ?uri.\n" "?uri rdfs:label ?label.\n" "}"; SLV2Values portIndices = slv2_plugin_query_variable(mData, portGroupQuery, 0); SLV2Values groupUris = slv2_plugin_query_variable(mData, portGroupQuery, 1); SLV2Values groupLabels = slv2_plugin_query_variable(mData, portGroupQuery, 2); std::map<wxString, LV2PortGroup> portGroups; std::vector<bool> inGroup(mControlInputs.size(), false); size_t nMemberships = slv2_values_size(portIndices); for (size_t i = 0; i < nMemberships; ++i) { uint32_t idx = slv2_value_as_int(slv2_values_get_at(portIndices, i)); uint32_t p; for (p = 0; p < mControlInputs.size(); ++p) { if (mControlInputs[p].mIndex == idx) break; } if (p == mControlInputs.size()) continue; wxString uri = wxString::FromUTF8(slv2_value_as_string(slv2_values_get_at(groupUris, i))); wxString label = wxString::FromUTF8(slv2_value_as_string(slv2_values_get_at(groupLabels, i))); std::map<wxString, LV2PortGroup>::iterator iter = portGroups.find(uri); if (iter == portGroups.end()) portGroups[uri] = LV2PortGroup(label); portGroups[uri].AddParameter(p); inGroup[p] = true; } slv2_values_free(portIndices); slv2_values_free(groupUris); slv2_values_free(groupLabels); // Add all ports that aren't in any port groups to the root group. for (uint32_t p = 0; p < mControlInputs.size(); ++p) { if (!inGroup[p]) mRootGroup.AddParameter(p); } // Find all subgroup relationships. char subGroupQuery[] = "PREFIX : <http://lv2plug.in/ns/lv2core#>\n" "PREFIX pg: <http://ll-plugins.nongnu.org/lv2/ext/portgroups#>\n" "SELECT ?sub, ?parent WHERE {\n" "?sub pg:subgroupOf ?parent.\n" "}"; SLV2Values subs = slv2_plugin_query_variable(mData, subGroupQuery, 0); SLV2Values parents = slv2_plugin_query_variable(mData, subGroupQuery, 1); size_t nSubgroups = slv2_values_size(subs); for (size_t i = 0; i < nSubgroups; ++i) { wxString parent = wxString::FromUTF8(slv2_value_as_uri(slv2_values_get_at(parents, i))); wxString sub = wxString::FromUTF8(slv2_value_as_uri(slv2_values_get_at(subs, i))); std::map<wxString, LV2PortGroup>::iterator iter = portGroups.find(parent); std::map<wxString, LV2PortGroup>::iterator iter2 = portGroups.find(sub); if (iter != portGroups.end() && iter2 != portGroups.end()) { iter->second.AddSubGroup(iter2->second); } } slv2_values_free(subs); slv2_values_free(parents); // Make all groups subgroups of the root group. std::map<wxString, LV2PortGroup>::iterator iter; for (iter = portGroups.begin(); iter != portGroups.end(); ++iter) mRootGroup.AddSubGroup(iter->second); mPortGroupsRetrieved = true; } std::queue<const LV2PortGroup*> groups; groups.push(&mRootGroup); while (!groups.empty()) { const LV2PortGroup* g = groups.front(); groups.pop(); const std::vector<LV2PortGroup>& subs = g->GetSubGroups(); for (std::vector<LV2PortGroup>::const_iterator iter = subs.begin(); iter != subs.end(); ++iter) groups.push(&*iter); } return mRootGroup; }