diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | utils/bench.h | 52 | ||||
-rw-r--r-- | utils/lv2bench.c | 179 | ||||
-rw-r--r-- | utils/uri_table.h | 73 | ||||
-rw-r--r-- | wscript | 48 |
5 files changed, 338 insertions, 15 deletions
@@ -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 */ @@ -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) |