int main(int argc, char** argv) { if (argc != 2) { fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]); return 1; } const char* bundle_path = argv[1]; LilvWorld* world = lilv_world_new(); // Load test plugin bundle uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path); SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true); LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf); lilv_world_load_bundle(world, bundle_uri); free(abs_bundle); serd_node_free(&bundle); LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI); const LilvPlugins* plugins = lilv_world_get_all_plugins(world); const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri); TEST_ASSERT(plugin); LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL); TEST_ASSERT(instance); lilv_world_free(world); return 0; }
Result LV2Module::instantiate (double samplerate) { freeInstance(); currentSampleRate = samplerate; features.clearQuick(); world.getFeatures (features); // check for a worker interface LilvNodes* nodes = lilv_plugin_get_extension_data (plugin); LILV_FOREACH (nodes, iter, nodes) { const LilvNode* node = lilv_nodes_get (nodes, iter); if (lilv_node_equals (node, world.work_interface)) { worker = new LV2Worker (world.getWorkThread(), 1); features.add (worker->getFeature()); } } lilv_nodes_free (nodes); nodes = nullptr; features.add (nullptr); instance = lilv_plugin_instantiate (plugin, samplerate, features.getRawDataPointer()); if (instance == nullptr) { features.clearQuick(); worker = nullptr; return Result::fail ("Could not instantiate plugin."); } if (const void* data = getExtensionData (LV2_WORKER__interface)) { assert (worker != nullptr); worker->setSize (2048); worker->setInterface (lilv_instance_get_handle (instance), (LV2_Worker_Interface*) data); } else if (worker) { features.removeFirstMatchingValue (worker->getFeature()); worker = nullptr; } return Result::ok(); }
bool LV2Effect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right, sampleCount lstart, sampleCount rstart, sampleCount len) { /* Allocate buffers */ if (mBlockSize == 0) { mBlockSize = left->GetMaxBlockSize() * 2; fInBuffer = new float *[mAudioInputs.GetCount()]; for (size_t i = 0; i < mAudioInputs.GetCount(); i++) { fInBuffer[i] = new float[mBlockSize]; } fOutBuffer = new float *[mAudioOutputs.GetCount()]; for (size_t i = 0; i < mAudioOutputs.GetCount(); i++) { fOutBuffer[i] = new float[mBlockSize]; } } /* Instantiate the plugin */ LilvInstance *handle = lilv_plugin_instantiate(mData, left->GetRate(), gLV2Features); if (!handle) { wxMessageBox(wxString::Format(_("Unable to load plug-in %s"), pluginName.c_str())); return false; } /* Write the Note On to the MIDI event buffer and connect it */ LV2_Event_Buffer *midiBuffer = NULL; int noteOffTime; if (mMidiInput) { midiBuffer = lv2_event_buffer_new(40, 2); LV2_Event_Iterator iter; lv2_event_begin(&iter, midiBuffer); uint8_t noteOn[] = { 0x90, mNoteKey, mNoteVelocity }; lv2_event_write(&iter, 0, 0, 1, 3, noteOn); noteOffTime = mNoteLength * left->GetRate(); if (noteOffTime < len && noteOffTime < mBlockSize) { uint8_t noteOff[] = { 0x80, mNoteKey, 64 }; lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff); } lilv_instance_connect_port(handle, mMidiInput->mIndex, midiBuffer); } for (size_t p = 0; p < mAudioInputs.GetCount(); p++) { lilv_instance_connect_port(handle, mAudioInputs[p].mIndex, fInBuffer[p]); } for (size_t p = 0; p < mAudioOutputs.GetCount(); p++) { lilv_instance_connect_port(handle, mAudioOutputs[p].mIndex, fOutBuffer[p]); } for (size_t p = 0; p < mControlInputs.GetCount(); p++) { lilv_instance_connect_port(handle, mControlInputs[p].mIndex, &mControlInputs[p].mControlBuffer); } for (size_t p = 0; p < mControlOutputs.GetCount(); p++) { lilv_instance_connect_port(handle, mControlOutputs[p].mIndex, &mControlOutputs[p].mControlBuffer); } float latency = 0.0; if (mLatencyPortIndex >= 0) { lilv_instance_connect_port(handle, mLatencyPortIndex, &latency); } lilv_instance_activate(handle); // Actually perform the effect here sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; sampleCount ols = ls; sampleCount ors = rs; bool noteOver = false; sampleCount delayed = 0; sampleCount delay = 0; bool cleared = false; while (len || delayed) { int block = mBlockSize; if (len) { if (block > len) { block = len; } if (left && mAudioInputs.GetCount() > 0) { left->Get((samplePtr)fInBuffer[0], floatSample, ls, block); } if (right && mAudioInputs.GetCount() > 1) { right->Get((samplePtr)fInBuffer[1], floatSample, rs, block); } } else if (delayed) { // At the end if we don't have enough left for a whole block if (block > delayed) { block = delayed; } // Clear the input buffer so that we only pass zeros to the effect. if (!cleared) { for (int i = 0; i < mBlockSize; i++) { fInBuffer[0][i] = 0.0; } if (right) { memcpy(fInBuffer[1], fOutBuffer[0], mBlockSize); } cleared = true; } } lilv_instance_run(handle, block); if (delayed == 0 && latency != 0) { delayed = delay = latency; } if (delay >= block) { delay -= block; } else if (delay > 0) { sampleCount oblock = block - delay; if (left && mAudioOutputs.GetCount() > 0) { left->Set((samplePtr)(fOutBuffer[0] + delay), floatSample, ols, oblock); } if (right && mAudioOutputs.GetCount() > 1) { right->Set((samplePtr)(fOutBuffer[1] + delay), floatSample, ors, oblock); } ols += oblock; ors += oblock; delay = 0; } else { if (left && mAudioOutputs.GetCount() > 0) { left->Set((samplePtr)fOutBuffer[0], floatSample, ols, block); } if (right && mAudioOutputs.GetCount() > 1) { right->Set((samplePtr)fOutBuffer[1], floatSample, ors, block); } ols += block; ors += block; } if (len) { len -= block; noteOffTime -= block; // Clear the event buffer and add the note off event if needed if (mMidiInput) { lv2_event_buffer_reset(midiBuffer, 1, (uint8_t *)midiBuffer + sizeof(LV2_Event_Buffer)); if (!noteOver && noteOffTime < len && noteOffTime < block) { LV2_Event_Iterator iter; lv2_event_begin(&iter, midiBuffer); uint8_t noteOff[] = { 0x80, mNoteKey, 64 }; lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff); noteOver = true; } } } else if (delayed) { delayed -= block; } ls += block; rs += block; if (mAudioInputs.GetCount() > 1) { if (TrackGroupProgress(count, (ls-lstart)/(double)originalLen)) { return false; } } else { if (TrackProgress(count, (ls-lstart)/(double)originalLen)) { return false; } } } lilv_instance_deactivate(handle); lilv_instance_free(handle); return true; }
static gboolean gst_lv2_filter_setup (GstAudioFilter * gsp, const GstAudioInfo * info) { GstLV2Filter *self; GstLV2FilterClass *oclass; GstAudioFilterClass *audiofilter_class; gint i; audiofilter_class = GST_AUDIO_FILTER_GET_CLASS (gsp); self = (GstLV2Filter *) gsp; oclass = (GstLV2FilterClass *) audiofilter_class; g_return_val_if_fail (self->activated == FALSE, FALSE); GST_DEBUG_OBJECT (self, "instantiating the plugin at %d Hz", GST_AUDIO_INFO_RATE (info)); if (self->instance) lilv_instance_free (self->instance); if (!(self->instance = lilv_plugin_instantiate (oclass->plugin, GST_AUDIO_INFO_RATE (info), NULL))) goto no_instance; /* connect the control ports */ for (i = 0; i < oclass->control_in_ports->len; i++) lilv_instance_connect_port (self->instance, g_array_index (oclass->control_in_ports, GstLV2FilterPort, i).index, &(self->ports.control.in[i])); for (i = 0; i < oclass->control_out_ports->len; i++) lilv_instance_connect_port (self->instance, g_array_index (oclass->control_out_ports, GstLV2FilterPort, i).index, &(self->ports.control.out[i])); /* FIXME Handle audio channel positionning while negotiating CAPS */ #if 0 /* set input group pad audio channel position */ for (i = 0; i < oclass->in_groups->len; ++i) { group = &g_array_index (oclass->in_groups, GstLV2FilterGroup, i); if (group->has_roles) { if ((positions = gst_lv2_filter_build_positions (group))) { if ((pad = gst_element_get_static_pad (GST_ELEMENT (gsp), lilv_node_as_string (group->symbol)))) { GST_INFO_OBJECT (self, "set audio channel positions on sink pad %s", lilv_node_as_string (group->symbol)); s = gst_caps_get_structure (caps, 0); gst_audio_set_channel_positions (s, positions); gst_object_unref (pad); } g_free (positions); positions = NULL; } } } /* set output group pad audio channel position */ for (i = 0; i < oclass->out_groups->len; ++i) { group = &g_array_index (oclass->out_groups, GstLV2FilterGroup, i); if (group->has_roles) { if ((positions = gst_lv2_filter_build_positions (group))) { if ((pad = gst_element_get_static_pad (GST_ELEMENT (gsp), lilv_node_as_string (group->symbol)))) { GST_INFO_OBJECT (self, "set audio channel positions on src pad %s", lilv_node_as_string (group->symbol)); s = gst_caps_get_structure (caps, 0); gst_audio_set_channel_positions (s, positions); gst_object_unref (pad); } g_free (positions); positions = NULL; } } } #endif lilv_instance_activate (self->instance); self->activated = TRUE; return TRUE; no_instance: { GST_ERROR_OBJECT (gsp, "could not create instance"); return FALSE; } }
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; }