/* * Test app to load & run all LV2 plugins for one cycle. * * Compile with: * gcc `pkg-config --cflags --libs slv2 glib-2.0` -lm -o test-lv2 test-lv2.c * * Simply run it with './test-lv2' to test all plugins. * Run it with './test-lv2 -p PACKAGE' to test a particular package. * (Run ./test-lv2 --help to see the list of known packages.) * * Run it with 'valgrind --tool=memcheck ./test-lv2' to spot memory errors. * * Released under GNU General Public License version 2. * Bits from lv2_jack_host Copyright (C) 2007-2009 Dave Robillard */ #include #include #include #include /* The sample rate to pass to the plugins. We don't output audio so it's not too important. */ #define SAMPLE_RATE 48000 /* The number of samples to use for the run. Again, not too important. */ #define NUM_SAMPLES 256 /* The size of the event buffer. */ #define EVENT_BUFFER_CAPACITY 1024 static char *package = NULL; static char *uri_prefix = NULL; static int uri_prefix_len = 0; static char *plugin_uri = NULL; static SLV2Value audio_class; static SLV2Value control_class; static SLV2Value event_class; /* LV2_URI_Map stuff. */ #define LV2_URI_MAP_URI "http://lv2plug.in/ns/ext/uri-map" typedef void* LV2_URI_Map_Callback_Data; typedef struct { LV2_URI_Map_Callback_Data callback_data; uint32_t (*uri_to_id)(LV2_URI_Map_Callback_Data callback_data, const char* map, const char* uri); } LV2_URI_Map_Feature; /* LV2_Event stuff. */ #define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" #define LV2_EVENT_AUDIO_STAMP 0 typedef struct { uint32_t frames; uint32_t subframes; uint16_t type; uint16_t size; } LV2_Event; typedef struct { uint8_t* data; uint16_t header_size; uint16_t stamp_type; uint32_t event_count; uint32_t capacity; uint32_t size; } LV2_Event_Buffer; typedef void* LV2_Event_Callback_Data; typedef struct { LV2_Event_Callback_Data callback_data; uint32_t (*lv2_event_ref)(LV2_Event_Callback_Data callback_data, LV2_Event* event); uint32_t (*lv2_event_unref)(LV2_Event_Callback_Data callback_data, LV2_Event* event); } LV2_Event_Feature; /** URI map feature, for event types (we use only MIDI) */ #define MIDI_EVENT_ID 1 uint32_t uri_to_id(LV2_URI_Map_Callback_Data callback_data, const char* map, const char* uri) { if (!strcmp(map, LV2_EVENT_URI) && !strcmp(uri, SLV2_EVENT_CLASS_MIDI)) return MIDI_EVENT_ID; else return 0; // no id for you! } static LV2_URI_Map_Feature uri_map = { NULL, &uri_to_id }; static const LV2_Feature uri_map_feature = { "http://lv2plug.in/ns/ext/uri-map", &uri_map }; /** We don't support type 0 events, so the ref and unref functions just point to the same empty function. */ uint32_t event_ref_func(LV2_Event_Callback_Data callback_data, LV2_Event* event) { return 0; } static LV2_Event_Feature event_ref = { NULL, &event_ref_func, &event_ref_func }; static const LV2_Feature event_ref_feature = { "http://lv2plug.in/ns/ext/event", &event_ref }; const LV2_Feature* features[3] = { &uri_map_feature, &event_ref_feature, NULL }; static gboolean port_is_a (SLV2Plugin plugin, SLV2Port port, SLV2Value value) { SLV2Values port_classes = slv2_port_get_classes (plugin, port); gint i, num_classes = slv2_values_size (port_classes); const gchar *value_string = slv2_value_as_string (value); SLV2Value class_value; for (i = 0; i < num_classes; i++) { class_value = slv2_values_get_at (port_classes, i); if (!strcmp (slv2_value_as_string (class_value), value_string)) return TRUE; } return FALSE; } static gpointer create_buffer_for_port (SLV2Plugin plugin, gint port_num) { SLV2Port port; SLV2Value def, min, max; LV2_Event_Buffer *event_buffer; gfloat *buffer = NULL; gint i; port = slv2_plugin_get_port_by_index (plugin, port_num); if (port_is_a (plugin, port, audio_class)) { buffer = g_new (float, NUM_SAMPLES); for (i = 0; i < NUM_SAMPLES; i++) buffer[i] = 0.0F; } else if (port_is_a (plugin, port, control_class)) { buffer = g_new (float, 1); *buffer = 0.0f; slv2_port_get_range (plugin, port, &def, &min, &max); if (def) { *buffer = slv2_value_as_float (def); slv2_value_free (def); } if (min) slv2_value_free (min); if (max) slv2_value_free (max); } else if (port_is_a (plugin, port, event_class)) { buffer = malloc (sizeof (LV2_Event_Buffer) + EVENT_BUFFER_CAPACITY); event_buffer = (LV2_Event_Buffer*) buffer; event_buffer->capacity = EVENT_BUFFER_CAPACITY; event_buffer->header_size = sizeof (LV2_Event_Buffer); event_buffer->data = (uint8_t*) buffer + event_buffer->header_size; event_buffer->stamp_type = LV2_EVENT_AUDIO_STAMP; event_buffer->event_count = 0; event_buffer->size = 0; } else g_print ("WARNING: Unknown port signal type. Skipping plugin.\n"); return buffer; } static void test_lv2_plugin (SLV2Plugin plugin) { SLV2Value uri_value, name_value, plugin_class_value; const gchar *uri, *name, *category; SLV2PluginClass plugin_class; SLV2Instance instance; GList *buffers = NULL, *elem; gpointer buffer; gint num_ports, port_num; gboolean run_test = TRUE; uri_value = slv2_plugin_get_uri (plugin); uri = slv2_value_as_string (uri_value); if (plugin_uri && strcmp (uri, plugin_uri)) return; if (uri_prefix && strncmp (uri, uri_prefix, uri_prefix_len)) return; #if 1 g_print ("###############################################################################\n"); #endif name_value = slv2_plugin_get_name (plugin); name = slv2_value_as_string (name_value); #if 1 g_print ("Testing %s (%s)\n", name, uri); #endif slv2_value_free (name_value); plugin_class = slv2_plugin_get_class (plugin); plugin_class_value = slv2_plugin_class_get_label (plugin_class); category = slv2_value_as_string (plugin_class_value); /* Instantiate the plugin. */ instance = slv2_plugin_instantiate (plugin, SAMPLE_RATE, features); if (!instance) { g_print ("ERROR: Unable to instantiate plugin: %s\n", uri); return; } /* Connect up the ports. */ num_ports = slv2_plugin_get_num_ports (plugin); for (port_num = 0; port_num < num_ports; port_num++) { buffer = create_buffer_for_port (plugin, port_num); if (buffer) { #if 0 g_print ("Connecting port: %i to: %p\n", port_num, buffer); #endif slv2_instance_connect_port (instance, port_num, buffer); buffers = g_list_prepend (buffers, buffer); } else { run_test = FALSE; break; } } if (run_test) { /* Activate it. */ slv2_instance_activate (instance); /* Run it. */ slv2_instance_run (instance, NUM_SAMPLES); /* Deactivate it. */ slv2_instance_deactivate (instance); } /* Free the buffers. */ for (elem = buffers; elem; elem = elem->next) g_free (elem->data); g_list_free (buffers); /* Free it. */ slv2_instance_free (instance); } static void test_lv2_plugins (void) { SLV2World slv2_world; SLV2Plugins slv2_plugins; SLV2Plugin plugin; gint num_plugins, i; slv2_world = slv2_world_new (); audio_class = slv2_value_new_uri (slv2_world, SLV2_PORT_CLASS_AUDIO); control_class = slv2_value_new_uri (slv2_world, SLV2_PORT_CLASS_CONTROL); event_class = slv2_value_new_uri (slv2_world, SLV2_PORT_CLASS_EVENT); slv2_world_load_all (slv2_world); slv2_plugins = slv2_world_get_all_plugins (slv2_world); num_plugins = slv2_plugins_size (slv2_plugins); for (i = 0; i < num_plugins; i++) { plugin = slv2_plugins_get_at (slv2_plugins, i); test_lv2_plugin (plugin); } } /* Command-line options. */ static GOptionEntry entries[] = { { "package", 'p', 0, G_OPTION_ARG_STRING, &package, "Package to test (one of calf, ll or swh)", "PACKAGE" }, { "uri", 'u', 0, G_OPTION_ARG_STRING, &plugin_uri, "URI of plugin to test", "URI" }, { NULL } }; int main (int argc, char *argv[]) { GOptionContext *context; GError *error = NULL; context = g_option_context_new ("- test LV2 plugins"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_print ("option parsing failed: %s\n", error->message); exit (1); } /* If a package is specified, set the appropriate uri prefix to match. */ if (package) { if (!strcmp (package, "calf")) uri_prefix = "http://calf.sourceforge.net/"; else if (!strcmp (package, "ll")) uri_prefix = "http://ll-plugins.nongnu.org/lv2/"; else if (!strcmp (package, "swh")) uri_prefix = "http://plugin.org.uk/swh-plugins/"; else { g_print ("unknown package: %s\n", package); exit (1); } uri_prefix_len = strlen (uri_prefix); } test_lv2_plugins (); return 0; }