summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--utils/bench.h52
-rw-r--r--utils/lv2bench.c179
-rw-r--r--utils/uri_table.h73
-rw-r--r--wscript48
5 files changed, 338 insertions, 15 deletions
diff --git a/NEWS b/NEWS
index 2b7933c..f16148b 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ lilv (9999) unstable;
* Fix crash when lv2info is run with an invalid URI argument
* Gracefully handle failure to save plugin state and print error message
* Reduce memory usage (per node)
+ * Add lv2bench utility
-- David Robillard <d@drobilla.net>
diff --git a/utils/bench.h b/utils/bench.h
new file mode 100644
index 0000000..948622c
--- /dev/null
+++ b/utils/bench.h
@@ -0,0 +1,52 @@
+/*
+ Copyright 2011-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file bench.h A simple real-time benchmarking API.
+*/
+
+#ifndef BENCH_H
+#define BENCH_H
+
+#define _POSIX_C_SOURCE 199309L
+
+#include <time.h>
+#include <sys/time.h>
+
+static inline double
+bench_elapsed_s(const struct timespec* start, const struct timespec* end)
+{
+ return ((end->tv_sec - start->tv_sec)
+ + ((end->tv_nsec - start->tv_nsec) * 0.000000001));
+}
+
+static inline struct timespec
+bench_start()
+{
+ struct timespec start_t;
+ clock_gettime(CLOCK_REALTIME, &start_t);
+ return start_t;
+}
+
+static inline double
+bench_end(const struct timespec* start_t)
+{
+ struct timespec end_t;
+ clock_gettime(CLOCK_REALTIME, &end_t);
+ return bench_elapsed_s(start_t, &end_t);
+}
+
+#endif /* BENCH_H */
diff --git a/utils/lv2bench.c b/utils/lv2bench.c
new file mode 100644
index 0000000..3335b3a
--- /dev/null
+++ b/utils/lv2bench.c
@@ -0,0 +1,179 @@
+/*
+ Copyright 2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define _POSIX_C_SOURCE 199309L
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv/lilv.h"
+
+#include "lilv_config.h"
+#include "bench.h"
+#include "uri_table.h"
+
+#define BLOCK_LENGTH 512
+
+static LilvNode* lv2_AudioPort = NULL;
+static LilvNode* lv2_CVPort = NULL;
+static LilvNode* lv2_ControlPort = NULL;
+static LilvNode* lv2_InputPort = NULL;
+static LilvNode* lv2_OutputPort = NULL;
+static LilvNode* urid_map = NULL;
+
+static void
+print_version(void)
+{
+ printf(
+ "lv2bench (lilv) " LILV_VERSION "\n"
+ "Copyright 2012 David Robillard <http://drobilla.net>\n"
+ "License: <http://www.opensource.org/licenses/isc-license>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n");
+}
+
+static void
+print_usage(void)
+{
+ printf("Usage: lv2bench\n");
+ printf("Benchmark all installed and supported LV2 plugins.\n");
+}
+
+static double
+bench(const LilvPlugin* p, uint32_t sample_count)
+{
+ float in[BLOCK_LENGTH];
+ float out[BLOCK_LENGTH];
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ 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 };
+
+ 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));
+ 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)));
+ 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);
+ return 0.0;
+ }
+ } else {
+ fprintf(stderr, "<%s> port %d has unknown type, skipping\n",
+ uri, index);
+ lilv_instance_free(instance);
+ return 0.0;
+ }
+ }
+
+ lilv_instance_activate(instance);
+
+ struct timespec ts = bench_start();
+ for (uint32_t i = 0; i < (sample_count / BLOCK_LENGTH); ++i) {
+ lilv_instance_run(instance, BLOCK_LENGTH);
+ }
+ const double elapsed = bench_end(&ts);
+
+ lilv_instance_deactivate(instance);
+ lilv_instance_free(instance);
+
+ uri_table_destroy(&uri_table);
+
+ printf("%lf %s\n", elapsed, uri);
+ return elapsed;
+}
+
+int
+main(int argc, char** argv)
+{
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--version")) {
+ print_version();
+ return 0;
+ } else if (!strcmp(argv[i], "--help")) {
+ print_usage();
+ return 0;
+ } else {
+ print_usage();
+ return 1;
+ }
+ }
+
+ LilvWorld* world = lilv_world_new();
+ lilv_world_load_all(world);
+
+ lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort);
+ lv2_CVPort = lilv_new_uri(world, LV2_CORE__CVPort);
+ lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort);
+ lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort);
+ lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort);
+ urid_map = lilv_new_uri(world, LV2_URID__map);
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ LILV_FOREACH(plugins, i, plugins) {
+ bench(lilv_plugins_get(plugins, i), 1 << 19);
+ }
+
+ lilv_node_free(urid_map);
+ lilv_node_free(lv2_OutputPort);
+ lilv_node_free(lv2_InputPort);
+ lilv_node_free(lv2_ControlPort);
+ lilv_node_free(lv2_CVPort);
+ lilv_node_free(lv2_AudioPort);
+
+ lilv_world_free(world);
+
+ return 0;
+}
diff --git a/utils/uri_table.h b/utils/uri_table.h
new file mode 100644
index 0000000..6a27c61
--- /dev/null
+++ b/utils/uri_table.h
@@ -0,0 +1,73 @@
+/*
+ Copyright 2011-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file uri_table.h A toy URI map/unmap implementation.
+
+ This file contains function definitions and must only be included once.
+*/
+
+#ifndef URI_TABLE_H
+#define URI_TABLE_H
+
+typedef struct {
+ char** uris;
+ size_t n_uris;
+} URITable;
+
+static void
+uri_table_init(URITable* table)
+{
+ table->uris = NULL;
+ table->n_uris = 0;
+}
+
+static void
+uri_table_destroy(URITable* table)
+{
+ free(table->uris);
+}
+
+static LV2_URID
+uri_table_map(LV2_URID_Map_Handle handle,
+ const char* uri)
+{
+ URITable* table = (URITable*)handle;
+ for (size_t i = 0; i < table->n_uris; ++i) {
+ if (!strcmp(table->uris[i], uri)) {
+ return i + 1;
+ }
+ }
+
+ const size_t len = strlen(uri);
+ table->uris = (char**)realloc(table->uris, ++table->n_uris * sizeof(char*));
+ table->uris[table->n_uris - 1] = malloc(len + 1);
+ memcpy(table->uris[table->n_uris - 1], uri, len + 1);
+ return table->n_uris;
+}
+
+static const char*
+uri_table_unmap(LV2_URID_Map_Handle handle,
+ LV2_URID urid)
+{
+ URITable* table = (URITable*)handle;
+ if (urid > 0 && urid <= table->n_uris) {
+ return table->uris[urid - 1];
+ }
+ return NULL;
+}
+
+#endif /* URI_TABLE_H */
diff --git a/wscript b/wscript
index cccdb4e..b7183f4 100644
--- a/wscript
+++ b/wscript
@@ -112,6 +112,14 @@ def configure(conf):
define_name='HAVE_FILENO',
mandatory=False)
+ conf.check_cc(function_name='clock_gettime',
+ header_name=['sys/time.h','time.h'],
+ defines=['_POSIX_C_SOURCE=199309L'],
+ define_name='HAVE_CLOCK_GETTIME',
+ uselib_store='CLOCK_GETTIME',
+ lib=['rt'],
+ mandatory=False)
+
autowaf.define(conf, 'LILV_VERSION', LILV_VERSION)
if Options.options.dyn_manifest:
autowaf.define(conf, 'LILV_DYN_MANIFEST', 1)
@@ -165,6 +173,23 @@ def configure(conf):
conf.undefine('LILV_DEFAULT_LV2_PATH') # Cmd line errors with VC++
print('')
+def build_util(bld, name, defines):
+ obj = bld(features = 'c cprogram',
+ source = name + '.c',
+ includes = ['.', './src', './utils'],
+ use = 'liblilv',
+ target = name,
+ defines = defines,
+ install_path = '${BINDIR}')
+ if not bld.env.BUILD_SHARED or bld.env.STATIC_PROGS:
+ obj.use = 'liblilv_static'
+ if bld.env.STATIC_PROGS:
+ if not bld.env.MSVC_COMPILER:
+ obj.lib = ['m']
+ obj.env.SHLIB_MARKER = obj.env.STLIB_MARKER
+ obj.linkflags = ['-static', '-Wl,--start-group']
+ return obj
+
def build(bld):
# C/C++ Headers
includedir = '${INCLUDEDIR}/lilv-%s/lilv' % LILV_MAJOR_VERSION
@@ -296,25 +321,18 @@ def build(bld):
# Utilities
if bld.env.BUILD_UTILS:
utils = '''
+ utils/lilv-bench
utils/lv2info
utils/lv2ls
- utils/lilv-bench
'''
for i in utils.split():
- obj = bld(features = 'c cprogram',
- source = i + '.c',
- includes = ['.', './src', './utils'],
- use = 'liblilv',
- target = i,
- defines = defines,
- install_path = '${BINDIR}')
- if not bld.env.BUILD_SHARED or bld.env.STATIC_PROGS:
- obj.use = 'liblilv_static'
- if bld.env.STATIC_PROGS:
- if not bld.env.MSVC_COMPILER:
- obj.lib = ['m']
- obj.env.SHLIB_MARKER = obj.env.STLIB_MARKER
- obj.linkflags = ['-static', '-Wl,--start-group']
+ build_util(bld, i, defines)
+
+ # lv2bench (less portable than other utilities)
+ if bld.is_defined('HAVE_CLOCK_GETTIME'):
+ obj = build_util(bld, 'utils/lv2bench', defines)
+ if not bld.env.MSVC_COMPILER:
+ obj.lib = ['rt']
# Documentation
autowaf.build_dox(bld, 'LILV', LILV_VERSION, top, out)