On Thu, 2009-07-30 at 11:27 +0100, james morris wrote:

> Are there any similar testing tools for LV2 plugins?
> 
> Although I'm familiar with gdb and valgrind, I don't know how to go
> about using these with plugins, and I guess/assume a simple host
> (perhaps one specifically designed for testing) would be best for doing
> this? Are there any existing tools?

I've done a quick port of my little test app to LV2. I've attached it.
We could add the various control port input tests from demolition later.

My first test results:

calf - memory errors in 2 of the small plugins.
       (plus the organ had an unknown port type so was skipped)
ll   - OK, except 3 of the plugins couldn't be instantiated.
       2 seem to have incorrect URLs in the cpp files.
       Not sure about the other one.
swh  - crashes (a known bug actually).


Damon

/*
 * 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 <drobilla.net>
 */
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <slv2/slv2.h>

/* 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;
}
_______________________________________________
Linux-audio-dev mailing list
[email protected]
http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev

Reply via email to