From 55dc97a32eeeb42012ce1e7d28ed99ea537f4d2f Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 14 Jul 2022 11:50:43 -0400 Subject: Switch to meson build system --- .gitignore | 2 - .gitmodules | 3 - INSTALL | 66 --- INSTALL.md | 70 ++++ NEWS | 3 +- bindings/python/meson.build | 9 + doc/c/Doxyfile | 26 -- doc/c/Doxyfile.in | 28 ++ doc/c/api/meson.build | 6 + doc/c/meson.build | 47 +++ doc/c/wscript | 43 -- doc/c/xml/meson.build | 18 + doc/meson.build | 19 + lilv.pc.in | 11 - meson.build | 201 +++++++++ meson/library/meson.build | 31 ++ meson/suppressions/meson.build | 102 +++++ meson/warnings/meson.build | 175 ++++++++ meson_options.txt | 17 + src/filesystem.c | 8 - src/util.c | 1 - test/bad_syntax.lv2/meson.build | 25 ++ test/core.lv2/lv2core.ttl | 30 -- test/core.lv2/manifest.ttl | 9 - test/failed_instantiation.lv2/meson.build | 25 ++ test/failed_lib_descriptor.lv2/meson.build | 25 ++ test/lib_descriptor.lv2/meson.build | 25 ++ test/lilv_test_uri_map.h | 9 +- test/lilv_test_utils.c | 4 +- test/lv2/core.lv2/lv2core.ttl | 30 ++ test/lv2/core.lv2/manifest.ttl | 9 + test/lv2/core.lv2/meson.build | 8 + test/meson.build | 80 ++++ test/missing_descriptor.lv2/meson.build | 25 ++ test/missing_name.lv2/meson.build | 25 ++ test/missing_plugin.lv2/meson.build | 25 ++ test/missing_port.lv2/meson.build | 25 ++ test/missing_port_name.lv2/meson.build | 25 ++ test/new_version.lv2/meson.build | 17 + test/old_version.lv2/meson.build | 17 + test/test.lv2/manifest.ttl.in | 7 - test/test.lv2/test.c | 483 ---------------------- test/test.lv2/test.ttl.in | 46 --- test/test_plugin.lv2/manifest.ttl.in | 7 + test/test_plugin.lv2/meson.build | 17 + test/test_plugin.lv2/test_plugin.c | 481 +++++++++++++++++++++ test/test_plugin.lv2/test_plugin.ttl.in | 46 +++ test/test_string.c | 2 - tools/bench.h | 51 +++ tools/lilv.bash_completion | 59 +++ tools/lv2apply.c | 368 +++++++++++++++++ tools/lv2bench.c | 282 +++++++++++++ tools/lv2info.c | 456 ++++++++++++++++++++ tools/lv2ls.c | 94 +++++ tools/meson.build | 83 ++++ tools/uri_table.h | 80 ++++ utils/bench.h | 53 --- utils/lilv-bench.c | 34 -- utils/lilv.bash_completion | 59 --- utils/lv2apply.c | 368 ----------------- utils/lv2bench.c | 284 ------------- utils/lv2info.c | 456 -------------------- utils/lv2ls.c | 94 ----- utils/uri_table.h | 80 ---- waf | 27 -- waflib | 1 - wscript | 643 ----------------------------- 67 files changed, 3140 insertions(+), 2845 deletions(-) delete mode 100644 .gitmodules delete mode 100644 INSTALL create mode 100644 INSTALL.md create mode 100644 bindings/python/meson.build delete mode 100644 doc/c/Doxyfile create mode 100644 doc/c/Doxyfile.in create mode 100644 doc/c/api/meson.build create mode 100644 doc/c/meson.build delete mode 100644 doc/c/wscript create mode 100644 doc/c/xml/meson.build create mode 100644 doc/meson.build delete mode 100644 lilv.pc.in create mode 100644 meson.build create mode 100644 meson/library/meson.build create mode 100644 meson/suppressions/meson.build create mode 100644 meson/warnings/meson.build create mode 100644 meson_options.txt create mode 100644 test/bad_syntax.lv2/meson.build delete mode 100644 test/core.lv2/lv2core.ttl delete mode 100644 test/core.lv2/manifest.ttl create mode 100644 test/failed_instantiation.lv2/meson.build create mode 100644 test/failed_lib_descriptor.lv2/meson.build create mode 100644 test/lib_descriptor.lv2/meson.build create mode 100644 test/lv2/core.lv2/lv2core.ttl create mode 100644 test/lv2/core.lv2/manifest.ttl create mode 100644 test/lv2/core.lv2/meson.build create mode 100644 test/meson.build create mode 100644 test/missing_descriptor.lv2/meson.build create mode 100644 test/missing_name.lv2/meson.build create mode 100644 test/missing_plugin.lv2/meson.build create mode 100644 test/missing_port.lv2/meson.build create mode 100644 test/missing_port_name.lv2/meson.build create mode 100644 test/new_version.lv2/meson.build create mode 100644 test/old_version.lv2/meson.build delete mode 100644 test/test.lv2/manifest.ttl.in delete mode 100644 test/test.lv2/test.c delete mode 100644 test/test.lv2/test.ttl.in create mode 100644 test/test_plugin.lv2/manifest.ttl.in create mode 100644 test/test_plugin.lv2/meson.build create mode 100644 test/test_plugin.lv2/test_plugin.c create mode 100644 test/test_plugin.lv2/test_plugin.ttl.in create mode 100644 tools/bench.h create mode 100644 tools/lilv.bash_completion create mode 100644 tools/lv2apply.c create mode 100644 tools/lv2bench.c create mode 100644 tools/lv2info.c create mode 100644 tools/lv2ls.c create mode 100644 tools/meson.build create mode 100644 tools/uri_table.h delete mode 100644 utils/bench.h delete mode 100644 utils/lilv-bench.c delete mode 100644 utils/lilv.bash_completion delete mode 100644 utils/lv2apply.c delete mode 100644 utils/lv2bench.c delete mode 100644 utils/lv2info.c delete mode 100644 utils/lv2ls.c delete mode 100644 utils/uri_table.h delete mode 100755 waf delete mode 160000 waflib delete mode 100644 wscript diff --git a/.gitignore b/.gitignore index 204c242..467d5b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ build/** -.waf-* -.lock-waf* __pycache__ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index cc8b569..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "waflib"] - path = waflib - url = ../../drobilla/autowaf.git diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 9b54f51..0000000 --- a/INSTALL +++ /dev/null @@ -1,66 +0,0 @@ -Installation Instructions -========================= - -Basic Installation ------------------- - -Building this software requires only Python. To install with default options: - - ./waf configure - ./waf - ./waf install # or sudo ./waf install - -Configuration Options ---------------------- - -All supported options can be viewed using the command: - - ./waf --help - -Most options only need to be passed during the configure stage, for example: - - ./waf configure --prefix=/usr - ./waf - ./waf install - -Compiler Configuration ----------------------- - -Several standard environment variables can be used to control how compilers are -invoked: - - * CC: Path to C compiler - * CFLAGS: C compiler options - * CXX: Path to C++ compiler - * CXXFLAGS: C++ compiler options - * CPPFLAGS: C preprocessor options - * LINKFLAGS: Linker options - -Library Versioning ------------------- - -This library uses semantic versioning . - -Several major versions can be installed in parallel. The shared library name, -include directory, and pkg-config file are suffixed with the major version -number. For example, a library named "foo" at version 1.x.y might install: - - /usr/include/foo-1/foo/foo.h - /usr/lib/foo-1.so.1.x.y - /usr/lib/pkgconfig/foo-1.pc - -Dependencies can check for the package "foo-1" with pkg-config. - -Packaging ---------- - -Everything can be installed to a specific root directory by passing a --destdir -option to the install stage (or setting the DESTDIR environment variable), -which adds a prefix to all install paths. For example: - - ./waf configure --prefix=/usr - ./waf - ./waf install --destdir=/tmp/package - -Packages should allow parallel installation of several major versions. For -example, the above would be packaged as "foo-1". \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..7109c35 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,70 @@ +Installation Instructions +========================= + +Prerequisites +------------- + +To build from source, you will need: + + * A relatively modern C compiler (GCC, Clang, and MSVC are known to work). + + * [Meson](http://mesonbuild.com/), which depends on + [Python](http://python.org/). + +This is a brief overview of building this project with meson. See the meson +documentation for more detailed information. + +Configuration +------------- + +The build is configured with the `setup` command, which creates a new build +directory with the given name: + + meson setup build + +Some environment variables are read during `setup` and stored with the +configuration: + + * `CC`: Path to C compiler. + * `CFLAGS`: C compiler options. + * `LDFLAGS`: Linker options. + +However, it is better to use meson options for configuration. All options can +be inspected with the `configure` command from within the build directory: + + cd build + meson configure + +Options can be set by passing C-style "define" options to `configure`: + + meson configure -Dc_args="-march=native" -Dprefix="/opt/mypackage/" + +Building +-------- + +From within a configured build directory, everything can be built with the +`compile` command: + + meson compile + +Similarly, tests can be run with the `test` command: + + meson test + +Meson can also generate a project for several popular IDEs, see the `backend` +option for details. + +Installation +------------ + +A compiled project can be installed with the `install` command: + + meson install + +You may need to acquire root permissions to install to a system-wide prefix. +For packaging, the installation may be staged to a directory using the +`DESTDIR` environment variable or the `--destdir` option: + + DESTDIR=/tmp/mypackage/ meson install + + meson install --destdir=/tmp/mypackage/ diff --git a/NEWS b/NEWS index aa0f786..4ef15c1 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,9 @@ lilv (0.24.15) unstable; * Fix fallback flock() detection on MacOS + * Switch to meson build system - -- David Robillard Fri, 17 Jun 2022 01:12:49 +0000 + -- David Robillard Sat, 09 Jul 2022 01:14:22 +0000 lilv (0.24.14) stable; diff --git a/bindings/python/meson.build b/bindings/python/meson.build new file mode 100644 index 0000000..350b0fe --- /dev/null +++ b/bindings/python/meson.build @@ -0,0 +1,9 @@ +# Copyright 2021-2022 David Robillard +# SPDX-License-Identifier: CC0-1.0 OR ISC + +pymod = import('python') +py = pymod.find_installation('python3', required: get_option('bindings_py')) + +if py.found() + py.install_sources(files('lilv.py')) +endif diff --git a/doc/c/Doxyfile b/doc/c/Doxyfile deleted file mode 100644 index ce10524..0000000 --- a/doc/c/Doxyfile +++ /dev/null @@ -1,26 +0,0 @@ -PROJECT_NAME = Lilv -PROJECT_BRIEF = "A library for simple use of LV2 plugins" - -QUIET = YES -WARN_AS_ERROR = NO -WARN_IF_UNDOCUMENTED = NO -WARN_NO_PARAMDOC = NO - -JAVADOC_AUTOBRIEF = YES - -CASE_SENSE_NAMES = YES -HIDE_IN_BODY_DOCS = YES -REFERENCES_LINK_SOURCE = NO - -GENERATE_HTML = NO -GENERATE_LATEX = NO -GENERATE_XML = YES -XML_PROGRAMLISTING = NO -SHOW_FILES = NO - -MACRO_EXPANSION = YES -PREDEFINED = LILV_API LILV_DEPRECATED - -INPUT = ../../include/lilv/lilv.h - -OUTPUT_DIRECTORY = . diff --git a/doc/c/Doxyfile.in b/doc/c/Doxyfile.in new file mode 100644 index 0000000..70abb39 --- /dev/null +++ b/doc/c/Doxyfile.in @@ -0,0 +1,28 @@ +PROJECT_NAME = Lilv +PROJECT_BRIEF = "A library for simple use of LV2 plugins" + +QUIET = YES +WARN_AS_ERROR = NO +WARN_IF_UNDOCUMENTED = NO +WARN_NO_PARAMDOC = NO + +JAVADOC_AUTOBRIEF = YES + +CASE_SENSE_NAMES = YES +HIDE_IN_BODY_DOCS = YES +REFERENCES_LINK_SOURCE = NO + +GENERATE_HTML = NO +GENERATE_LATEX = NO +GENERATE_XML = YES +XML_PROGRAMLISTING = NO +SHOW_FILES = NO + +MACRO_EXPANSION = YES +PREDEFINED = LILV_API LILV_DEPRECATED + +RECURSIVE = YES +STRIP_FROM_PATH = @LILV_SRCDIR@ +INPUT = @LILV_SRCDIR@/include + +OUTPUT_DIRECTORY = @DOX_OUTPUT@ diff --git a/doc/c/api/meson.build b/doc/c/api/meson.build new file mode 100644 index 0000000..4717825 --- /dev/null +++ b/doc/c/api/meson.build @@ -0,0 +1,6 @@ +c_lilv_rst = custom_target( + 'lilv.rst', + command: [dox_to_sphinx, '-f', '@INPUT0@', '@OUTDIR@'], + input: [c_index_xml] + c_rst_files, + output: 'lilv.rst', +) diff --git a/doc/c/meson.build b/doc/c/meson.build new file mode 100644 index 0000000..f75dc74 --- /dev/null +++ b/doc/c/meson.build @@ -0,0 +1,47 @@ +config = configuration_data() +config.set('LILV_VERSION', meson.project_version()) + +conf_py = configure_file(configuration: config, + input: files('../conf.py.in'), + output: 'conf.py') + +configure_file(copy: true, + input: files('../summary.rst'), + output: 'summary.rst') + +c_rst_files = files( + 'index.rst', + 'overview.rst', + 'plugins.rst', + 'uis.rst', + 'world.rst', +) + +foreach f : c_rst_files + configure_file(copy: true, input: f, output: '@PLAINNAME@') +endforeach + +subdir('xml') +subdir('api') + +docs = custom_target( + 'singlehtml', + build_by_default: true, + command: [sphinx_build, '-M', 'singlehtml', '@OUTDIR@', '@OUTDIR@', + '-E', '-q', '-t', 'singlehtml'], + input: [c_rst_files, c_lilv_rst, c_index_xml], + install: true, + install_dir: docdir / 'lilv-0', + output: 'singlehtml', +) + +docs = custom_target( + 'html', + build_by_default: true, + command: [sphinx_build, '-M', 'html', '@OUTDIR@', '@OUTDIR@', + '-E', '-q', '-t', 'html'], + input: [c_rst_files, c_lilv_rst, c_index_xml], + install: true, + install_dir: docdir / 'lilv-0', + output: 'html', +) diff --git a/doc/c/wscript b/doc/c/wscript deleted file mode 100644 index d515ddf..0000000 --- a/doc/c/wscript +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python - -def build(bld): - dox_to_sphinx = bld.path.find_node("../../scripts/dox_to_sphinx.py") - index_xml = bld.path.get_bld().make_node("xml/index.xml") - - files = [ - ("../summary.rst", "sphinx/summary.rst"), - ("index.rst", "sphinx/index.rst"), - ("overview.rst", "sphinx/overview.rst"), - ("plugins.rst", "sphinx/plugins.rst"), - ("uis.rst", "sphinx/uis.rst"), - ("world.rst", "sphinx/world.rst"), - ] - - # Run Doxygen to generate XML documentation - bld(features="doxygen", doxyfile="Doxyfile") - - # Substitute variables to make Sphinx configuration file - bld(features="subst", - source="../conf.py.in", - target="sphinx/conf.py", - LILV_VERSION=bld.env.LILV_VERSION) - - # Copy static documentation files to Sphinx build directory - for f in files: - bld(features="subst", is_copy=True, source=f[0], target=f[1]) - - # Generate Sphinx markup from Doxygen XML - bld.add_group() - bld(rule="${PYTHON} " + dox_to_sphinx.abspath() + " -f ${SRC} ${TGT}", - source=index_xml, - target="sphinx/api/") - - doc_dir = bld.env.DOCDIR + "/lilv-%s/" % bld.env.LILV_MAJOR_VERSION - - # Run Sphinx to generate HTML documentation - for builder in ["html", "singlehtml"]: - bld(features="sphinx", - sphinx_source=bld.path.get_bld().make_node("sphinx"), - sphinx_output_format=builder, - sphinx_options=["-E", "-q", "-t", builder], - install_path=doc_dir + "c/%s/" % builder) diff --git a/doc/c/xml/meson.build b/doc/c/xml/meson.build new file mode 100644 index 0000000..6de27f9 --- /dev/null +++ b/doc/c/xml/meson.build @@ -0,0 +1,18 @@ +doxygen = find_program('doxygen') + +config = configuration_data() +config.set('LILV_SRCDIR', lilv_src_root) +config.set('DOX_OUTPUT', meson.current_build_dir() / '..') + +c_doxyfile = configure_file( + configuration: config, + input: files('../Doxyfile.in'), + output: 'Doxyfile', +) + +c_index_xml = custom_target( + 'index.xml', + command: [doxygen, '@INPUT0@'], + input: [c_doxyfile] + c_headers, + output: 'index.xml', +) diff --git a/doc/meson.build b/doc/meson.build new file mode 100644 index 0000000..efa9d71 --- /dev/null +++ b/doc/meson.build @@ -0,0 +1,19 @@ +# Copyright 2021-2022 David Robillard +# SPDX-License-Identifier: CC0-1.0 OR ISC + +docdir = get_option('datadir') / 'doc' + +doxygen = find_program('doxygen', required: get_option('docs')) +dox_to_sphinx = files('../scripts/dox_to_sphinx.py') +sphinx_build = find_program('sphinx-build', required: get_option('docs')) + +build_docs = doxygen.found() and sphinx_build.found() + +if build_docs + subdir('c') +endif + +if not meson.is_subproject() + summary('Documentation', build_docs, bool_yn: true) +endif + diff --git a/lilv.pc.in b/lilv.pc.in deleted file mode 100644 index 97f4f8c..0000000 --- a/lilv.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@PREFIX@ -exec_prefix=@EXEC_PREFIX@ -libdir=@LIBDIR@ -includedir=@INCLUDEDIR@ - -Name: Lilv -Version: @LILV_VERSION@ -Description: Simple C library for hosting LV2 plugins -Requires: @LILV_PKG_DEPS@ -Libs: -L${libdir} -l@LIB_LILV@ @LILV_PKG_LIBS@ -Cflags: -I${includedir}/lilv-@LILV_MAJOR_VERSION@ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..f36873b --- /dev/null +++ b/meson.build @@ -0,0 +1,201 @@ +# Copyright 2021-2022 David Robillard +# SPDX-License-Identifier: CC0-1.0 OR ISC + +project('lilv', ['c'], + version: '0.24.15', + license: 'ISC', + meson_version: '>= 0.56.0', + default_options: [ + 'b_ndebug=if-release', + 'buildtype=release', + 'c_std=c99', + ]) + +lilv_src_root = meson.current_source_dir() +major_version = meson.project_version().split('.')[0] +version_suffix = '-@0@'.format(major_version) +versioned_name = 'lilv' + version_suffix + +####################### +# Compilers and Flags # +####################### + +# Load build tools +pkg = import('pkgconfig') +cc = meson.get_compiler('c') + +# Set global warning flags +if get_option('strict') and not meson.is_subproject() + subdir('meson/warnings') +endif +subdir('meson/suppressions') + +########################## +# Platform Configuration # +########################## + +platform_defines = ['-DLILV_VERSION="@0@"'.format(meson.project_version())] +if host_machine.system() == 'darwin' + platform_defines += [ + '-D_DARWIN_C_SOURCE', + ] +elif host_machine.system() in ['gnu', 'linux'] + platform_defines += [ + '-D_DEFAULT_SOURCE', + '-D_POSIX_C_SOURCE=200809L', + ] +endif + +add_project_arguments(platform_defines, language: ['c']) + +################ +# Dependencies # +################ + +m_dep = cc.find_library('m', required: false) +dl_dep = cc.find_library('dl', required: false) + +lv2_dep = dependency('lv2', + version: '>= 1.18.2', + fallback: ['lv2', 'lv2_dep']) + +serd_dep = dependency('serd-0', + version: '>= 0.30.9', + fallback: ['serd', 'serd_dep']) + +sord_dep = dependency('sord-0', + version: '>= 0.16.9', + fallback: ['sord', 'sord_dep']) + +sratom_dep = dependency('sratom-0', + version: '>=0.6.9', + fallback: ['sratom', 'sratom_dep']) + +########### +# Library # +########### + +c_headers = files('include/lilv/lilv.h') +cpp_headers = files('include/lilv/lilvmm.hpp') + +sources = files( + 'src/collections.c', + 'src/filesystem.c', + 'src/instance.c', + 'src/lib.c', + 'src/node.c', + 'src/plugin.c', + 'src/pluginclass.c', + 'src/port.c', + 'src/query.c', + 'src/scalepoint.c', + 'src/state.c', + 'src/ui.c', + 'src/util.c', + 'src/world.c', + 'src/zix/tree.c', +) + +# Set appropriate arguments for building against the library type +extra_c_args = [] +subdir('meson/library') +if get_option('default_library') == 'static' + extra_c_args = ['-DLILV_STATIC'] +endif + +# Build main shared and/or static library +liblilv = library( + meson.project_name() + library_suffix, + sources, + c_args: c_suppressions + extra_c_args + ['-DLILV_INTERNAL', '-DZIX_STATIC'], + dependencies: [m_dep, dl_dep, lv2_dep, serd_dep, sord_dep, sratom_dep], + gnu_symbol_visibility: 'hidden', + include_directories: include_directories('include', 'src'), + install: true, + version: meson.project_version(), +) + +# Declare dependency for internal meson dependants +lilv_dep = declare_dependency( + compile_args: extra_c_args, + dependencies: [m_dep, dl_dep, lv2_dep, serd_dep, sord_dep, sratom_dep], + include_directories: include_directories('include'), + link_with: liblilv, +) + +# Generage pkg-config file for external dependants +pkg.generate( + liblilv, + description: 'Library for hosting LV2 plugins', + extra_cflags: extra_c_args, + filebase: versioned_name, + name: 'Lilv', + subdirs: [versioned_name], + version: meson.project_version(), +) + +# Install headers to a versioned include directory +install_headers(c_headers, subdir: versioned_name / 'lilv') +install_headers(cpp_headers, subdir: versioned_name / 'lilv') + +######### +# Tools # +######### + +subdir('tools') + +############ +# Bindings # +############ + +subdir('bindings/python') + +########### +# Support # +########### + +if not get_option('docs').disabled() + subdir('doc') +endif + +if not get_option('tests').disabled() + # Get or build a static library for linking internal tests + if get_option('default_library') == 'both' + liblilv_static = liblilv.get_static_lib() + elif get_option('default_library') == 'shared' + liblilv_static = static_library( + meson.project_name() + library_suffix, + sources, + include_directories: include_directories('include', 'src'), + c_args: c_suppressions + ['-DLILV_INTERNAL', '-DLILV_STATIC', '-DZIX_STATIC'], + dependencies: [m_dep, dl_dep, lv2_dep, serd_dep, sord_dep, sratom_dep], + gnu_symbol_visibility: 'default') + else + liblilv_static = liblilv + endif + + lilv_static_dep = declare_dependency( + compile_args: extra_c_args, + dependencies: [m_dep, dl_dep, lv2_dep, serd_dep, sord_dep, sratom_dep], + include_directories: include_directories('include'), + link_with: liblilv_static, + ) + + # Build and run tests against static library + subdir('test') +endif + +if not meson.is_subproject() + summary('Tests', not get_option('tests').disabled(), bool_yn: true) + summary('Tools', not get_option('tools').disabled(), bool_yn: true) + + summary('Install prefix', get_option('prefix')) + + summary('Headers', get_option('prefix') / get_option('includedir')) + summary('Libraries', get_option('prefix') / get_option('libdir')) + + if not get_option('tools').disabled() + summary('Executables', get_option('prefix') / get_option('bindir')) + summary('Man pages', get_option('prefix') / get_option('mandir')) + endif +endif diff --git a/meson/library/meson.build b/meson/library/meson.build new file mode 100644 index 0000000..fffc831 --- /dev/null +++ b/meson/library/meson.build @@ -0,0 +1,31 @@ +# Copyright 2020-2022 David Robillard +# SPDX-License-Identifier: CC0-1.0 OR ISC + +# General definitions for building libraries. +# +# These are essentially workarounds for Meson/Windows/MSVC. Unfortunately, +# Meson's default_library option doesn't support shared and static builds very +# well. In particular, it's often necessary to define different symbols for +# static and shared builds of libraries so that symbols can be exported. To +# work around this, default_library=both isn't supported on Windows. On other +# platforms with GCC-like compilers, we can support both because symbols can +# safely be exported in the same way (giving them default visibility) in both +# static and shared builds. + +default_library = get_option('default_library') +host_system = host_machine.system() + +# Abort on Windows with default_library=both +if host_system == 'windows' and default_library == 'both' + error('default_library=both is not supported on Windows') +endif + +# Set library_suffix to the suffix for libraries +if host_system == 'windows' and default_library == 'shared' + # Meson appends a version to the name only for DLLs, which leads to + # inconsistent library names, like `mylib-1-1`. So, provide no suffix to + # ultimately get the same name as on other platforms, like `mylib-1`. + library_suffix = '' +else + library_suffix = '-@0@'.format(meson.project_version().split('.')[0]) +endif diff --git a/meson/suppressions/meson.build b/meson/suppressions/meson.build new file mode 100644 index 0000000..d0593f2 --- /dev/null +++ b/meson/suppressions/meson.build @@ -0,0 +1,102 @@ +# Copyright 2020-2022 David Robillard +# SPDX-License-Identifier: CC0-1.0 OR ISC + +# Project-specific warning suppressions. +# +# This should be used in conjunction with the generic "warnings" sibling that +# enables all reasonable warnings for the compiler. It lives here just to keep +# the top-level meson.build more readable. + +##### +# C # +##### + +if is_variable('cc') + c_suppressions = [] + + if get_option('strict') + if cc.get_id() == 'clang' + c_suppressions += [ + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-declaration-after-statement', + '-Wno-documentation-unknown-command', + '-Wno-double-promotion', + '-Wno-float-equal', + '-Wno-format-nonliteral', + '-Wno-implicit-float-conversion', + '-Wno-implicit-int-conversion', + '-Wno-nullability-extension', + '-Wno-nullable-to-nonnull-conversion', + '-Wno-padded', + '-Wno-reserved-id-macro', + '-Wno-shorten-64-to-32', + '-Wno-sign-conversion', + '-Wno-switch-enum', + '-Wno-vla', + ] + + if host_machine.system() == 'darwin' + c_suppressions += [ + '-Wno-unused-macros', + ] + elif host_machine.system() == 'freebsd' + c_suppressions += [ + '-Wno-c11-extensions', + ] + endif + + elif cc.get_id() == 'gcc' + c_suppressions += [ + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-conversion', + '-Wno-double-promotion', + '-Wno-float-equal', + '-Wno-format-nonliteral', + '-Wno-format-truncation', + '-Wno-inline', + '-Wno-padded', + '-Wno-stack-protector', + '-Wno-strict-overflow', + '-Wno-suggest-attribute=const', + '-Wno-suggest-attribute=pure', + '-Wno-switch-default', + '-Wno-switch-enum', + '-Wno-unsuffixed-float-constants', + '-Wno-unused-const-variable', + '-Wno-unused-parameter', + '-Wno-vla', + ] + + if host_machine.system() =='windows' + c_suppressions += [ + '-Wno-bad-function-cast', + '-Wno-unused-macros', + ] + endif + + elif cc.get_id() == 'msvc' + c_suppressions += [ + '/wd4061', # enumerator in switch is not explicitly handled + '/wd4090', # different const qualifiers + '/wd4191', # unsafe conversion from FARPROC + '/wd4244', # conversion from floating point, possible loss of data + '/wd4267', # conversion from size_t, possible loss of data + '/wd4365', # signed/unsigned mismatch + '/wd4514', # unreferenced inline function has been removed + '/wd4706', # assignment within conditional expression + '/wd4710', # function not inlined + '/wd4711', # function selected for automatic inline expansion + '/wd4774', # format string is not a string literal + '/wd4800', # implicit conversion to bool + '/wd4820', # padding added after construct + '/wd4996', # POSIX name for this item is deprecated + '/wd5045', # will insert Spectre mitigation for memory load + ] + endif + + endif + + c_suppressions = cc.get_supported_arguments(c_suppressions) +endif diff --git a/meson/warnings/meson.build b/meson/warnings/meson.build new file mode 100644 index 0000000..4d23ad3 --- /dev/null +++ b/meson/warnings/meson.build @@ -0,0 +1,175 @@ +# Copyright 2020-2022 David Robillard +# SPDX-License-Identifier: CC0-1.0 OR ISC + +# General code to enable approximately all warnings in GCC 12, clang, and MSVC. +# +# This is trivial for clang and MSVC, but GCC doesn't have an "everything" +# option, so we need to enable everything we want explicitly. Wall is assumed, +# but Wextra is not, for stability. +# +# These are collected from common.opt and c.opt in the GCC source, and manually +# curated with the help of the GCC documentation. Warnings that are +# application-specific, historical, or about compatibility between specific +# language revisions are omitted. The intent here is to have roughly the same +# meaning as clang's Weverything: extremely strict, but general. Specifically +# omitted are: +# +# General: +# +# Wabi= +# Waggregate-return +# Walloc-size-larger-than=BYTES +# Walloca-larger-than=BYTES +# Wframe-larger-than=BYTES +# Wlarger-than=BYTES +# Wstack-usage=BYTES +# Wsystem-headers +# Wtraditional +# Wtraditional-conversion +# Wtrampolines +# Wvla-larger-than=BYTES +# +# Build specific: +# +# Wpoison-system-directories +# +# C Specific: +# +# Wc11-c2x-compat +# Wc90-c99-compat +# Wc99-c11-compat +# Wdeclaration-after-statement +# Wtraditional +# Wtraditional-conversion +# +# C++ Specific: +# +# Wc++0x-compat +# Wc++1z-compat +# Wc++2a-compat +# Wctad-maybe-unsupported +# Wnamespaces +# Wtemplates + +# GCC warnings that apply to all C-family languages +gcc_common_warnings = [ + '-Walloc-zero', + '-Walloca', + '-Wanalyzer-too-complex', + '-Warith-conversion', + '-Warray-bounds=2', + '-Wattribute-alias=2', + '-Wbidi-chars=ucn', + '-Wcast-align=strict', + '-Wcast-function-type', + '-Wcast-qual', + '-Wclobbered', + '-Wconversion', + '-Wdate-time', + '-Wdisabled-optimization', + '-Wdouble-promotion', + '-Wduplicated-branches', + '-Wduplicated-cond', + '-Wempty-body', + '-Wendif-labels', + '-Wfloat-equal', + '-Wformat-overflow=2', + '-Wformat-signedness', + '-Wformat-truncation=2', + '-Wformat=2', + '-Wignored-qualifiers', + '-Wimplicit-fallthrough=3', + '-Winit-self', + '-Winline', + '-Winvalid-pch', + '-Wlogical-op', + '-Wmissing-declarations', + '-Wmissing-field-initializers', + '-Wmissing-include-dirs', + '-Wmultichar', + '-Wnormalized=nfc', + '-Wnull-dereference', + '-Wopenacc-parallelism', + '-Woverlength-strings', + '-Wpacked', + '-Wpacked-bitfield-compat', + '-Wpadded', + '-Wpointer-arith', + '-Wredundant-decls', + '-Wshadow', + '-Wshift-negative-value', + '-Wshift-overflow=2', + '-Wstack-protector', + '-Wstrict-aliasing=3', + '-Wstrict-overflow=5', + '-Wstring-compare', + '-Wstringop-overflow=3', + '-Wsuggest-attribute=cold', + '-Wsuggest-attribute=const', + '-Wsuggest-attribute=format', + '-Wsuggest-attribute=malloc', + '-Wsuggest-attribute=noreturn', + '-Wsuggest-attribute=pure', + '-Wswitch-default', + '-Wswitch-enum', + '-Wtrampolines', + '-Wtrivial-auto-var-init', + '-Wtype-limits', + '-Wundef', + '-Wuninitialized', + '-Wunsafe-loop-optimizations', + '-Wunused', + '-Wunused-const-variable=2', + '-Wunused-macros', + '-Wvector-operation-performance', + '-Wvla', + '-Wwrite-strings', +] + +##### +# C # +##### + +if is_variable('cc') + # Set all_c_warnings for the current C compiler + all_c_warnings = [] + + if cc.get_id() == 'clang' + all_c_warnings += ['-Weverything'] + + if not meson.is_cross_build() + all_c_warnings += [ + '-Wno-poison-system-directories', + ] + endif + + elif cc.get_id() == 'gcc' + all_c_warnings += gcc_common_warnings + [ + '-Wabsolute-value', + '-Wbad-function-cast', + '-Wc++-compat', + '-Wenum-conversion', + '-Wjump-misses-init', + '-Wmissing-parameter-type', + '-Wmissing-prototypes', + '-Wnested-externs', + '-Wold-style-declaration', + '-Wold-style-definition', + '-Woverride-init', + '-Wsign-compare', + '-Wstrict-prototypes', + '-Wunsuffixed-float-constants', + ] + + elif cc.get_id() == 'msvc' + all_c_warnings += [ + '/Wall', + '/experimental:external', + '/external:W0', + '/external:anglebrackets', + ] + endif + + all_c_warnings = cc.get_supported_arguments(all_c_warnings) + add_global_arguments(all_c_warnings, language: ['c']) +endif diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..03a5a82 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,17 @@ +option('docs', type: 'feature', value: 'auto', yield: true, + description: 'Build documentation') + +option('strict', type: 'boolean', value: false, yield: true, + description: 'Enable ultra-strict warnings') + +option('bindings_py', type: 'feature', value: 'auto', yield: true, + description: 'Build Python bindings') + +option('tests', type: 'feature', value: 'auto', yield: true, + description: 'Build tests') + +option('title', type: 'string', value: 'Lilv', + description: 'Project title') + +option('tools', type: 'feature', value: 'auto', yield: true, + description: 'Build command line utilities') diff --git a/src/filesystem.c b/src/filesystem.c index 2785cca..5e44937 100644 --- a/src/filesystem.c +++ b/src/filesystem.c @@ -14,14 +14,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define _POSIX_C_SOURCE 200809L /* for fileno */ -#define _BSD_SOURCE 1 /* for realpath, symlink */ -#define _DEFAULT_SOURCE 1 /* for realpath, symlink */ - -#ifdef __APPLE__ -# define _DARWIN_C_SOURCE 1 /* for flock */ -#endif - #include "filesystem.h" #include "lilv_config.h" #include "lilv_internal.h" diff --git a/src/util.c b/src/util.c index 92f72b9..b2b4057 100644 --- a/src/util.c +++ b/src/util.c @@ -21,7 +21,6 @@ #include "serd/serd.h" #include -#include #include #include diff --git a/test/bad_syntax.lv2/meson.build b/test/bad_syntax.lv2/meson.build new file mode 100644 index 0000000..f50b43d --- /dev/null +++ b/test/bad_syntax.lv2/meson.build @@ -0,0 +1,25 @@ +module = shared_module('bad_syntax', + files('bad_syntax.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'bad_syntax.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + +test('bad_syntax', + executable('test_bad_syntax', + files('test_bad_syntax.c'), + c_args: c_suppressions + test_args, + dependencies: [lv2_dep, lilv_static_dep]), + args: [meson.current_build_dir() / ''], + suite: 'plugin') + diff --git a/test/core.lv2/lv2core.ttl b/test/core.lv2/lv2core.ttl deleted file mode 100644 index 5659487..0000000 --- a/test/core.lv2/lv2core.ttl +++ /dev/null @@ -1,30 +0,0 @@ -@prefix lv2: . -@prefix owl: . -@prefix rdfs: . - - - a owl:Ontology . - -lv2:PluginBase - a rdfs:Class , - owl:Class ; - rdfs:label "Plugin Base" . - -lv2:Plugin - a rdfs:Class , - owl:Class ; - rdfs:subClassOf lv2:PluginBase ; - rdfs:label "Plugin" . - -lv2:DynamicsPlugin - a rdfs:Class , - owl:Class ; - rdfs:subClassOf lv2:Plugin ; - rdfs:label "Dynamics" . - -lv2:CompressorPlugin - a rdfs:Class , - owl:Class ; - rdfs:subClassOf lv2:DynamicsPlugin ; - rdfs:label "Compressor" . - diff --git a/test/core.lv2/manifest.ttl b/test/core.lv2/manifest.ttl deleted file mode 100644 index a77ad71..0000000 --- a/test/core.lv2/manifest.ttl +++ /dev/null @@ -1,9 +0,0 @@ -@prefix doap: . -@prefix lv2: . -@prefix rdfs: . - - - a lv2:Specification ; - lv2:minorVersion 16 ; - lv2:microVersion 0 ; - rdfs:seeAlso . diff --git a/test/failed_instantiation.lv2/meson.build b/test/failed_instantiation.lv2/meson.build new file mode 100644 index 0000000..8e95549 --- /dev/null +++ b/test/failed_instantiation.lv2/meson.build @@ -0,0 +1,25 @@ +module = shared_module('failed_instantiation', + files('failed_instantiation.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'failed_instantiation.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + +test('failed_instantiation', + executable('test_failed_instantiation', + files('test_failed_instantiation.c'), + c_args: c_suppressions + test_args, + dependencies: [lv2_dep, lilv_static_dep]), + args: [meson.current_build_dir() / ''], + suite: 'plugin') + diff --git a/test/failed_lib_descriptor.lv2/meson.build b/test/failed_lib_descriptor.lv2/meson.build new file mode 100644 index 0000000..e4e9d76 --- /dev/null +++ b/test/failed_lib_descriptor.lv2/meson.build @@ -0,0 +1,25 @@ +module = shared_module('failed_lib_descriptor', + files('failed_lib_descriptor.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'failed_lib_descriptor.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + +test('failed_lib_descriptor', + executable('test_failed_lib_descriptor', + files('test_failed_lib_descriptor.c'), + c_args: c_suppressions + test_args, + dependencies: [lv2_dep, lilv_static_dep]), + args: [meson.current_build_dir() / ''], + suite: 'plugin') + diff --git a/test/lib_descriptor.lv2/meson.build b/test/lib_descriptor.lv2/meson.build new file mode 100644 index 0000000..5a85fe5 --- /dev/null +++ b/test/lib_descriptor.lv2/meson.build @@ -0,0 +1,25 @@ +module = shared_module('lib_descriptor', + files('lib_descriptor.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'lib_descriptor.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + +test('lib_descriptor', + executable('test_lib_descriptor', + files('test_lib_descriptor.c'), + c_args: c_suppressions + test_args, + dependencies: [lv2_dep, lilv_static_dep]), + args: [meson.current_build_dir() / ''], + suite: 'plugin') + diff --git a/test/lilv_test_uri_map.h b/test/lilv_test_uri_map.h index 5f85c86..88c9107 100644 --- a/test/lilv_test_uri_map.h +++ b/test/lilv_test_uri_map.h @@ -23,14 +23,13 @@ #include "serd/serd.h" #include -#include #include #include #include typedef struct { - char** uris; - size_t n_uris; + char** uris; + uint32_t n_uris; } LilvTestUriMap; static inline void @@ -43,7 +42,7 @@ lilv_test_uri_map_init(LilvTestUriMap* const map) static inline void lilv_test_uri_map_clear(LilvTestUriMap* const map) { - for (size_t i = 0; i < map->n_uris; ++i) { + for (uint32_t i = 0; i < map->n_uris; ++i) { free(map->uris[i]); } @@ -57,7 +56,7 @@ map_uri(LV2_URID_Map_Handle handle, const char* uri) { LilvTestUriMap* map = (LilvTestUriMap*)handle; - for (size_t i = 0; i < map->n_uris; ++i) { + for (uint32_t i = 0; i < map->n_uris; ++i) { if (!strcmp(map->uris[i], uri)) { return i + 1; } diff --git a/test/lilv_test_utils.c b/test/lilv_test_utils.c index f658f1b..941509a 100644 --- a/test/lilv_test_utils.c +++ b/test/lilv_test_utils.c @@ -14,8 +14,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define _POSIX_C_SOURCE 200809L /* for setenv */ - #include "lilv_test_utils.h" #include "../src/filesystem.h" @@ -47,7 +45,7 @@ lilv_test_env_new(void) // Set custom LV2_PATH in build directory to only use test data char* test_path = lilv_path_canonical(LILV_TEST_DIR); - char* lv2_path = lilv_strjoin(test_path, "/test_lv2_path", NULL); + char* lv2_path = lilv_strjoin(test_path, "/lv2", NULL); LilvNode* path = lilv_new_string(world, lv2_path); lilv_world_set_option(world, LILV_OPTION_LV2_PATH, path); free(lv2_path); diff --git a/test/lv2/core.lv2/lv2core.ttl b/test/lv2/core.lv2/lv2core.ttl new file mode 100644 index 0000000..5659487 --- /dev/null +++ b/test/lv2/core.lv2/lv2core.ttl @@ -0,0 +1,30 @@ +@prefix lv2: . +@prefix owl: . +@prefix rdfs: . + + + a owl:Ontology . + +lv2:PluginBase + a rdfs:Class , + owl:Class ; + rdfs:label "Plugin Base" . + +lv2:Plugin + a rdfs:Class , + owl:Class ; + rdfs:subClassOf lv2:PluginBase ; + rdfs:label "Plugin" . + +lv2:DynamicsPlugin + a rdfs:Class , + owl:Class ; + rdfs:subClassOf lv2:Plugin ; + rdfs:label "Dynamics" . + +lv2:CompressorPlugin + a rdfs:Class , + owl:Class ; + rdfs:subClassOf lv2:DynamicsPlugin ; + rdfs:label "Compressor" . + diff --git a/test/lv2/core.lv2/manifest.ttl b/test/lv2/core.lv2/manifest.ttl new file mode 100644 index 0000000..a77ad71 --- /dev/null +++ b/test/lv2/core.lv2/manifest.ttl @@ -0,0 +1,9 @@ +@prefix doap: . +@prefix lv2: . +@prefix rdfs: . + + + a lv2:Specification ; + lv2:minorVersion 16 ; + lv2:microVersion 0 ; + rdfs:seeAlso . diff --git a/test/lv2/core.lv2/meson.build b/test/lv2/core.lv2/meson.build new file mode 100644 index 0000000..4ad7969 --- /dev/null +++ b/test/lv2/core.lv2/meson.build @@ -0,0 +1,8 @@ +ttl_files = files( + 'lv2core.ttl', + 'manifest.ttl', +) + +foreach file : ttl_files + configure_file(copy: true, input: file, output: '@PLAINNAME@') +endforeach diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 0000000..22b316b --- /dev/null +++ b/test/meson.build @@ -0,0 +1,80 @@ +# Copyright 2020-2022 David Robillard +# SPDX-License-Identifier: CC0-1.0 OR ISC + +# Check release metadata +autoship = find_program('autoship', required: false) +if autoship.found() + test('autoship', autoship, args: ['test', lilv_src_root], suite: 'data') +endif + +test_args = ['-DLILV_STATIC'] +if cc.get_id() == 'msvc' + test_args += [ + '/wd4464', # relative include path contains '..' + ] +endif + +subdir('lv2/core.lv2') + +################ +# Bundle Tests # +################ + +subdir('bad_syntax.lv2') +subdir('failed_instantiation.lv2') +subdir('failed_lib_descriptor.lv2') +subdir('lib_descriptor.lv2') +subdir('missing_descriptor.lv2') +subdir('missing_name.lv2') +subdir('missing_plugin.lv2') +subdir('missing_port.lv2') +subdir('missing_port_name.lv2') +subdir('new_version.lv2') +subdir('old_version.lv2') +subdir('test_plugin.lv2') + +############## +# Unit Tests # +############## + +unit_tests = [ + 'bad_port_index', + 'bad_port_symbol', + 'classes', + 'discovery', + 'filesystem', + 'get_symbol', + 'no_author', + 'no_verify', + 'plugin', + 'port', + 'preset', + 'project', + 'project_no_author', + 'prototype', + 'reload_bundle', + 'replace_version', + 'state', + 'string', + 'ui', + 'util', + 'value', + 'verify', + 'world', +] + +define_args = [ + '-DLILV_TEST_BUNDLE="@0@/"'.format(meson.current_build_dir() / 'test_plugin.lv2'), + '-DLILV_TEST_DIR="@0@/"'.format(meson.current_build_dir()), +] + +foreach unit : unit_tests + test(unit, + executable('test_@0@'.format(unit), + files('lilv_test_utils.c', 'test_@0@.c'.format(unit)), + c_args: define_args + test_args + c_suppressions, + include_directories: include_directories('../src'), + dependencies: [lv2_dep, lilv_static_dep]), + suite: 'unit') +endforeach + diff --git a/test/missing_descriptor.lv2/meson.build b/test/missing_descriptor.lv2/meson.build new file mode 100644 index 0000000..cf1d45c --- /dev/null +++ b/test/missing_descriptor.lv2/meson.build @@ -0,0 +1,25 @@ +module = shared_module('missing_descriptor', + files('missing_descriptor.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'missing_descriptor.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + +test('missing_descriptor', + executable('test_missing_descriptor', + files('test_missing_descriptor.c'), + c_args: c_suppressions + test_args, + dependencies: [lv2_dep, lilv_static_dep]), + args: [meson.current_build_dir() / ''], + suite: 'plugin') + diff --git a/test/missing_name.lv2/meson.build b/test/missing_name.lv2/meson.build new file mode 100644 index 0000000..3fe6ce4 --- /dev/null +++ b/test/missing_name.lv2/meson.build @@ -0,0 +1,25 @@ +module = shared_module('missing_name', + files('missing_name.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'missing_name.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + +test('missing_name', + executable('test_missing_name', + files('test_missing_name.c'), + c_args: c_suppressions + test_args, + dependencies: [lv2_dep, lilv_static_dep]), + args: [meson.current_build_dir() / ''], + suite: 'plugin') + diff --git a/test/missing_plugin.lv2/meson.build b/test/missing_plugin.lv2/meson.build new file mode 100644 index 0000000..ca6a8fe --- /dev/null +++ b/test/missing_plugin.lv2/meson.build @@ -0,0 +1,25 @@ +module = shared_module('missing_plugin', + files('missing_plugin.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'missing_plugin.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + +test('missing_plugin', + executable('test_missing_plugin', + files('test_missing_plugin.c'), + c_args: c_suppressions + test_args, + dependencies: [lv2_dep, lilv_static_dep]), + args: [meson.current_build_dir() / ''], + suite: 'plugin') + diff --git a/test/missing_port.lv2/meson.build b/test/missing_port.lv2/meson.build new file mode 100644 index 0000000..f497b8c --- /dev/null +++ b/test/missing_port.lv2/meson.build @@ -0,0 +1,25 @@ +module = shared_module('missing_port', + files('missing_port.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'missing_port.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + +test('missing_port', + executable('test_missing_port', + files('test_missing_port.c'), + c_args: c_suppressions + test_args, + dependencies: [lv2_dep, lilv_static_dep]), + args: [meson.current_build_dir() / ''], + suite: 'plugin') + diff --git a/test/missing_port_name.lv2/meson.build b/test/missing_port_name.lv2/meson.build new file mode 100644 index 0000000..07e7811 --- /dev/null +++ b/test/missing_port_name.lv2/meson.build @@ -0,0 +1,25 @@ +module = shared_module('missing_port_name', + files('missing_port_name.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'missing_port_name.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + +test('missing_port_name', + executable('test_missing_port_name', + files('test_missing_port_name.c'), + c_args: c_suppressions + test_args, + dependencies: [lv2_dep, lilv_static_dep]), + args: [meson.current_build_dir() / ''], + suite: 'plugin') + diff --git a/test/new_version.lv2/meson.build b/test/new_version.lv2/meson.build new file mode 100644 index 0000000..b745415 --- /dev/null +++ b/test/new_version.lv2/meson.build @@ -0,0 +1,17 @@ +module = shared_module('new_version', + files('new_version.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'new_version.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + diff --git a/test/old_version.lv2/meson.build b/test/old_version.lv2/meson.build new file mode 100644 index 0000000..efec6e1 --- /dev/null +++ b/test/old_version.lv2/meson.build @@ -0,0 +1,17 @@ +module = shared_module('old_version', + files('old_version.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'old_version.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + diff --git a/test/test.lv2/manifest.ttl.in b/test/test.lv2/manifest.ttl.in deleted file mode 100644 index bc3952c..0000000 --- a/test/test.lv2/manifest.ttl.in +++ /dev/null @@ -1,7 +0,0 @@ -@prefix lv2: . -@prefix rdfs: . - - - a lv2:Plugin ; - lv2:binary ; - rdfs:seeAlso . diff --git a/test/test.lv2/test.c b/test/test.lv2/test.c deleted file mode 100644 index 1dc7076..0000000 --- a/test/test.lv2/test.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - Lilv Test Plugin - Copyright 2011-2022 David Robillard - - 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 200809L - -#include "lv2/atom/atom.h" -#include "lv2/core/lv2.h" -#include "lv2/state/state.h" -#include "lv2/urid/urid.h" - -#ifdef _WIN32 -# define _WIN32_LEAN_AND_MEAN -# include -#endif - -#include -#include -#include -#include - -#define TEST_URI "http://example.org/lilv-test-plugin" - -enum { TEST_INPUT = 0, TEST_OUTPUT = 1, TEST_CONTROL = 2 }; - -typedef struct { - LV2_URID_Map* map; - LV2_State_Free_Path* free_path; - - struct { - LV2_URID atom_Float; - } uris; - - char* tmp_dir_path; - char* rec_file_path; - FILE* rec_file; - - float* input; - float* output; - unsigned num_runs; -} Test; - -static char* -temp_directory_path(void) -{ -#ifdef _WIN32 - const DWORD len = GetTempPath(0, NULL); - char* const buf = (char*)calloc(len, 1); - if (GetTempPath(len, buf) == 0) { - free(buf); - return NULL; - } - - return buf; -#else - const char* const tmpdir = getenv("TMPDIR"); - if (tmpdir) { - const size_t tmpdir_len = strlen(tmpdir); - char* const result = (char*)calloc(tmpdir_len + 1, 1); - - memcpy(result, tmpdir, tmpdir_len + 1); - return result; - } - - char* const result = (char*)calloc(6, 1); - - memcpy(result, "/tmp/", 6); - return result; -#endif -} - -static void -cleanup(LV2_Handle instance) -{ - Test* test = (Test*)instance; - if (test->rec_file) { - fclose(test->rec_file); - } - - if (test->free_path) { - test->free_path->free_path(test->free_path->handle, test->rec_file_path); - } - - free(test->tmp_dir_path); - free(instance); -} - -static void -connect_port(LV2_Handle instance, uint32_t port, void* data) -{ - Test* test = (Test*)instance; - switch (port) { - case TEST_INPUT: - test->input = (float*)data; - break; - case TEST_OUTPUT: - test->output = (float*)data; - break; - case TEST_CONTROL: - test->output = (float*)data; - break; - default: - break; - } -} - -static LV2_Handle -instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* path, - const LV2_Feature* const* features) -{ - (void)descriptor; - (void)rate; - (void)path; - - Test* test = (Test*)calloc(1, sizeof(Test)); - if (!test) { - return NULL; - } - - test->tmp_dir_path = temp_directory_path(); - - LV2_State_Make_Path* make_path = NULL; - - for (int i = 0; features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { - test->map = (LV2_URID_Map*)features[i]->data; - test->uris.atom_Float = - test->map->map(test->map->handle, LV2_ATOM__Float); - } else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { - make_path = (LV2_State_Make_Path*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_STATE__freePath)) { - test->free_path = (LV2_State_Free_Path*)features[i]->data; - } - } - - if (!test->map) { - fprintf(stderr, "Host does not support urid:map\n"); - free(test); - return NULL; - } - - if (make_path) { - if (!test->free_path) { - fprintf(stderr, "Host provided make_path without free_path\n"); - free(test); - return NULL; - } - - test->rec_file_path = make_path->path(make_path->handle, "recfile"); - if (!(test->rec_file = fopen(test->rec_file_path, "w"))) { - fprintf(stderr, "ERROR: Failed to open rec file\n"); - } - fprintf(test->rec_file, "instantiate\n"); - } - - return (LV2_Handle)test; -} - -static void -run(LV2_Handle instance, uint32_t sample_count) -{ - Test* test = (Test*)instance; - *test->output = *test->input; - if (sample_count == 1) { - ++test->num_runs; - } else if (sample_count == 2 && test->rec_file) { - // Append to rec file (changes size) - fprintf(test->rec_file, "run\n"); - } else if (sample_count == 3 && test->rec_file) { - // Change the first byte of rec file (doesn't change size) - fseek(test->rec_file, 0, SEEK_SET); - fprintf(test->rec_file, "X"); - fseek(test->rec_file, 0, SEEK_END); - } -} - -static uint32_t -map_uri(Test* plugin, const char* uri) -{ - return plugin->map->map(plugin->map->handle, uri); -} - -static LV2_State_Status -save(LV2_Handle instance, - LV2_State_Store_Function store, - void* callback_data, - uint32_t flags, - const LV2_Feature* const* features) -{ - (void)flags; - - Test* plugin = (Test*)instance; - - LV2_State_Map_Path* map_path = NULL; - LV2_State_Make_Path* make_path = NULL; - LV2_State_Free_Path* free_path = NULL; - for (int i = 0; features && features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { - map_path = (LV2_State_Map_Path*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { - make_path = (LV2_State_Make_Path*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_STATE__freePath)) { - free_path = (LV2_State_Free_Path*)features[i]->data; - } - } - - if (!map_path || !free_path) { - return LV2_STATE_ERR_NO_FEATURE; - } - - store(callback_data, - map_uri(plugin, "http://example.org/greeting"), - "hello", - strlen("hello") + 1, - map_uri(plugin, LV2_ATOM__String), - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - const uint32_t urid = map_uri(plugin, "http://example.org/urivalue"); - store(callback_data, - map_uri(plugin, "http://example.org/uri"), - &urid, - sizeof(uint32_t), - map_uri(plugin, LV2_ATOM__URID), - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - // Try to store second value for the same property (should fail) - const uint32_t urid2 = map_uri(plugin, "http://example.org/urivalue2"); - if (!store(callback_data, - map_uri(plugin, "http://example.org/uri"), - &urid2, - sizeof(uint32_t), - map_uri(plugin, LV2_ATOM__URID), - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE)) { - return LV2_STATE_ERR_UNKNOWN; - } - - // Try to store with a null key (should fail) - if (!store(callback_data, - 0, - &urid2, - sizeof(uint32_t), - map_uri(plugin, LV2_ATOM__URID), - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE)) { - return LV2_STATE_ERR_UNKNOWN; - } - - store(callback_data, - map_uri(plugin, "http://example.org/num-runs"), - &plugin->num_runs, - sizeof(plugin->num_runs), - map_uri(plugin, LV2_ATOM__Int), - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - const float two = 2.0f; - store(callback_data, - map_uri(plugin, "http://example.org/two"), - &two, - sizeof(two), - map_uri(plugin, LV2_ATOM__Float), - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - const uint32_t affirmative = 1; - store(callback_data, - map_uri(plugin, "http://example.org/true"), - &affirmative, - sizeof(affirmative), - map_uri(plugin, LV2_ATOM__Bool), - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - const uint32_t negative = 0; - store(callback_data, - map_uri(plugin, "http://example.org/false"), - &negative, - sizeof(negative), - map_uri(plugin, LV2_ATOM__Bool), - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - const uint8_t blob[] = "I am a blob of arbitrary data."; - store(callback_data, - map_uri(plugin, "http://example.org/blob"), - blob, - sizeof(blob), - map_uri(plugin, "http://example.org/SomeUnknownType"), - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - if (map_path) { - const char* const file_name = "temp_file.txt"; - const size_t file_name_len = strlen(file_name); - const size_t dir_path_len = strlen(plugin->tmp_dir_path); - char* const tmp_file_path = - (char*)calloc(dir_path_len + file_name_len + 1, 1); - - memcpy(tmp_file_path, plugin->tmp_dir_path, dir_path_len); - memcpy(tmp_file_path + dir_path_len, file_name, file_name_len + 1); - - FILE* file = fopen(tmp_file_path, "w"); - if (!file) { - fprintf(stderr, "error: Failed to open file %s\n", tmp_file_path); - free(tmp_file_path); - return LV2_STATE_ERR_UNKNOWN; - } - - fprintf(file, "Hello\n"); - fclose(file); - - char* apath = map_path->abstract_path(map_path->handle, tmp_file_path); - char* apath2 = map_path->abstract_path(map_path->handle, tmp_file_path); - free(tmp_file_path); - if (strcmp(apath, apath2)) { - fprintf(stderr, "error: Path %s != %s\n", apath, apath2); - } - - store(callback_data, - map_uri(plugin, "http://example.org/extfile"), - apath, - strlen(apath) + 1, - map_uri(plugin, LV2_ATOM__Path), - LV2_STATE_IS_POD); - - free_path->free_path(free_path->handle, apath); - free_path->free_path(free_path->handle, apath2); - - if (plugin->rec_file) { - fflush(plugin->rec_file); - apath = map_path->abstract_path(map_path->handle, plugin->rec_file_path); - - store(callback_data, - map_uri(plugin, "http://example.org/recfile"), - apath, - strlen(apath) + 1, - map_uri(plugin, LV2_ATOM__Path), - LV2_STATE_IS_POD); - - free_path->free_path(free_path->handle, apath); - } - - if (make_path) { - char* spath = make_path->path(make_path->handle, "save"); - FILE* sfile = fopen(spath, "w"); - fprintf(sfile, "save"); - fclose(sfile); - - apath = map_path->abstract_path(map_path->handle, spath); - store(callback_data, - map_uri(plugin, "http://example.org/save-file"), - apath, - strlen(apath) + 1, - map_uri(plugin, LV2_ATOM__Path), - LV2_STATE_IS_POD); - free_path->free_path(free_path->handle, apath); - free_path->free_path(free_path->handle, spath); - } - } - - return LV2_STATE_SUCCESS; -} - -static LV2_State_Status -restore(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - void* callback_data, - uint32_t flags, - const LV2_Feature* const* features) -{ - (void)flags; - - Test* plugin = (Test*)instance; - - LV2_State_Map_Path* map_path = NULL; - LV2_State_Free_Path* free_path = NULL; - for (int i = 0; features && features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { - map_path = (LV2_State_Map_Path*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_STATE__freePath)) { - free_path = (LV2_State_Free_Path*)features[i]->data; - } - } - - size_t size = 0; - uint32_t type = 0; - uint32_t valflags = 0; - - plugin->num_runs = - *(int32_t*)retrieve(callback_data, - map_uri(plugin, "http://example.org/num-runs"), - &size, - &type, - &valflags); - - if (!map_path || !free_path) { - return LV2_STATE_ERR_NO_FEATURE; - } - - char* apath = (char*)retrieve(callback_data, - map_uri(plugin, "http://example.org/extfile"), - &size, - &type, - &valflags); - - if (valflags != LV2_STATE_IS_POD) { - fprintf(stderr, "error: Restored bad file flags\n"); - return LV2_STATE_ERR_BAD_FLAGS; - } - - if (apath) { - char* path = map_path->absolute_path(map_path->handle, apath); - FILE* f = fopen(path, "r"); - char str[8]; - size_t n_read = fread(str, 1, sizeof(str), f); - fclose(f); - if (strncmp(str, "Hello\n", n_read)) { - fprintf( - stderr, "error: Restored bad file contents `%s' != `Hello'\n", str); - } - free_path->free_path(free_path->handle, path); - } - - apath = (char*)retrieve(callback_data, - map_uri(plugin, "http://example.org/save-file"), - &size, - &type, - &valflags); - if (apath) { - char* spath = map_path->absolute_path(map_path->handle, apath); - FILE* sfile = fopen(spath, "r"); - if (!sfile) { - fprintf(stderr, "error: Failed to open save file %s\n", spath); - } else { - fclose(sfile); - } - free_path->free_path(free_path->handle, spath); - } else { - fprintf(stderr, "error: Failed to restore save file.\n"); - } - - return LV2_STATE_SUCCESS; -} - -static const void* -extension_data(const char* uri) -{ - static const LV2_State_Interface state = {save, restore}; - if (!strcmp(uri, LV2_STATE__interface)) { - return &state; - } - return NULL; -} - -static const LV2_Descriptor descriptor = {TEST_URI, - instantiate, - connect_port, - NULL, // activate, - run, - NULL, // deactivate, - cleanup, - extension_data}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* -lv2_descriptor(uint32_t index) -{ - switch (index) { - case 0: - return &descriptor; - default: - return NULL; - } -} diff --git a/test/test.lv2/test.ttl.in b/test/test.lv2/test.ttl.in deleted file mode 100644 index 1c16b4c..0000000 --- a/test/test.lv2/test.ttl.in +++ /dev/null @@ -1,46 +0,0 @@ -# Lilv Test Plugin -# Copyright 2011-2015 David Robillard -# -# 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. - -@prefix doap: . -@prefix foaf: . -@prefix lv2: . -@prefix ui: . - - - a lv2:Plugin ; - doap:name "Lilv Test" ; - doap:license ; - lv2:requiredFeature ; - lv2:optionalFeature lv2:hardRTCapable ; - lv2:extensionData ; - lv2:port [ - a lv2:InputPort , - lv2:ControlPort ; - lv2:index 0 ; - lv2:symbol "input" ; - lv2:name "Input" - ] , [ - a lv2:OutputPort , - lv2:ControlPort ; - lv2:index 1 ; - lv2:symbol "output" ; - lv2:name "Output" - ] , [ - a lv2:InputPort , - lv2:ControlPort ; - lv2:index 2 ; - lv2:symbol "control" ; - lv2:name "Control" - ] . diff --git a/test/test_plugin.lv2/manifest.ttl.in b/test/test_plugin.lv2/manifest.ttl.in new file mode 100644 index 0000000..48b009b --- /dev/null +++ b/test/test_plugin.lv2/manifest.ttl.in @@ -0,0 +1,7 @@ +@prefix lv2: . +@prefix rdfs: . + + + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . diff --git a/test/test_plugin.lv2/meson.build b/test/test_plugin.lv2/meson.build new file mode 100644 index 0000000..2a4273d --- /dev/null +++ b/test/test_plugin.lv2/meson.build @@ -0,0 +1,17 @@ +module = shared_module('test_plugin', + files('test_plugin.c'), + c_args: c_suppressions, + dependencies: lv2_dep, + gnu_symbol_visibility: 'hidden', + name_prefix: '') + +extension = '.' + module.full_path().split('.')[-1] +config = configuration_data({'SHLIB_EXT': extension}) +ttl_files = ['manifest.ttl', 'test_plugin.ttl'] + +foreach f : ttl_files + configure_file(input: files(f + '.in'), + output: f, + configuration: config) +endforeach + diff --git a/test/test_plugin.lv2/test_plugin.c b/test/test_plugin.lv2/test_plugin.c new file mode 100644 index 0000000..9615fff --- /dev/null +++ b/test/test_plugin.lv2/test_plugin.c @@ -0,0 +1,481 @@ +/* + Lilv Test Plugin + Copyright 2011-2022 David Robillard + + 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. +*/ + +#include "lv2/atom/atom.h" +#include "lv2/core/lv2.h" +#include "lv2/state/state.h" +#include "lv2/urid/urid.h" + +#ifdef _WIN32 +# define _WIN32_LEAN_AND_MEAN +# include +#endif + +#include +#include +#include +#include + +#define TEST_URI "http://example.org/lilv-test-plugin" + +enum { TEST_INPUT = 0, TEST_OUTPUT = 1, TEST_CONTROL = 2 }; + +typedef struct { + LV2_URID_Map* map; + LV2_State_Free_Path* free_path; + + struct { + LV2_URID atom_Float; + } uris; + + char* tmp_dir_path; + char* rec_file_path; + FILE* rec_file; + + float* input; + float* output; + unsigned num_runs; +} Test; + +static char* +temp_directory_path(void) +{ +#ifdef _WIN32 + const DWORD len = GetTempPath(0, NULL); + char* const buf = (char*)calloc(len, 1); + if (GetTempPath(len, buf) == 0) { + free(buf); + return NULL; + } + + return buf; +#else + const char* const tmpdir = getenv("TMPDIR"); + if (tmpdir) { + const size_t tmpdir_len = strlen(tmpdir); + char* const result = (char*)calloc(tmpdir_len + 1, 1); + + memcpy(result, tmpdir, tmpdir_len + 1); + return result; + } + + char* const result = (char*)calloc(6, 1); + + memcpy(result, "/tmp/", 6); + return result; +#endif +} + +static void +cleanup(LV2_Handle instance) +{ + Test* test = (Test*)instance; + if (test->rec_file) { + fclose(test->rec_file); + } + + if (test->free_path) { + test->free_path->free_path(test->free_path->handle, test->rec_file_path); + } + + free(test->tmp_dir_path); + free(instance); +} + +static void +connect_port(LV2_Handle instance, uint32_t port, void* data) +{ + Test* test = (Test*)instance; + switch (port) { + case TEST_INPUT: + test->input = (float*)data; + break; + case TEST_OUTPUT: + test->output = (float*)data; + break; + case TEST_CONTROL: + test->output = (float*)data; + break; + default: + break; + } +} + +static LV2_Handle +instantiate(const LV2_Descriptor* descriptor, + double rate, + const char* path, + const LV2_Feature* const* features) +{ + (void)descriptor; + (void)rate; + (void)path; + + Test* test = (Test*)calloc(1, sizeof(Test)); + if (!test) { + return NULL; + } + + test->tmp_dir_path = temp_directory_path(); + + LV2_State_Make_Path* make_path = NULL; + + for (int i = 0; features[i]; ++i) { + if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { + test->map = (LV2_URID_Map*)features[i]->data; + test->uris.atom_Float = + test->map->map(test->map->handle, LV2_ATOM__Float); + } else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { + make_path = (LV2_State_Make_Path*)features[i]->data; + } else if (!strcmp(features[i]->URI, LV2_STATE__freePath)) { + test->free_path = (LV2_State_Free_Path*)features[i]->data; + } + } + + if (!test->map) { + fprintf(stderr, "Host does not support urid:map\n"); + free(test); + return NULL; + } + + if (make_path) { + if (!test->free_path) { + fprintf(stderr, "Host provided make_path without free_path\n"); + free(test); + return NULL; + } + + test->rec_file_path = make_path->path(make_path->handle, "recfile"); + if (!(test->rec_file = fopen(test->rec_file_path, "w"))) { + fprintf(stderr, "ERROR: Failed to open rec file\n"); + } + fprintf(test->rec_file, "instantiate\n"); + } + + return (LV2_Handle)test; +} + +static void +run(LV2_Handle instance, uint32_t sample_count) +{ + Test* test = (Test*)instance; + *test->output = *test->input; + if (sample_count == 1) { + ++test->num_runs; + } else if (sample_count == 2 && test->rec_file) { + // Append to rec file (changes size) + fprintf(test->rec_file, "run\n"); + } else if (sample_count == 3 && test->rec_file) { + // Change the first byte of rec file (doesn't change size) + fseek(test->rec_file, 0, SEEK_SET); + fprintf(test->rec_file, "X"); + fseek(test->rec_file, 0, SEEK_END); + } +} + +static uint32_t +map_uri(Test* plugin, const char* uri) +{ + return plugin->map->map(plugin->map->handle, uri); +} + +static LV2_State_Status +save(LV2_Handle instance, + LV2_State_Store_Function store, + void* callback_data, + uint32_t flags, + const LV2_Feature* const* features) +{ + (void)flags; + + Test* plugin = (Test*)instance; + + LV2_State_Map_Path* map_path = NULL; + LV2_State_Make_Path* make_path = NULL; + LV2_State_Free_Path* free_path = NULL; + for (int i = 0; features && features[i]; ++i) { + if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { + map_path = (LV2_State_Map_Path*)features[i]->data; + } else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { + make_path = (LV2_State_Make_Path*)features[i]->data; + } else if (!strcmp(features[i]->URI, LV2_STATE__freePath)) { + free_path = (LV2_State_Free_Path*)features[i]->data; + } + } + + if (!map_path || !free_path) { + return LV2_STATE_ERR_NO_FEATURE; + } + + store(callback_data, + map_uri(plugin, "http://example.org/greeting"), + "hello", + strlen("hello") + 1, + map_uri(plugin, LV2_ATOM__String), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + const uint32_t urid = map_uri(plugin, "http://example.org/urivalue"); + store(callback_data, + map_uri(plugin, "http://example.org/uri"), + &urid, + sizeof(uint32_t), + map_uri(plugin, LV2_ATOM__URID), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + // Try to store second value for the same property (should fail) + const uint32_t urid2 = map_uri(plugin, "http://example.org/urivalue2"); + if (!store(callback_data, + map_uri(plugin, "http://example.org/uri"), + &urid2, + sizeof(uint32_t), + map_uri(plugin, LV2_ATOM__URID), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE)) { + return LV2_STATE_ERR_UNKNOWN; + } + + // Try to store with a null key (should fail) + if (!store(callback_data, + 0, + &urid2, + sizeof(uint32_t), + map_uri(plugin, LV2_ATOM__URID), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE)) { + return LV2_STATE_ERR_UNKNOWN; + } + + store(callback_data, + map_uri(plugin, "http://example.org/num-runs"), + &plugin->num_runs, + sizeof(plugin->num_runs), + map_uri(plugin, LV2_ATOM__Int), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + const float two = 2.0f; + store(callback_data, + map_uri(plugin, "http://example.org/two"), + &two, + sizeof(two), + map_uri(plugin, LV2_ATOM__Float), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + const uint32_t affirmative = 1; + store(callback_data, + map_uri(plugin, "http://example.org/true"), + &affirmative, + sizeof(affirmative), + map_uri(plugin, LV2_ATOM__Bool), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + const uint32_t negative = 0; + store(callback_data, + map_uri(plugin, "http://example.org/false"), + &negative, + sizeof(negative), + map_uri(plugin, LV2_ATOM__Bool), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + const uint8_t blob[] = "I am a blob of arbitrary data."; + store(callback_data, + map_uri(plugin, "http://example.org/blob"), + blob, + sizeof(blob), + map_uri(plugin, "http://example.org/SomeUnknownType"), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + if (map_path) { + const char* const file_name = "temp_file.txt"; + const size_t file_name_len = strlen(file_name); + const size_t dir_path_len = strlen(plugin->tmp_dir_path); + char* const tmp_file_path = + (char*)calloc(dir_path_len + file_name_len + 1, 1); + + memcpy(tmp_file_path, plugin->tmp_dir_path, dir_path_len); + memcpy(tmp_file_path + dir_path_len, file_name, file_name_len + 1); + + FILE* file = fopen(tmp_file_path, "w"); + if (!file) { + fprintf(stderr, "error: Failed to open file %s\n", tmp_file_path); + free(tmp_file_path); + return LV2_STATE_ERR_UNKNOWN; + } + + fprintf(file, "Hello\n"); + fclose(file); + + char* apath = map_path->abstract_path(map_path->handle, tmp_file_path); + char* apath2 = map_path->abstract_path(map_path->handle, tmp_file_path); + free(tmp_file_path); + if (strcmp(apath, apath2)) { + fprintf(stderr, "error: Path %s != %s\n", apath, apath2); + } + + store(callback_data, + map_uri(plugin, "http://example.org/extfile"), + apath, + strlen(apath) + 1, + map_uri(plugin, LV2_ATOM__Path), + LV2_STATE_IS_POD); + + free_path->free_path(free_path->handle, apath); + free_path->free_path(free_path->handle, apath2); + + if (plugin->rec_file) { + fflush(plugin->rec_file); + apath = map_path->abstract_path(map_path->handle, plugin->rec_file_path); + + store(callback_data, + map_uri(plugin, "http://example.org/recfile"), + apath, + strlen(apath) + 1, + map_uri(plugin, LV2_ATOM__Path), + LV2_STATE_IS_POD); + + free_path->free_path(free_path->handle, apath); + } + + if (make_path) { + char* spath = make_path->path(make_path->handle, "save"); + FILE* sfile = fopen(spath, "w"); + fprintf(sfile, "save"); + fclose(sfile); + + apath = map_path->abstract_path(map_path->handle, spath); + store(callback_data, + map_uri(plugin, "http://example.org/save-file"), + apath, + strlen(apath) + 1, + map_uri(plugin, LV2_ATOM__Path), + LV2_STATE_IS_POD); + free_path->free_path(free_path->handle, apath); + free_path->free_path(free_path->handle, spath); + } + } + + return LV2_STATE_SUCCESS; +} + +static LV2_State_Status +restore(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + void* callback_data, + uint32_t flags, + const LV2_Feature* const* features) +{ + (void)flags; + + Test* plugin = (Test*)instance; + + LV2_State_Map_Path* map_path = NULL; + LV2_State_Free_Path* free_path = NULL; + for (int i = 0; features && features[i]; ++i) { + if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { + map_path = (LV2_State_Map_Path*)features[i]->data; + } else if (!strcmp(features[i]->URI, LV2_STATE__freePath)) { + free_path = (LV2_State_Free_Path*)features[i]->data; + } + } + + size_t size = 0; + uint32_t type = 0; + uint32_t valflags = 0; + + plugin->num_runs = + *(int32_t*)retrieve(callback_data, + map_uri(plugin, "http://example.org/num-runs"), + &size, + &type, + &valflags); + + if (!map_path || !free_path) { + return LV2_STATE_ERR_NO_FEATURE; + } + + char* apath = (char*)retrieve(callback_data, + map_uri(plugin, "http://example.org/extfile"), + &size, + &type, + &valflags); + + if (valflags != LV2_STATE_IS_POD) { + fprintf(stderr, "error: Restored bad file flags\n"); + return LV2_STATE_ERR_BAD_FLAGS; + } + + if (apath) { + char* path = map_path->absolute_path(map_path->handle, apath); + FILE* f = fopen(path, "r"); + char str[8]; + size_t n_read = fread(str, 1, sizeof(str), f); + fclose(f); + if (strncmp(str, "Hello\n", n_read)) { + fprintf( + stderr, "error: Restored bad file contents `%s' != `Hello'\n", str); + } + free_path->free_path(free_path->handle, path); + } + + apath = (char*)retrieve(callback_data, + map_uri(plugin, "http://example.org/save-file"), + &size, + &type, + &valflags); + if (apath) { + char* spath = map_path->absolute_path(map_path->handle, apath); + FILE* sfile = fopen(spath, "r"); + if (!sfile) { + fprintf(stderr, "error: Failed to open save file %s\n", spath); + } else { + fclose(sfile); + } + free_path->free_path(free_path->handle, spath); + } else { + fprintf(stderr, "error: Failed to restore save file.\n"); + } + + return LV2_STATE_SUCCESS; +} + +static const void* +extension_data(const char* uri) +{ + static const LV2_State_Interface state = {save, restore}; + if (!strcmp(uri, LV2_STATE__interface)) { + return &state; + } + return NULL; +} + +static const LV2_Descriptor descriptor = {TEST_URI, + instantiate, + connect_port, + NULL, // activate, + run, + NULL, // deactivate, + cleanup, + extension_data}; + +LV2_SYMBOL_EXPORT +const LV2_Descriptor* +lv2_descriptor(uint32_t index) +{ + switch (index) { + case 0: + return &descriptor; + default: + return NULL; + } +} diff --git a/test/test_plugin.lv2/test_plugin.ttl.in b/test/test_plugin.lv2/test_plugin.ttl.in new file mode 100644 index 0000000..1c16b4c --- /dev/null +++ b/test/test_plugin.lv2/test_plugin.ttl.in @@ -0,0 +1,46 @@ +# Lilv Test Plugin +# Copyright 2011-2015 David Robillard +# +# 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. + +@prefix doap: . +@prefix foaf: . +@prefix lv2: . +@prefix ui: . + + + a lv2:Plugin ; + doap:name "Lilv Test" ; + doap:license ; + lv2:requiredFeature ; + lv2:optionalFeature lv2:hardRTCapable ; + lv2:extensionData ; + lv2:port [ + a lv2:InputPort , + lv2:ControlPort ; + lv2:index 0 ; + lv2:symbol "input" ; + lv2:name "Input" + ] , [ + a lv2:OutputPort , + lv2:ControlPort ; + lv2:index 1 ; + lv2:symbol "output" ; + lv2:name "Output" + ] , [ + a lv2:InputPort , + lv2:ControlPort ; + lv2:index 2 ; + lv2:symbol "control" ; + lv2:name "Control" + ] . diff --git a/test/test_string.c b/test/test_string.c index dcfd894..4b718b5 100644 --- a/test/test_string.c +++ b/test/test_string.c @@ -14,8 +14,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define _POSIX_C_SOURCE 200809L /* for setenv */ - #undef NDEBUG #include "../src/lilv_internal.h" diff --git a/tools/bench.h b/tools/bench.h new file mode 100644 index 0000000..33e37f6 --- /dev/null +++ b/tools/bench.h @@ -0,0 +1,51 @@ +/* + Copyright 2011-2019 David Robillard + + 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 + +#include + +typedef struct timespec BenchmarkTime; + +static inline double +bench_elapsed_s(const BenchmarkTime* start, const BenchmarkTime* end) +{ + return ((end->tv_sec - start->tv_sec) + + ((end->tv_nsec - start->tv_nsec) * 0.000000001)); +} + +static inline BenchmarkTime +bench_start(void) +{ + BenchmarkTime start_t; + clock_gettime(CLOCK_REALTIME, &start_t); + return start_t; +} + +static inline double +bench_end(const BenchmarkTime* start_t) +{ + BenchmarkTime end_t; + clock_gettime(CLOCK_REALTIME, &end_t); + return bench_elapsed_s(start_t, &end_t); +} + +#endif /* BENCH_H */ diff --git a/tools/lilv.bash_completion b/tools/lilv.bash_completion new file mode 100644 index 0000000..4a553a7 --- /dev/null +++ b/tools/lilv.bash_completion @@ -0,0 +1,59 @@ +# Bash auto-completion script written for lv2info and lv2jack. +# Could be adapted to any other program that takes an +# LV2 plugin URI as parameter. + +# Updated for Lilv by David Robillard on 2012-01-08. +# Written by Lars Luthman on 2009-10-12. +# No copyright claimed for this script. Do what you want with it. + +# For some reason Bash splits the command line not only at whitespace +# but also at ':' signs before putting the parts into COMP_WORDS. +# Since ':' is used in all URIs, which are what we want to complete, +# we have to put the URI back together before we can complete it +# and then cut off the parts we prepended from the completions. +# It probably breaks in some special cases but for most common uses +# it should work fine. + +function _lv2info() { + local uri cur opts w wn raw_reply len type + opts=`lv2ls | xargs -n1 echo -n " "` + + # This is the last "word", as split by Bash. + cur="${COMP_WORDS[COMP_CWORD]}" + w="$cur" + + # Add the previous word while it or this one is a word break character + for i in `seq $(( $COMP_CWORD - 1 )) -1 1`; do + wn="${COMP_WORDS[i]}" + if expr "$COMP_WORDBREAKS" : ".*$wn" > /dev/null; then + if expr "$COMP_WORDBREAKS" : ".*$w" > /dev/null; then + break + fi + fi + w="$wn" + uri="$w$uri" + done + + # Check the length of the words we prepend + len=${#uri} + uri="$uri$cur" + raw_reply="$(compgen -W "${opts}" -- ${uri})" + + # If we are listing alternatives, just print the full URIs. + type=`echo $COMP_TYPE | awk '{ printf "%c", $1 }'` + if expr "?!@%" : ".*$type" > /dev/null; then + COMPREPLY=( $raw_reply ) + return 0 + fi + + # Otherwise, strip the prepended words from all completion suggestions. + COMPREPLY=() + for i in $raw_reply; do + COMPREPLY=( ${COMPREPLY[@]} ${i:len} ) + done +} + +complete -F _lv2info lv2info + +# And the same for lv2jack. +complete -F _lv2info lv2jack diff --git a/tools/lv2apply.c b/tools/lv2apply.c new file mode 100644 index 0000000..e042999 --- /dev/null +++ b/tools/lv2apply.c @@ -0,0 +1,368 @@ +/* + Copyright 2007-2019 David Robillard + + 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. +*/ + +#include "lilv/lilv.h" + +#include "lv2/core/lv2.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__GNUC__) +# define LILV_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) +#else +# define LILV_LOG_FUNC(fmt, arg1) +#endif + +/** Control port value set from the command line */ +typedef struct Param { + const char* sym; ///< Port symbol + float value; ///< Control value +} Param; + +/** Port type (only float ports are supported) */ +typedef enum { TYPE_CONTROL, TYPE_AUDIO } PortType; + +/** Runtime port information */ +typedef struct { + const LilvPort* lilv_port; ///< Port description + PortType type; ///< Datatype + uint32_t index; ///< Port index + float value; ///< Control value (if applicable) + bool is_input; ///< True iff an input port + bool optional; ///< True iff connection optional +} Port; + +/** Application state */ +typedef struct { + LilvWorld* world; + const LilvPlugin* plugin; + LilvInstance* instance; + const char* in_path; + const char* out_path; + SNDFILE* in_file; + SNDFILE* out_file; + unsigned n_params; + Param* params; + unsigned n_ports; + unsigned n_audio_in; + unsigned n_audio_out; + Port* ports; +} LV2Apply; + +static int +fatal(LV2Apply* self, int status, const char* fmt, ...); + +/** Open a sound file with error handling. */ +static SNDFILE* +sopen(LV2Apply* self, const char* path, int mode, SF_INFO* fmt) +{ + SNDFILE* file = sf_open(path, mode, fmt); + const int st = sf_error(file); + if (st) { + fatal(self, 1, "Failed to open %s (%s)\n", path, sf_error_number(st)); + return NULL; + } + return file; +} + +/** Close a sound file with error handling. */ +static void +sclose(const char* path, SNDFILE* file) +{ + int st = 0; + if (file && (st = sf_close(file))) { + fatal(NULL, 1, "Failed to close %s (%s)\n", path, sf_error_number(st)); + } +} + +/** + Read a single frame from a file into an interleaved buffer. + + If more channels are required than are available in the file, the remaining + channels are distributed in a round-robin fashion (LRLRL). +*/ +static bool +sread(SNDFILE* file, unsigned file_chans, float* buf, unsigned buf_chans) +{ + const sf_count_t n_read = sf_readf_float(file, buf, 1); + for (unsigned i = file_chans - 1; i < buf_chans; ++i) { + buf[i] = buf[i % file_chans]; + } + return n_read == 1; +} + +/** Clean up all resources. */ +static int +cleanup(int status, LV2Apply* self) +{ + sclose(self->in_path, self->in_file); + sclose(self->out_path, self->out_file); + lilv_instance_free(self->instance); + lilv_world_free(self->world); + free(self->ports); + free(self->params); + return status; +} + +/** Print a fatal error and clean up for exit. */ +LILV_LOG_FUNC(3, 4) +static int +fatal(LV2Apply* self, int status, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, args); + va_end(args); + return self ? cleanup(status, self) : status; +} + +/** + Create port structures from data (via create_port()) for all ports. +*/ +static int +create_ports(LV2Apply* self) +{ + LilvWorld* world = self->world; + const uint32_t n_ports = lilv_plugin_get_num_ports(self->plugin); + + self->n_ports = n_ports; + self->ports = (Port*)calloc(self->n_ports, sizeof(Port)); + + /* Get default values for all ports */ + float* values = (float*)calloc(n_ports, sizeof(float)); + lilv_plugin_get_port_ranges_float(self->plugin, NULL, NULL, values); + + LilvNode* lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort); + LilvNode* lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort); + LilvNode* lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort); + LilvNode* lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort); + LilvNode* lv2_connectionOptional = + lilv_new_uri(world, LV2_CORE__connectionOptional); + + for (uint32_t i = 0; i < n_ports; ++i) { + Port* port = &self->ports[i]; + const LilvPort* lport = lilv_plugin_get_port_by_index(self->plugin, i); + + port->lilv_port = lport; + port->index = i; + port->value = isnan(values[i]) ? 0.0f : values[i]; + port->optional = + lilv_port_has_property(self->plugin, lport, lv2_connectionOptional); + + /* Check if port is an input or output */ + if (lilv_port_is_a(self->plugin, lport, lv2_InputPort)) { + port->is_input = true; + } else if (!lilv_port_is_a(self->plugin, lport, lv2_OutputPort) && + !port->optional) { + return fatal(self, 1, "Port %u is neither input nor output\n", i); + } + + /* Check if port is an audio or control port */ + if (lilv_port_is_a(self->plugin, lport, lv2_ControlPort)) { + port->type = TYPE_CONTROL; + } else if (lilv_port_is_a(self->plugin, lport, lv2_AudioPort)) { + port->type = TYPE_AUDIO; + if (port->is_input) { + ++self->n_audio_in; + } else { + ++self->n_audio_out; + } + } else if (!port->optional) { + return fatal(self, 1, "Port %u has unsupported type\n", i); + } + } + + lilv_node_free(lv2_connectionOptional); + lilv_node_free(lv2_ControlPort); + lilv_node_free(lv2_AudioPort); + lilv_node_free(lv2_OutputPort); + lilv_node_free(lv2_InputPort); + free(values); + + return 0; +} + +static void +print_version(void) +{ + printf("lv2apply (lilv) " LILV_VERSION "\n" + "Copyright 2007-2021 David Robillard \n" + "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 int +print_usage(int status) +{ + fprintf(status ? stderr : stdout, + "Usage: lv2apply [OPTION]... PLUGIN_URI\n" + "Apply an LV2 plugin to an audio file.\n\n" + " -i IN_FILE Input file\n" + " -o OUT_FILE Output file\n" + " -c SYM VAL Control value\n" + " --help Display this help and exit\n" + " --version Display version information and exit\n"); + return status; +} + +int +main(int argc, char** argv) +{ + LV2Apply self = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 0, NULL}; + + /* Parse command line arguments */ + const char* plugin_uri = NULL; + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--version")) { + free(self.params); + print_version(); + return 0; + } + + if (!strcmp(argv[i], "--help")) { + free(self.params); + return print_usage(0); + } + + if (!strcmp(argv[i], "-i")) { + self.in_path = argv[++i]; + } else if (!strcmp(argv[i], "-o")) { + self.out_path = argv[++i]; + } else if (!strcmp(argv[i], "-c")) { + if (argc < i + 3) { + return fatal(&self, 1, "Missing argument for -c\n"); + } + self.params = + (Param*)realloc(self.params, ++self.n_params * sizeof(Param)); + self.params[self.n_params - 1].sym = argv[++i]; + self.params[self.n_params - 1].value = atof(argv[++i]); + } else if (argv[i][0] == '-') { + free(self.params); + return print_usage(1); + } else if (i == argc - 1) { + plugin_uri = argv[i]; + } + } + + /* Check that required arguments are given */ + if (!self.in_path || !self.out_path || !plugin_uri) { + free(self.params); + return print_usage(1); + } + + /* Create world and plugin URI */ + self.world = lilv_world_new(); + LilvNode* uri = lilv_new_uri(self.world, plugin_uri); + if (!uri) { + return fatal(&self, 2, "Invalid plugin URI <%s>\n", plugin_uri); + } + + /* Discover world */ + lilv_world_load_all(self.world); + + /* Get plugin */ + const LilvPlugins* plugins = lilv_world_get_all_plugins(self.world); + const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, uri); + lilv_node_free(uri); + if (!(self.plugin = plugin)) { + return fatal(&self, 3, "Plugin <%s> not found\n", plugin_uri); + } + + /* Open input file */ + SF_INFO in_fmt = {0, 0, 0, 0, 0, 0}; + if (!(self.in_file = sopen(&self, self.in_path, SFM_READ, &in_fmt))) { + return 4; + } + + /* Create port structures */ + if (create_ports(&self)) { + return 5; + } + + if (self.n_audio_in == 0 || + (in_fmt.channels != (int)self.n_audio_in && in_fmt.channels != 1)) { + return fatal(&self, + 6, + "Unable to map %d inputs to %u ports\n", + in_fmt.channels, + self.n_audio_in); + } + + /* Set control values */ + for (unsigned i = 0; i < self.n_params; ++i) { + const Param* param = &self.params[i]; + LilvNode* sym = lilv_new_string(self.world, param->sym); + const LilvPort* port = lilv_plugin_get_port_by_symbol(plugin, sym); + lilv_node_free(sym); + if (!port) { + return fatal(&self, 7, "Unknown port `%s'\n", param->sym); + } + + self.ports[lilv_port_get_index(plugin, port)].value = param->value; + } + + /* Open output file */ + SF_INFO out_fmt = in_fmt; + out_fmt.channels = self.n_audio_out; + if (!(self.out_file = sopen(&self, self.out_path, SFM_WRITE, &out_fmt))) { + free(self.ports); + return 8; + } + + /* Instantiate plugin and connect ports */ + const uint32_t n_ports = lilv_plugin_get_num_ports(plugin); + float in_buf[self.n_audio_in > 0 ? self.n_audio_in : 1]; + float out_buf[self.n_audio_out > 0 ? self.n_audio_out : 1]; + self.instance = lilv_plugin_instantiate(self.plugin, in_fmt.samplerate, NULL); + for (uint32_t p = 0, i = 0, o = 0; p < n_ports; ++p) { + if (self.ports[p].type == TYPE_CONTROL) { + lilv_instance_connect_port(self.instance, p, &self.ports[p].value); + } else if (self.ports[p].type == TYPE_AUDIO) { + if (self.ports[p].is_input) { + lilv_instance_connect_port(self.instance, p, in_buf + i++); + } else { + lilv_instance_connect_port(self.instance, p, out_buf + o++); + } + } else { + lilv_instance_connect_port(self.instance, p, NULL); + } + } + + /* Ports are now connected to buffers in interleaved format, so we can run + a single frame at a time and avoid having to interleave buffers to + read/write from/to sndfile. */ + + lilv_instance_activate(self.instance); + while (sread(self.in_file, in_fmt.channels, in_buf, self.n_audio_in)) { + lilv_instance_run(self.instance, 1); + if (sf_writef_float(self.out_file, out_buf, 1) != 1) { + return fatal(&self, 9, "Failed to write to output file\n"); + } + } + lilv_instance_deactivate(self.instance); + + return cleanup(0, &self); +} diff --git a/tools/lv2bench.c b/tools/lv2bench.c new file mode 100644 index 0000000..f0746cb --- /dev/null +++ b/tools/lv2bench.c @@ -0,0 +1,282 @@ +/* + Copyright 2012-2019 David Robillard + + 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. +*/ + +#include "lilv/lilv.h" +#include "lv2/atom/atom.h" +#include "lv2/core/lv2.h" +#include "lv2/urid/urid.h" + +#include "bench.h" +#include "lilv_config.h" +#include "uri_table.h" + +#include +#include +#include +#include +#include +#include +#include + +static LilvNode* atom_AtomPort = NULL; +static LilvNode* atom_Sequence = NULL; +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 bool full_output = false; + +static void +print_version(void) +{ + printf("lv2bench (lilv) " LILV_VERSION "\n" + "Copyright 2012-2021 David Robillard \n" + "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("lv2bench - Benchmark all installed and supported LV2 plugins.\n"); + printf("Usage: lv2bench [OPTIONS] [PLUGIN_URI]\n"); + printf("\n"); + printf(" -b BLOCK_SIZE Specify block size, in audio frames.\n"); + printf(" -f, --full Full plottable output.\n"); + printf(" -h, --help Display this help and exit.\n"); + printf(" -n FRAMES Total number of audio frames to process\n"); + printf(" --version Display version information and exit\n"); +} + +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 * 2ul, sizeof(float)); + float* const in = buf; + float* const out = buf + block_size; + if (!buf) { + fprintf(stderr, "Out of memory\n"); + return 0.0; + } + + const size_t atom_capacity = 1024; + + LV2_Atom_Sequence seq_in = {{sizeof(LV2_Atom_Sequence_Body), + uri_table_map(&uri_table, LV2_ATOM__Sequence)}, + {0, 0}}; + + LV2_Atom_Sequence* seq_out = + (LV2_Atom_Sequence*)malloc(sizeof(LV2_Atom_Sequence) + atom_capacity); + + 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(seq_out); + 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(seq_out); + free(buf); + uri_table_destroy(&uri_table); + return 0.0; + } + + const uint32_t n_ports = lilv_plugin_get_num_ports(p); + float* const mins = (float*)calloc(n_ports, sizeof(float)); + float* const maxes = (float*)calloc(n_ports, sizeof(float)); + float* const controls = (float*)calloc(n_ports, sizeof(float)); + lilv_plugin_get_port_ranges_float(p, mins, maxes, controls); + + 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)) { + if (isnan(controls[index])) { + if (!isnan(mins[index])) { + controls[index] = mins[index]; + } else if (!isnan(maxes[index])) { + controls[index] = maxes[index]; + } else { + controls[index] = 0.0; + } + } + 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 %u neither input nor output, skipping\n", + uri, + index); + lilv_instance_free(instance); + free(seq_out); + free(buf); + free(controls); + uri_table_destroy(&uri_table); + return 0.0; + } + } else if (lilv_port_is_a(p, port, atom_AtomPort)) { + if (lilv_port_is_a(p, port, lv2_InputPort)) { + lilv_instance_connect_port(instance, index, &seq_in); + } else { + lilv_instance_connect_port(instance, index, seq_out); + } + } else { + fprintf(stderr, "<%s> port %u has unknown type, skipping\n", uri, index); + lilv_instance_free(instance); + free(seq_out); + 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) { + seq_in.atom.size = sizeof(LV2_Atom_Sequence_Body); + seq_in.atom.type = uri_table_map(&uri_table, LV2_ATOM__Sequence); + seq_out->atom.size = atom_capacity; + seq_out->atom.type = uri_table_map(&uri_table, LV2_ATOM__Chunk); + + lilv_instance_run(instance, block_size); + } + const double elapsed = bench_end(&ts); + + lilv_instance_deactivate(instance); + lilv_instance_free(instance); + free(controls); + free(maxes); + free(mins); + free(seq_out); + + uri_table_destroy(&uri_table); + + if (full_output) { + printf("%u %u ", block_size, sample_count); + } + printf("%lf %s\n", elapsed, uri); + + free(buf); + return elapsed; +} + +int +main(int argc, char** argv) +{ + uint32_t block_size = 512; + uint32_t sample_count = (1 << 19); + + int a = 1; + for (; a < argc; ++a) { + if (!strcmp(argv[a], "--version")) { + print_version(); + return 0; + } + + if (!strcmp(argv[a], "--help")) { + print_usage(); + return 0; + } + + if (!strcmp(argv[a], "-f")) { + full_output = true; + } else if (!strcmp(argv[a], "-n") && (a + 1 < argc)) { + sample_count = atoi(argv[++a]); + } else if (!strcmp(argv[a], "-b") && (a + 1 < argc)) { + block_size = atoi(argv[++a]); + } else if (argv[a][0] != '-') { + break; + } else { + print_usage(); + return 1; + } + } + + const char* const plugin_uri_str = (a < argc ? argv[a++] : NULL); + + LilvWorld* world = lilv_world_new(); + lilv_world_load_all(world); + + atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort); + atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence); + 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); + + if (full_output) { + printf("# Block Samples Time Plugin\n"); + } + + const LilvPlugins* plugins = lilv_world_get_all_plugins(world); + if (plugin_uri_str) { + LilvNode* uri = lilv_new_uri(world, plugin_uri_str); + bench(lilv_plugins_get_by_uri(plugins, uri), sample_count, block_size); + lilv_node_free(uri); + } else { + LILV_FOREACH (plugins, i, plugins) { + bench(lilv_plugins_get(plugins, i), sample_count, block_size); + } + } + + 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_node_free(atom_Sequence); + lilv_node_free(atom_AtomPort); + + lilv_world_free(world); + + return 0; +} diff --git a/tools/lv2info.c b/tools/lv2info.c new file mode 100644 index 0000000..81a3ed4 --- /dev/null +++ b/tools/lv2info.c @@ -0,0 +1,456 @@ +/* + Copyright 2007-2019 David Robillard + + 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. +*/ + +#include "lilv_config.h" + +#include "lilv/lilv.h" +#include "lv2/core/lv2.h" +#include "lv2/event/event.h" +#include "lv2/port-groups/port-groups.h" +#include "lv2/presets/presets.h" + +#include +#include +#include +#include +#include +#include + +static LilvNode* applies_to_pred = NULL; +static LilvNode* control_class = NULL; +static LilvNode* event_class = NULL; +static LilvNode* group_pred = NULL; +static LilvNode* label_pred = NULL; +static LilvNode* preset_class = NULL; +static LilvNode* designation_pred = NULL; +static LilvNode* supports_event_pred = NULL; + +static void +print_port(const LilvPlugin* p, + uint32_t index, + float* mins, + float* maxes, + float* defaults) +{ + const LilvPort* port = lilv_plugin_get_port_by_index(p, index); + + printf("\n\tPort %u:\n", index); + + if (!port) { + printf("\t\tERROR: Illegal/nonexistent port\n"); + return; + } + + bool first = true; + + const LilvNodes* classes = lilv_port_get_classes(p, port); + printf("\t\tType: "); + LILV_FOREACH (nodes, i, classes) { + const LilvNode* value = lilv_nodes_get(classes, i); + if (!first) { + printf("\n\t\t "); + } + printf("%s", lilv_node_as_uri(value)); + first = false; + } + + if (lilv_port_is_a(p, port, event_class)) { + LilvNodes* supported = lilv_port_get_value(p, port, supports_event_pred); + if (lilv_nodes_size(supported) > 0) { + printf("\n\t\tSupported events:\n"); + LILV_FOREACH (nodes, i, supported) { + const LilvNode* value = lilv_nodes_get(supported, i); + printf("\t\t\t%s\n", lilv_node_as_uri(value)); + } + } + lilv_nodes_free(supported); + } + + LilvScalePoints* points = lilv_port_get_scale_points(p, port); + if (points) { + printf("\n\t\tScale Points:\n"); + } + LILV_FOREACH (scale_points, i, points) { + const LilvScalePoint* point = lilv_scale_points_get(points, i); + printf("\t\t\t%s = \"%s\"\n", + lilv_node_as_string(lilv_scale_point_get_value(point)), + lilv_node_as_string(lilv_scale_point_get_label(point))); + } + lilv_scale_points_free(points); + + const LilvNode* sym = lilv_port_get_symbol(p, port); + printf("\n\t\tSymbol: %s\n", lilv_node_as_string(sym)); + + LilvNode* name = lilv_port_get_name(p, port); + printf("\t\tName: %s\n", lilv_node_as_string(name)); + lilv_node_free(name); + + LilvNodes* groups = lilv_port_get_value(p, port, group_pred); + if (lilv_nodes_size(groups) > 0) { + printf("\t\tGroup: %s\n", + lilv_node_as_string(lilv_nodes_get_first(groups))); + } + lilv_nodes_free(groups); + + LilvNodes* designations = lilv_port_get_value(p, port, designation_pred); + if (lilv_nodes_size(designations) > 0) { + printf("\t\tDesignation: %s\n", + lilv_node_as_string(lilv_nodes_get_first(designations))); + } + lilv_nodes_free(designations); + + if (lilv_port_is_a(p, port, control_class)) { + if (!isnan(mins[index])) { + printf("\t\tMinimum: %f\n", mins[index]); + } + if (!isnan(maxes[index])) { + printf("\t\tMaximum: %f\n", maxes[index]); + } + if (!isnan(defaults[index])) { + printf("\t\tDefault: %f\n", defaults[index]); + } + } + + LilvNodes* properties = lilv_port_get_properties(p, port); + if (lilv_nodes_size(properties) > 0) { + printf("\t\tProperties: "); + } + first = true; + LILV_FOREACH (nodes, i, properties) { + if (!first) { + printf("\t\t "); + } + printf("%s\n", lilv_node_as_uri(lilv_nodes_get(properties, i))); + first = false; + } + if (lilv_nodes_size(properties) > 0) { + printf("\n"); + } + lilv_nodes_free(properties); +} + +static void +print_plugin(LilvWorld* world, const LilvPlugin* p) +{ + LilvNode* val = NULL; + + printf("%s\n\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); + + val = lilv_plugin_get_name(p); + if (val) { + printf("\tName: %s\n", lilv_node_as_string(val)); + lilv_node_free(val); + } + + const LilvPluginClass* pclass = lilv_plugin_get_class(p); + const LilvNode* class_label = lilv_plugin_class_get_label(pclass); + if (class_label) { + printf("\tClass: %s\n", lilv_node_as_string(class_label)); + } + + val = lilv_plugin_get_author_name(p); + if (val) { + printf("\tAuthor: %s\n", lilv_node_as_string(val)); + lilv_node_free(val); + } + + val = lilv_plugin_get_author_email(p); + if (val) { + printf("\tAuthor Email: %s\n", lilv_node_as_uri(val)); + lilv_node_free(val); + } + + val = lilv_plugin_get_author_homepage(p); + if (val) { + printf("\tAuthor Homepage: %s\n", lilv_node_as_uri(val)); + lilv_node_free(val); + } + + if (lilv_plugin_has_latency(p)) { + uint32_t latency_port = lilv_plugin_get_latency_port_index(p); + printf("\tHas latency: yes, reported by port %u\n", latency_port); + } else { + printf("\tHas latency: no\n"); + } + + printf("\tBundle: %s\n", + lilv_node_as_uri(lilv_plugin_get_bundle_uri(p))); + + const LilvNode* binary_uri = lilv_plugin_get_library_uri(p); + if (binary_uri) { + printf("\tBinary: %s\n", + lilv_node_as_uri(lilv_plugin_get_library_uri(p))); + } + + LilvUIs* uis = lilv_plugin_get_uis(p); + if (lilv_nodes_size(uis) > 0) { + printf("\tUIs:\n"); + LILV_FOREACH (uis, i, uis) { + const LilvUI* ui = lilv_uis_get(uis, i); + printf("\t\t%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui))); + + const char* binary = lilv_node_as_uri(lilv_ui_get_binary_uri(ui)); + + const LilvNodes* types = lilv_ui_get_classes(ui); + LILV_FOREACH (nodes, t, types) { + printf("\t\t\tClass: %s\n", + lilv_node_as_uri(lilv_nodes_get(types, t))); + } + + if (binary) { + printf("\t\t\tBinary: %s\n", binary); + } + + printf("\t\t\tBundle: %s\n", + lilv_node_as_uri(lilv_ui_get_bundle_uri(ui))); + } + } + lilv_uis_free(uis); + + printf("\tData URIs: "); + const LilvNodes* data_uris = lilv_plugin_get_data_uris(p); + bool first = true; + LILV_FOREACH (nodes, i, data_uris) { + if (!first) { + printf("\n\t "); + } + printf("%s", lilv_node_as_uri(lilv_nodes_get(data_uris, i))); + first = false; + } + printf("\n"); + + /* Required Features */ + + LilvNodes* features = lilv_plugin_get_required_features(p); + if (features) { + printf("\tRequired Features: "); + } + first = true; + LILV_FOREACH (nodes, i, features) { + if (!first) { + printf("\n\t "); + } + printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); + first = false; + } + if (features) { + printf("\n"); + } + lilv_nodes_free(features); + + /* Optional Features */ + + features = lilv_plugin_get_optional_features(p); + if (features) { + printf("\tOptional Features: "); + } + first = true; + LILV_FOREACH (nodes, i, features) { + if (!first) { + printf("\n\t "); + } + printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); + first = false; + } + if (features) { + printf("\n"); + } + lilv_nodes_free(features); + + /* Extension Data */ + + LilvNodes* data = lilv_plugin_get_extension_data(p); + if (data) { + printf("\tExtension Data: "); + } + first = true; + LILV_FOREACH (nodes, i, data) { + if (!first) { + printf("\n\t "); + } + printf("%s", lilv_node_as_uri(lilv_nodes_get(data, i))); + first = false; + } + if (data) { + printf("\n"); + } + lilv_nodes_free(data); + + /* Presets */ + + LilvNodes* presets = lilv_plugin_get_related(p, preset_class); + if (presets) { + printf("\tPresets: \n"); + } + LILV_FOREACH (nodes, i, presets) { + const LilvNode* preset = lilv_nodes_get(presets, i); + lilv_world_load_resource(world, preset); + LilvNodes* titles = lilv_world_find_nodes(world, preset, label_pred, NULL); + if (titles) { + const LilvNode* title = lilv_nodes_get_first(titles); + printf("\t %s\n", lilv_node_as_string(title)); + lilv_nodes_free(titles); + } else { + fprintf(stderr, + "Preset <%s> has no rdfs:label\n", + lilv_node_as_string(lilv_nodes_get(presets, i))); + } + } + lilv_nodes_free(presets); + + /* Ports */ + + const uint32_t num_ports = lilv_plugin_get_num_ports(p); + float* mins = (float*)calloc(num_ports, sizeof(float)); + float* maxes = (float*)calloc(num_ports, sizeof(float)); + float* defaults = (float*)calloc(num_ports, sizeof(float)); + lilv_plugin_get_port_ranges_float(p, mins, maxes, defaults); + + for (uint32_t i = 0; i < num_ports; ++i) { + print_port(p, i, mins, maxes, defaults); + } + + free(mins); + free(maxes); + free(defaults); +} + +static void +print_version(void) +{ + printf("lv2info (lilv) " LILV_VERSION "\n" + "Copyright 2007-2021 David Robillard \n" + "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: lv2info [OPTION]... PLUGIN_URI\n" + "Print information about an installed LV2 plugin.\n\n" + " -p FILE Write Turtle description of plugin to FILE\n" + " -m FILE Add record of plugin to manifest FILE\n" + " --help Display this help and exit\n" + " --version Display version information and exit\n\n" + "For -p and -m, Turtle files are appended to (not overwritten),\n" + "and @prefix directives are only written if the file was empty.\n" + "This allows several plugins to be added to a single file.\n"); +} + +int +main(int argc, char** argv) +{ + if (argc == 1) { + print_usage(); + return 1; + } + + const char* plugin_file = NULL; + const char* manifest_file = NULL; + const char* plugin_uri = NULL; + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--version")) { + print_version(); + return 0; + } + + if (!strcmp(argv[i], "--help")) { + print_usage(); + return 0; + } + + if (!strcmp(argv[i], "-p")) { + plugin_file = argv[++i]; + } else if (!strcmp(argv[i], "-m")) { + manifest_file = argv[++i]; + } else if (argv[i][0] == '-') { + print_usage(); + return 1; + } else if (i == argc - 1) { + plugin_uri = argv[i]; + } + } + + int ret = 0; + + LilvWorld* world = lilv_world_new(); + lilv_world_load_all(world); + + LilvNode* uri = lilv_new_uri(world, plugin_uri); + if (!uri) { + fprintf(stderr, "Invalid plugin URI\n"); + lilv_world_free(world); + return 1; + } + + applies_to_pred = lilv_new_uri(world, LV2_CORE__appliesTo); + control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT); + event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT); + group_pred = lilv_new_uri(world, LV2_PORT_GROUPS__group); + label_pred = lilv_new_uri(world, LILV_NS_RDFS "label"); + preset_class = lilv_new_uri(world, LV2_PRESETS__Preset); + designation_pred = lilv_new_uri(world, LV2_CORE__designation); + supports_event_pred = lilv_new_uri(world, LV2_EVENT__supportsEvent); + + const LilvPlugins* plugins = lilv_world_get_all_plugins(world); + const LilvPlugin* p = lilv_plugins_get_by_uri(plugins, uri); + + if (p && plugin_file) { + LilvNode* base = lilv_new_file_uri(world, NULL, plugin_file); + + FILE* plugin_fd = fopen(plugin_file, "a"); + if (plugin_fd) { + lilv_plugin_write_description(world, p, base, plugin_fd); + fclose(plugin_fd); + } else { + fprintf(stderr, "error: Failed to open %s\n", plugin_file); + } + + if (manifest_file) { + FILE* manifest_fd = fopen(manifest_file, "a"); + if (manifest_fd) { + lilv_plugin_write_manifest_entry( + world, p, base, manifest_fd, plugin_file); + fclose(manifest_fd); + } else { + fprintf(stderr, "error: Failed to open %s\n", manifest_file); + } + } + lilv_node_free(base); + } else if (p) { + print_plugin(world, p); + } else { + fprintf(stderr, "Plugin not found.\n"); + } + + ret = (p != NULL ? 0 : -1); + + lilv_node_free(uri); + + lilv_node_free(supports_event_pred); + lilv_node_free(designation_pred); + lilv_node_free(preset_class); + lilv_node_free(label_pred); + lilv_node_free(group_pred); + lilv_node_free(event_class); + lilv_node_free(control_class); + lilv_node_free(applies_to_pred); + + lilv_world_free(world); + return ret; +} diff --git a/tools/lv2ls.c b/tools/lv2ls.c new file mode 100644 index 0000000..9226b89 --- /dev/null +++ b/tools/lv2ls.c @@ -0,0 +1,94 @@ +/* + Copyright 2007-2019 David Robillard + + 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. +*/ + +#include "lilv_config.h" + +#include "lilv/lilv.h" + +#include +#include +#include + +static void +list_plugins(const LilvPlugins* list, bool show_names) +{ + LILV_FOREACH (plugins, i, list) { + const LilvPlugin* p = lilv_plugins_get(list, i); + if (show_names) { + LilvNode* n = lilv_plugin_get_name(p); + printf("%s\n", lilv_node_as_string(n)); + lilv_node_free(n); + } else { + printf("%s\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); + } + } +} + +static void +print_version(void) +{ + printf("lv2ls (lilv) " LILV_VERSION "\n" + "Copyright 2007-2021 David Robillard \n" + "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: lv2ls [OPTION]...\n"); + printf("List all installed LV2 plugins.\n"); + printf("\n"); + printf(" -n, --names Show names instead of URIs\n"); + printf(" --help Display this help and exit\n"); + printf(" --version Display version information and exit\n"); + printf("\n"); + printf("The environment variable LV2_PATH can be used to control where\n"); + printf( + "this (and all other lilv based LV2 hosts) will search for plugins.\n"); +} + +int +main(int argc, char** argv) +{ + bool show_names = false; + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--names") || !strcmp(argv[i], "-n")) { + show_names = true; + } else 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); + + const LilvPlugins* plugins = lilv_world_get_all_plugins(world); + + list_plugins(plugins, show_names); + + lilv_world_free(world); + + return 0; +} diff --git a/tools/meson.build b/tools/meson.build new file mode 100644 index 0000000..59249f2 --- /dev/null +++ b/tools/meson.build @@ -0,0 +1,83 @@ +# Copyright 2021-2022 David Robillard +# SPDX-License-Identifier: CC0-1.0 OR ISC + +include_dirs = include_directories('../src') + +############################# +# "Basic" (Lilv-Only) Tools # +############################# + +basic_tools = [ + 'lv2info', + 'lv2ls', +] + +foreach tool : basic_tools + executable( + tool, + files(tool + '.c'), + c_args: c_suppressions, + dependencies: lilv_dep, + include_directories: include_dirs, + install: true, + ) + + install_man(files('..' / 'doc' / tool + '.1')) +endforeach + +install_data( + files('lilv.bash_completion'), + install_dir: get_option('sysconfdir') / 'bash_completion.d', + rename: 'lilv', +) + +########################### +# lv2apply (uses sndfile) # +########################### + +sndfile_dep = dependency( + 'sndfile', + version: '>= 1.0.0', + required: get_option('tools'), +) + +if sndfile_dep.found() + executable( + 'lv2apply', + files('lv2apply.c'), + c_args: c_suppressions, + dependencies: [lilv_dep, sndfile_dep], + include_directories: include_dirs, + install: true, + ) + + install_man(files('..' / 'doc' / 'lv2apply.1')) +endif + +################################# +# lv2bench (uses clock_gettime) # +################################# + +if host_machine.system() != 'windows' + rt_dep = cc.find_library('rt', required: false) + + clock_gettime_code = '''#include +int main(void) { struct timespec t; return clock_gettime(CLOCK_MONOTONIC, &t); } +''' + + if cc.compiles(clock_gettime_code, + args: platform_defines, + dependencies: [rt_dep], + name: 'clock_gettime') + executable( + 'lv2bench', + files('lv2bench.c'), + c_args: c_suppressions, + dependencies: [lilv_dep, rt_dep, sndfile_dep], + include_directories: include_dirs, + install: true, + ) + + install_man(files('..' / 'doc' / 'lv2bench.1')) + endif +endif diff --git a/tools/uri_table.h b/tools/uri_table.h new file mode 100644 index 0000000..5a337a3 --- /dev/null +++ b/tools/uri_table.h @@ -0,0 +1,80 @@ +/* + Copyright 2011-2019 David Robillard + + 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 + +#include "lv2/urid/urid.h" + +#include +#include + +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) +{ + for (size_t i = 0; i < table->n_uris; ++i) { + free(table->uris[i]); + } + + 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] = (char*)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/utils/bench.h b/utils/bench.h deleted file mode 100644 index 505631f..0000000 --- a/utils/bench.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright 2011-2019 David Robillard - - 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 200809L - -#include - -typedef struct timespec BenchmarkTime; - -static inline double -bench_elapsed_s(const BenchmarkTime* start, const BenchmarkTime* end) -{ - return ((end->tv_sec - start->tv_sec) + - ((end->tv_nsec - start->tv_nsec) * 0.000000001)); -} - -static inline BenchmarkTime -bench_start(void) -{ - BenchmarkTime start_t; - clock_gettime(CLOCK_REALTIME, &start_t); - return start_t; -} - -static inline double -bench_end(const BenchmarkTime* start_t) -{ - BenchmarkTime end_t; - clock_gettime(CLOCK_REALTIME, &end_t); - return bench_elapsed_s(start_t, &end_t); -} - -#endif /* BENCH_H */ diff --git a/utils/lilv-bench.c b/utils/lilv-bench.c deleted file mode 100644 index 56b7fee..0000000 --- a/utils/lilv-bench.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright 2007-2012 David Robillard - - 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. -*/ - -#include "lilv/lilv.h" - -int -main(void) -{ - LilvWorld* world = lilv_world_new(); - lilv_world_load_all(world); - - const LilvPlugins* plugins = lilv_world_get_all_plugins(world); - LILV_FOREACH (plugins, p, plugins) { - const LilvPlugin* plugin = lilv_plugins_get(plugins, p); - lilv_plugin_get_class(plugin); - } - - lilv_world_free(world); - - return 0; -} diff --git a/utils/lilv.bash_completion b/utils/lilv.bash_completion deleted file mode 100644 index 4a553a7..0000000 --- a/utils/lilv.bash_completion +++ /dev/null @@ -1,59 +0,0 @@ -# Bash auto-completion script written for lv2info and lv2jack. -# Could be adapted to any other program that takes an -# LV2 plugin URI as parameter. - -# Updated for Lilv by David Robillard on 2012-01-08. -# Written by Lars Luthman on 2009-10-12. -# No copyright claimed for this script. Do what you want with it. - -# For some reason Bash splits the command line not only at whitespace -# but also at ':' signs before putting the parts into COMP_WORDS. -# Since ':' is used in all URIs, which are what we want to complete, -# we have to put the URI back together before we can complete it -# and then cut off the parts we prepended from the completions. -# It probably breaks in some special cases but for most common uses -# it should work fine. - -function _lv2info() { - local uri cur opts w wn raw_reply len type - opts=`lv2ls | xargs -n1 echo -n " "` - - # This is the last "word", as split by Bash. - cur="${COMP_WORDS[COMP_CWORD]}" - w="$cur" - - # Add the previous word while it or this one is a word break character - for i in `seq $(( $COMP_CWORD - 1 )) -1 1`; do - wn="${COMP_WORDS[i]}" - if expr "$COMP_WORDBREAKS" : ".*$wn" > /dev/null; then - if expr "$COMP_WORDBREAKS" : ".*$w" > /dev/null; then - break - fi - fi - w="$wn" - uri="$w$uri" - done - - # Check the length of the words we prepend - len=${#uri} - uri="$uri$cur" - raw_reply="$(compgen -W "${opts}" -- ${uri})" - - # If we are listing alternatives, just print the full URIs. - type=`echo $COMP_TYPE | awk '{ printf "%c", $1 }'` - if expr "?!@%" : ".*$type" > /dev/null; then - COMPREPLY=( $raw_reply ) - return 0 - fi - - # Otherwise, strip the prepended words from all completion suggestions. - COMPREPLY=() - for i in $raw_reply; do - COMPREPLY=( ${COMPREPLY[@]} ${i:len} ) - done -} - -complete -F _lv2info lv2info - -# And the same for lv2jack. -complete -F _lv2info lv2jack diff --git a/utils/lv2apply.c b/utils/lv2apply.c deleted file mode 100644 index e042999..0000000 --- a/utils/lv2apply.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - Copyright 2007-2019 David Robillard - - 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. -*/ - -#include "lilv/lilv.h" - -#include "lv2/core/lv2.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__GNUC__) -# define LILV_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) -#else -# define LILV_LOG_FUNC(fmt, arg1) -#endif - -/** Control port value set from the command line */ -typedef struct Param { - const char* sym; ///< Port symbol - float value; ///< Control value -} Param; - -/** Port type (only float ports are supported) */ -typedef enum { TYPE_CONTROL, TYPE_AUDIO } PortType; - -/** Runtime port information */ -typedef struct { - const LilvPort* lilv_port; ///< Port description - PortType type; ///< Datatype - uint32_t index; ///< Port index - float value; ///< Control value (if applicable) - bool is_input; ///< True iff an input port - bool optional; ///< True iff connection optional -} Port; - -/** Application state */ -typedef struct { - LilvWorld* world; - const LilvPlugin* plugin; - LilvInstance* instance; - const char* in_path; - const char* out_path; - SNDFILE* in_file; - SNDFILE* out_file; - unsigned n_params; - Param* params; - unsigned n_ports; - unsigned n_audio_in; - unsigned n_audio_out; - Port* ports; -} LV2Apply; - -static int -fatal(LV2Apply* self, int status, const char* fmt, ...); - -/** Open a sound file with error handling. */ -static SNDFILE* -sopen(LV2Apply* self, const char* path, int mode, SF_INFO* fmt) -{ - SNDFILE* file = sf_open(path, mode, fmt); - const int st = sf_error(file); - if (st) { - fatal(self, 1, "Failed to open %s (%s)\n", path, sf_error_number(st)); - return NULL; - } - return file; -} - -/** Close a sound file with error handling. */ -static void -sclose(const char* path, SNDFILE* file) -{ - int st = 0; - if (file && (st = sf_close(file))) { - fatal(NULL, 1, "Failed to close %s (%s)\n", path, sf_error_number(st)); - } -} - -/** - Read a single frame from a file into an interleaved buffer. - - If more channels are required than are available in the file, the remaining - channels are distributed in a round-robin fashion (LRLRL). -*/ -static bool -sread(SNDFILE* file, unsigned file_chans, float* buf, unsigned buf_chans) -{ - const sf_count_t n_read = sf_readf_float(file, buf, 1); - for (unsigned i = file_chans - 1; i < buf_chans; ++i) { - buf[i] = buf[i % file_chans]; - } - return n_read == 1; -} - -/** Clean up all resources. */ -static int -cleanup(int status, LV2Apply* self) -{ - sclose(self->in_path, self->in_file); - sclose(self->out_path, self->out_file); - lilv_instance_free(self->instance); - lilv_world_free(self->world); - free(self->ports); - free(self->params); - return status; -} - -/** Print a fatal error and clean up for exit. */ -LILV_LOG_FUNC(3, 4) -static int -fatal(LV2Apply* self, int status, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - fprintf(stderr, "error: "); - vfprintf(stderr, fmt, args); - va_end(args); - return self ? cleanup(status, self) : status; -} - -/** - Create port structures from data (via create_port()) for all ports. -*/ -static int -create_ports(LV2Apply* self) -{ - LilvWorld* world = self->world; - const uint32_t n_ports = lilv_plugin_get_num_ports(self->plugin); - - self->n_ports = n_ports; - self->ports = (Port*)calloc(self->n_ports, sizeof(Port)); - - /* Get default values for all ports */ - float* values = (float*)calloc(n_ports, sizeof(float)); - lilv_plugin_get_port_ranges_float(self->plugin, NULL, NULL, values); - - LilvNode* lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort); - LilvNode* lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort); - LilvNode* lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort); - LilvNode* lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort); - LilvNode* lv2_connectionOptional = - lilv_new_uri(world, LV2_CORE__connectionOptional); - - for (uint32_t i = 0; i < n_ports; ++i) { - Port* port = &self->ports[i]; - const LilvPort* lport = lilv_plugin_get_port_by_index(self->plugin, i); - - port->lilv_port = lport; - port->index = i; - port->value = isnan(values[i]) ? 0.0f : values[i]; - port->optional = - lilv_port_has_property(self->plugin, lport, lv2_connectionOptional); - - /* Check if port is an input or output */ - if (lilv_port_is_a(self->plugin, lport, lv2_InputPort)) { - port->is_input = true; - } else if (!lilv_port_is_a(self->plugin, lport, lv2_OutputPort) && - !port->optional) { - return fatal(self, 1, "Port %u is neither input nor output\n", i); - } - - /* Check if port is an audio or control port */ - if (lilv_port_is_a(self->plugin, lport, lv2_ControlPort)) { - port->type = TYPE_CONTROL; - } else if (lilv_port_is_a(self->plugin, lport, lv2_AudioPort)) { - port->type = TYPE_AUDIO; - if (port->is_input) { - ++self->n_audio_in; - } else { - ++self->n_audio_out; - } - } else if (!port->optional) { - return fatal(self, 1, "Port %u has unsupported type\n", i); - } - } - - lilv_node_free(lv2_connectionOptional); - lilv_node_free(lv2_ControlPort); - lilv_node_free(lv2_AudioPort); - lilv_node_free(lv2_OutputPort); - lilv_node_free(lv2_InputPort); - free(values); - - return 0; -} - -static void -print_version(void) -{ - printf("lv2apply (lilv) " LILV_VERSION "\n" - "Copyright 2007-2021 David Robillard \n" - "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 int -print_usage(int status) -{ - fprintf(status ? stderr : stdout, - "Usage: lv2apply [OPTION]... PLUGIN_URI\n" - "Apply an LV2 plugin to an audio file.\n\n" - " -i IN_FILE Input file\n" - " -o OUT_FILE Output file\n" - " -c SYM VAL Control value\n" - " --help Display this help and exit\n" - " --version Display version information and exit\n"); - return status; -} - -int -main(int argc, char** argv) -{ - LV2Apply self = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 0, NULL}; - - /* Parse command line arguments */ - const char* plugin_uri = NULL; - for (int i = 1; i < argc; ++i) { - if (!strcmp(argv[i], "--version")) { - free(self.params); - print_version(); - return 0; - } - - if (!strcmp(argv[i], "--help")) { - free(self.params); - return print_usage(0); - } - - if (!strcmp(argv[i], "-i")) { - self.in_path = argv[++i]; - } else if (!strcmp(argv[i], "-o")) { - self.out_path = argv[++i]; - } else if (!strcmp(argv[i], "-c")) { - if (argc < i + 3) { - return fatal(&self, 1, "Missing argument for -c\n"); - } - self.params = - (Param*)realloc(self.params, ++self.n_params * sizeof(Param)); - self.params[self.n_params - 1].sym = argv[++i]; - self.params[self.n_params - 1].value = atof(argv[++i]); - } else if (argv[i][0] == '-') { - free(self.params); - return print_usage(1); - } else if (i == argc - 1) { - plugin_uri = argv[i]; - } - } - - /* Check that required arguments are given */ - if (!self.in_path || !self.out_path || !plugin_uri) { - free(self.params); - return print_usage(1); - } - - /* Create world and plugin URI */ - self.world = lilv_world_new(); - LilvNode* uri = lilv_new_uri(self.world, plugin_uri); - if (!uri) { - return fatal(&self, 2, "Invalid plugin URI <%s>\n", plugin_uri); - } - - /* Discover world */ - lilv_world_load_all(self.world); - - /* Get plugin */ - const LilvPlugins* plugins = lilv_world_get_all_plugins(self.world); - const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, uri); - lilv_node_free(uri); - if (!(self.plugin = plugin)) { - return fatal(&self, 3, "Plugin <%s> not found\n", plugin_uri); - } - - /* Open input file */ - SF_INFO in_fmt = {0, 0, 0, 0, 0, 0}; - if (!(self.in_file = sopen(&self, self.in_path, SFM_READ, &in_fmt))) { - return 4; - } - - /* Create port structures */ - if (create_ports(&self)) { - return 5; - } - - if (self.n_audio_in == 0 || - (in_fmt.channels != (int)self.n_audio_in && in_fmt.channels != 1)) { - return fatal(&self, - 6, - "Unable to map %d inputs to %u ports\n", - in_fmt.channels, - self.n_audio_in); - } - - /* Set control values */ - for (unsigned i = 0; i < self.n_params; ++i) { - const Param* param = &self.params[i]; - LilvNode* sym = lilv_new_string(self.world, param->sym); - const LilvPort* port = lilv_plugin_get_port_by_symbol(plugin, sym); - lilv_node_free(sym); - if (!port) { - return fatal(&self, 7, "Unknown port `%s'\n", param->sym); - } - - self.ports[lilv_port_get_index(plugin, port)].value = param->value; - } - - /* Open output file */ - SF_INFO out_fmt = in_fmt; - out_fmt.channels = self.n_audio_out; - if (!(self.out_file = sopen(&self, self.out_path, SFM_WRITE, &out_fmt))) { - free(self.ports); - return 8; - } - - /* Instantiate plugin and connect ports */ - const uint32_t n_ports = lilv_plugin_get_num_ports(plugin); - float in_buf[self.n_audio_in > 0 ? self.n_audio_in : 1]; - float out_buf[self.n_audio_out > 0 ? self.n_audio_out : 1]; - self.instance = lilv_plugin_instantiate(self.plugin, in_fmt.samplerate, NULL); - for (uint32_t p = 0, i = 0, o = 0; p < n_ports; ++p) { - if (self.ports[p].type == TYPE_CONTROL) { - lilv_instance_connect_port(self.instance, p, &self.ports[p].value); - } else if (self.ports[p].type == TYPE_AUDIO) { - if (self.ports[p].is_input) { - lilv_instance_connect_port(self.instance, p, in_buf + i++); - } else { - lilv_instance_connect_port(self.instance, p, out_buf + o++); - } - } else { - lilv_instance_connect_port(self.instance, p, NULL); - } - } - - /* Ports are now connected to buffers in interleaved format, so we can run - a single frame at a time and avoid having to interleave buffers to - read/write from/to sndfile. */ - - lilv_instance_activate(self.instance); - while (sread(self.in_file, in_fmt.channels, in_buf, self.n_audio_in)) { - lilv_instance_run(self.instance, 1); - if (sf_writef_float(self.out_file, out_buf, 1) != 1) { - return fatal(&self, 9, "Failed to write to output file\n"); - } - } - lilv_instance_deactivate(self.instance); - - return cleanup(0, &self); -} diff --git a/utils/lv2bench.c b/utils/lv2bench.c deleted file mode 100644 index 26f52d1..0000000 --- a/utils/lv2bench.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - Copyright 2012-2019 David Robillard - - 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 200809L - -#include "lilv/lilv.h" -#include "lv2/atom/atom.h" -#include "lv2/core/lv2.h" -#include "lv2/urid/urid.h" - -#include "bench.h" -#include "lilv_config.h" -#include "uri_table.h" - -#include -#include -#include -#include -#include -#include -#include - -static LilvNode* atom_AtomPort = NULL; -static LilvNode* atom_Sequence = NULL; -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 bool full_output = false; - -static void -print_version(void) -{ - printf("lv2bench (lilv) " LILV_VERSION "\n" - "Copyright 2012-2021 David Robillard \n" - "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("lv2bench - Benchmark all installed and supported LV2 plugins.\n"); - printf("Usage: lv2bench [OPTIONS] [PLUGIN_URI]\n"); - printf("\n"); - printf(" -b BLOCK_SIZE Specify block size, in audio frames.\n"); - printf(" -f, --full Full plottable output.\n"); - printf(" -h, --help Display this help and exit.\n"); - printf(" -n FRAMES Total number of audio frames to process\n"); - printf(" --version Display version information and exit\n"); -} - -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 * 2ul, sizeof(float)); - float* const in = buf; - float* const out = buf + block_size; - if (!buf) { - fprintf(stderr, "Out of memory\n"); - return 0.0; - } - - const size_t atom_capacity = 1024; - - LV2_Atom_Sequence seq_in = {{sizeof(LV2_Atom_Sequence_Body), - uri_table_map(&uri_table, LV2_ATOM__Sequence)}, - {0, 0}}; - - LV2_Atom_Sequence* seq_out = - (LV2_Atom_Sequence*)malloc(sizeof(LV2_Atom_Sequence) + atom_capacity); - - 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(seq_out); - 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(seq_out); - free(buf); - uri_table_destroy(&uri_table); - return 0.0; - } - - const uint32_t n_ports = lilv_plugin_get_num_ports(p); - float* const mins = (float*)calloc(n_ports, sizeof(float)); - float* const maxes = (float*)calloc(n_ports, sizeof(float)); - float* const controls = (float*)calloc(n_ports, sizeof(float)); - lilv_plugin_get_port_ranges_float(p, mins, maxes, controls); - - 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)) { - if (isnan(controls[index])) { - if (!isnan(mins[index])) { - controls[index] = mins[index]; - } else if (!isnan(maxes[index])) { - controls[index] = maxes[index]; - } else { - controls[index] = 0.0; - } - } - 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 %u neither input nor output, skipping\n", - uri, - index); - lilv_instance_free(instance); - free(seq_out); - free(buf); - free(controls); - uri_table_destroy(&uri_table); - return 0.0; - } - } else if (lilv_port_is_a(p, port, atom_AtomPort)) { - if (lilv_port_is_a(p, port, lv2_InputPort)) { - lilv_instance_connect_port(instance, index, &seq_in); - } else { - lilv_instance_connect_port(instance, index, seq_out); - } - } else { - fprintf(stderr, "<%s> port %u has unknown type, skipping\n", uri, index); - lilv_instance_free(instance); - free(seq_out); - 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) { - seq_in.atom.size = sizeof(LV2_Atom_Sequence_Body); - seq_in.atom.type = uri_table_map(&uri_table, LV2_ATOM__Sequence); - seq_out->atom.size = atom_capacity; - seq_out->atom.type = uri_table_map(&uri_table, LV2_ATOM__Chunk); - - lilv_instance_run(instance, block_size); - } - const double elapsed = bench_end(&ts); - - lilv_instance_deactivate(instance); - lilv_instance_free(instance); - free(controls); - free(maxes); - free(mins); - free(seq_out); - - uri_table_destroy(&uri_table); - - if (full_output) { - printf("%u %u ", block_size, sample_count); - } - printf("%lf %s\n", elapsed, uri); - - free(buf); - return elapsed; -} - -int -main(int argc, char** argv) -{ - uint32_t block_size = 512; - uint32_t sample_count = (1 << 19); - - int a = 1; - for (; a < argc; ++a) { - if (!strcmp(argv[a], "--version")) { - print_version(); - return 0; - } - - if (!strcmp(argv[a], "--help")) { - print_usage(); - return 0; - } - - if (!strcmp(argv[a], "-f")) { - full_output = true; - } else if (!strcmp(argv[a], "-n") && (a + 1 < argc)) { - sample_count = atoi(argv[++a]); - } else if (!strcmp(argv[a], "-b") && (a + 1 < argc)) { - block_size = atoi(argv[++a]); - } else if (argv[a][0] != '-') { - break; - } else { - print_usage(); - return 1; - } - } - - const char* const plugin_uri_str = (a < argc ? argv[a++] : NULL); - - LilvWorld* world = lilv_world_new(); - lilv_world_load_all(world); - - atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort); - atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence); - 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); - - if (full_output) { - printf("# Block Samples Time Plugin\n"); - } - - const LilvPlugins* plugins = lilv_world_get_all_plugins(world); - if (plugin_uri_str) { - LilvNode* uri = lilv_new_uri(world, plugin_uri_str); - bench(lilv_plugins_get_by_uri(plugins, uri), sample_count, block_size); - lilv_node_free(uri); - } else { - LILV_FOREACH (plugins, i, plugins) { - bench(lilv_plugins_get(plugins, i), sample_count, block_size); - } - } - - 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_node_free(atom_Sequence); - lilv_node_free(atom_AtomPort); - - lilv_world_free(world); - - return 0; -} diff --git a/utils/lv2info.c b/utils/lv2info.c deleted file mode 100644 index 81a3ed4..0000000 --- a/utils/lv2info.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - Copyright 2007-2019 David Robillard - - 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. -*/ - -#include "lilv_config.h" - -#include "lilv/lilv.h" -#include "lv2/core/lv2.h" -#include "lv2/event/event.h" -#include "lv2/port-groups/port-groups.h" -#include "lv2/presets/presets.h" - -#include -#include -#include -#include -#include -#include - -static LilvNode* applies_to_pred = NULL; -static LilvNode* control_class = NULL; -static LilvNode* event_class = NULL; -static LilvNode* group_pred = NULL; -static LilvNode* label_pred = NULL; -static LilvNode* preset_class = NULL; -static LilvNode* designation_pred = NULL; -static LilvNode* supports_event_pred = NULL; - -static void -print_port(const LilvPlugin* p, - uint32_t index, - float* mins, - float* maxes, - float* defaults) -{ - const LilvPort* port = lilv_plugin_get_port_by_index(p, index); - - printf("\n\tPort %u:\n", index); - - if (!port) { - printf("\t\tERROR: Illegal/nonexistent port\n"); - return; - } - - bool first = true; - - const LilvNodes* classes = lilv_port_get_classes(p, port); - printf("\t\tType: "); - LILV_FOREACH (nodes, i, classes) { - const LilvNode* value = lilv_nodes_get(classes, i); - if (!first) { - printf("\n\t\t "); - } - printf("%s", lilv_node_as_uri(value)); - first = false; - } - - if (lilv_port_is_a(p, port, event_class)) { - LilvNodes* supported = lilv_port_get_value(p, port, supports_event_pred); - if (lilv_nodes_size(supported) > 0) { - printf("\n\t\tSupported events:\n"); - LILV_FOREACH (nodes, i, supported) { - const LilvNode* value = lilv_nodes_get(supported, i); - printf("\t\t\t%s\n", lilv_node_as_uri(value)); - } - } - lilv_nodes_free(supported); - } - - LilvScalePoints* points = lilv_port_get_scale_points(p, port); - if (points) { - printf("\n\t\tScale Points:\n"); - } - LILV_FOREACH (scale_points, i, points) { - const LilvScalePoint* point = lilv_scale_points_get(points, i); - printf("\t\t\t%s = \"%s\"\n", - lilv_node_as_string(lilv_scale_point_get_value(point)), - lilv_node_as_string(lilv_scale_point_get_label(point))); - } - lilv_scale_points_free(points); - - const LilvNode* sym = lilv_port_get_symbol(p, port); - printf("\n\t\tSymbol: %s\n", lilv_node_as_string(sym)); - - LilvNode* name = lilv_port_get_name(p, port); - printf("\t\tName: %s\n", lilv_node_as_string(name)); - lilv_node_free(name); - - LilvNodes* groups = lilv_port_get_value(p, port, group_pred); - if (lilv_nodes_size(groups) > 0) { - printf("\t\tGroup: %s\n", - lilv_node_as_string(lilv_nodes_get_first(groups))); - } - lilv_nodes_free(groups); - - LilvNodes* designations = lilv_port_get_value(p, port, designation_pred); - if (lilv_nodes_size(designations) > 0) { - printf("\t\tDesignation: %s\n", - lilv_node_as_string(lilv_nodes_get_first(designations))); - } - lilv_nodes_free(designations); - - if (lilv_port_is_a(p, port, control_class)) { - if (!isnan(mins[index])) { - printf("\t\tMinimum: %f\n", mins[index]); - } - if (!isnan(maxes[index])) { - printf("\t\tMaximum: %f\n", maxes[index]); - } - if (!isnan(defaults[index])) { - printf("\t\tDefault: %f\n", defaults[index]); - } - } - - LilvNodes* properties = lilv_port_get_properties(p, port); - if (lilv_nodes_size(properties) > 0) { - printf("\t\tProperties: "); - } - first = true; - LILV_FOREACH (nodes, i, properties) { - if (!first) { - printf("\t\t "); - } - printf("%s\n", lilv_node_as_uri(lilv_nodes_get(properties, i))); - first = false; - } - if (lilv_nodes_size(properties) > 0) { - printf("\n"); - } - lilv_nodes_free(properties); -} - -static void -print_plugin(LilvWorld* world, const LilvPlugin* p) -{ - LilvNode* val = NULL; - - printf("%s\n\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); - - val = lilv_plugin_get_name(p); - if (val) { - printf("\tName: %s\n", lilv_node_as_string(val)); - lilv_node_free(val); - } - - const LilvPluginClass* pclass = lilv_plugin_get_class(p); - const LilvNode* class_label = lilv_plugin_class_get_label(pclass); - if (class_label) { - printf("\tClass: %s\n", lilv_node_as_string(class_label)); - } - - val = lilv_plugin_get_author_name(p); - if (val) { - printf("\tAuthor: %s\n", lilv_node_as_string(val)); - lilv_node_free(val); - } - - val = lilv_plugin_get_author_email(p); - if (val) { - printf("\tAuthor Email: %s\n", lilv_node_as_uri(val)); - lilv_node_free(val); - } - - val = lilv_plugin_get_author_homepage(p); - if (val) { - printf("\tAuthor Homepage: %s\n", lilv_node_as_uri(val)); - lilv_node_free(val); - } - - if (lilv_plugin_has_latency(p)) { - uint32_t latency_port = lilv_plugin_get_latency_port_index(p); - printf("\tHas latency: yes, reported by port %u\n", latency_port); - } else { - printf("\tHas latency: no\n"); - } - - printf("\tBundle: %s\n", - lilv_node_as_uri(lilv_plugin_get_bundle_uri(p))); - - const LilvNode* binary_uri = lilv_plugin_get_library_uri(p); - if (binary_uri) { - printf("\tBinary: %s\n", - lilv_node_as_uri(lilv_plugin_get_library_uri(p))); - } - - LilvUIs* uis = lilv_plugin_get_uis(p); - if (lilv_nodes_size(uis) > 0) { - printf("\tUIs:\n"); - LILV_FOREACH (uis, i, uis) { - const LilvUI* ui = lilv_uis_get(uis, i); - printf("\t\t%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui))); - - const char* binary = lilv_node_as_uri(lilv_ui_get_binary_uri(ui)); - - const LilvNodes* types = lilv_ui_get_classes(ui); - LILV_FOREACH (nodes, t, types) { - printf("\t\t\tClass: %s\n", - lilv_node_as_uri(lilv_nodes_get(types, t))); - } - - if (binary) { - printf("\t\t\tBinary: %s\n", binary); - } - - printf("\t\t\tBundle: %s\n", - lilv_node_as_uri(lilv_ui_get_bundle_uri(ui))); - } - } - lilv_uis_free(uis); - - printf("\tData URIs: "); - const LilvNodes* data_uris = lilv_plugin_get_data_uris(p); - bool first = true; - LILV_FOREACH (nodes, i, data_uris) { - if (!first) { - printf("\n\t "); - } - printf("%s", lilv_node_as_uri(lilv_nodes_get(data_uris, i))); - first = false; - } - printf("\n"); - - /* Required Features */ - - LilvNodes* features = lilv_plugin_get_required_features(p); - if (features) { - printf("\tRequired Features: "); - } - first = true; - LILV_FOREACH (nodes, i, features) { - if (!first) { - printf("\n\t "); - } - printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); - first = false; - } - if (features) { - printf("\n"); - } - lilv_nodes_free(features); - - /* Optional Features */ - - features = lilv_plugin_get_optional_features(p); - if (features) { - printf("\tOptional Features: "); - } - first = true; - LILV_FOREACH (nodes, i, features) { - if (!first) { - printf("\n\t "); - } - printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); - first = false; - } - if (features) { - printf("\n"); - } - lilv_nodes_free(features); - - /* Extension Data */ - - LilvNodes* data = lilv_plugin_get_extension_data(p); - if (data) { - printf("\tExtension Data: "); - } - first = true; - LILV_FOREACH (nodes, i, data) { - if (!first) { - printf("\n\t "); - } - printf("%s", lilv_node_as_uri(lilv_nodes_get(data, i))); - first = false; - } - if (data) { - printf("\n"); - } - lilv_nodes_free(data); - - /* Presets */ - - LilvNodes* presets = lilv_plugin_get_related(p, preset_class); - if (presets) { - printf("\tPresets: \n"); - } - LILV_FOREACH (nodes, i, presets) { - const LilvNode* preset = lilv_nodes_get(presets, i); - lilv_world_load_resource(world, preset); - LilvNodes* titles = lilv_world_find_nodes(world, preset, label_pred, NULL); - if (titles) { - const LilvNode* title = lilv_nodes_get_first(titles); - printf("\t %s\n", lilv_node_as_string(title)); - lilv_nodes_free(titles); - } else { - fprintf(stderr, - "Preset <%s> has no rdfs:label\n", - lilv_node_as_string(lilv_nodes_get(presets, i))); - } - } - lilv_nodes_free(presets); - - /* Ports */ - - const uint32_t num_ports = lilv_plugin_get_num_ports(p); - float* mins = (float*)calloc(num_ports, sizeof(float)); - float* maxes = (float*)calloc(num_ports, sizeof(float)); - float* defaults = (float*)calloc(num_ports, sizeof(float)); - lilv_plugin_get_port_ranges_float(p, mins, maxes, defaults); - - for (uint32_t i = 0; i < num_ports; ++i) { - print_port(p, i, mins, maxes, defaults); - } - - free(mins); - free(maxes); - free(defaults); -} - -static void -print_version(void) -{ - printf("lv2info (lilv) " LILV_VERSION "\n" - "Copyright 2007-2021 David Robillard \n" - "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: lv2info [OPTION]... PLUGIN_URI\n" - "Print information about an installed LV2 plugin.\n\n" - " -p FILE Write Turtle description of plugin to FILE\n" - " -m FILE Add record of plugin to manifest FILE\n" - " --help Display this help and exit\n" - " --version Display version information and exit\n\n" - "For -p and -m, Turtle files are appended to (not overwritten),\n" - "and @prefix directives are only written if the file was empty.\n" - "This allows several plugins to be added to a single file.\n"); -} - -int -main(int argc, char** argv) -{ - if (argc == 1) { - print_usage(); - return 1; - } - - const char* plugin_file = NULL; - const char* manifest_file = NULL; - const char* plugin_uri = NULL; - for (int i = 1; i < argc; ++i) { - if (!strcmp(argv[i], "--version")) { - print_version(); - return 0; - } - - if (!strcmp(argv[i], "--help")) { - print_usage(); - return 0; - } - - if (!strcmp(argv[i], "-p")) { - plugin_file = argv[++i]; - } else if (!strcmp(argv[i], "-m")) { - manifest_file = argv[++i]; - } else if (argv[i][0] == '-') { - print_usage(); - return 1; - } else if (i == argc - 1) { - plugin_uri = argv[i]; - } - } - - int ret = 0; - - LilvWorld* world = lilv_world_new(); - lilv_world_load_all(world); - - LilvNode* uri = lilv_new_uri(world, plugin_uri); - if (!uri) { - fprintf(stderr, "Invalid plugin URI\n"); - lilv_world_free(world); - return 1; - } - - applies_to_pred = lilv_new_uri(world, LV2_CORE__appliesTo); - control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT); - event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT); - group_pred = lilv_new_uri(world, LV2_PORT_GROUPS__group); - label_pred = lilv_new_uri(world, LILV_NS_RDFS "label"); - preset_class = lilv_new_uri(world, LV2_PRESETS__Preset); - designation_pred = lilv_new_uri(world, LV2_CORE__designation); - supports_event_pred = lilv_new_uri(world, LV2_EVENT__supportsEvent); - - const LilvPlugins* plugins = lilv_world_get_all_plugins(world); - const LilvPlugin* p = lilv_plugins_get_by_uri(plugins, uri); - - if (p && plugin_file) { - LilvNode* base = lilv_new_file_uri(world, NULL, plugin_file); - - FILE* plugin_fd = fopen(plugin_file, "a"); - if (plugin_fd) { - lilv_plugin_write_description(world, p, base, plugin_fd); - fclose(plugin_fd); - } else { - fprintf(stderr, "error: Failed to open %s\n", plugin_file); - } - - if (manifest_file) { - FILE* manifest_fd = fopen(manifest_file, "a"); - if (manifest_fd) { - lilv_plugin_write_manifest_entry( - world, p, base, manifest_fd, plugin_file); - fclose(manifest_fd); - } else { - fprintf(stderr, "error: Failed to open %s\n", manifest_file); - } - } - lilv_node_free(base); - } else if (p) { - print_plugin(world, p); - } else { - fprintf(stderr, "Plugin not found.\n"); - } - - ret = (p != NULL ? 0 : -1); - - lilv_node_free(uri); - - lilv_node_free(supports_event_pred); - lilv_node_free(designation_pred); - lilv_node_free(preset_class); - lilv_node_free(label_pred); - lilv_node_free(group_pred); - lilv_node_free(event_class); - lilv_node_free(control_class); - lilv_node_free(applies_to_pred); - - lilv_world_free(world); - return ret; -} diff --git a/utils/lv2ls.c b/utils/lv2ls.c deleted file mode 100644 index 9226b89..0000000 --- a/utils/lv2ls.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - Copyright 2007-2019 David Robillard - - 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. -*/ - -#include "lilv_config.h" - -#include "lilv/lilv.h" - -#include -#include -#include - -static void -list_plugins(const LilvPlugins* list, bool show_names) -{ - LILV_FOREACH (plugins, i, list) { - const LilvPlugin* p = lilv_plugins_get(list, i); - if (show_names) { - LilvNode* n = lilv_plugin_get_name(p); - printf("%s\n", lilv_node_as_string(n)); - lilv_node_free(n); - } else { - printf("%s\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); - } - } -} - -static void -print_version(void) -{ - printf("lv2ls (lilv) " LILV_VERSION "\n" - "Copyright 2007-2021 David Robillard \n" - "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: lv2ls [OPTION]...\n"); - printf("List all installed LV2 plugins.\n"); - printf("\n"); - printf(" -n, --names Show names instead of URIs\n"); - printf(" --help Display this help and exit\n"); - printf(" --version Display version information and exit\n"); - printf("\n"); - printf("The environment variable LV2_PATH can be used to control where\n"); - printf( - "this (and all other lilv based LV2 hosts) will search for plugins.\n"); -} - -int -main(int argc, char** argv) -{ - bool show_names = false; - for (int i = 1; i < argc; ++i) { - if (!strcmp(argv[i], "--names") || !strcmp(argv[i], "-n")) { - show_names = true; - } else 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); - - const LilvPlugins* plugins = lilv_world_get_all_plugins(world); - - list_plugins(plugins, show_names); - - lilv_world_free(world); - - return 0; -} diff --git a/utils/uri_table.h b/utils/uri_table.h deleted file mode 100644 index 5a337a3..0000000 --- a/utils/uri_table.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - Copyright 2011-2019 David Robillard - - 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 - -#include "lv2/urid/urid.h" - -#include -#include - -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) -{ - for (size_t i = 0; i < table->n_uris; ++i) { - free(table->uris[i]); - } - - 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] = (char*)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/waf b/waf deleted file mode 100755 index 887215c..0000000 --- a/waf +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -# Minimal waf script for projects that include waflib directly - -import sys -import inspect -import os - -try: - from waflib import Context, Scripting -except Exception as e: - sys.stderr.write('error: Failed to import waf (%s)\n' % e) - if os.path.exists('.git'): - sys.stderr.write("Are submodules up to date? " - "Try 'git submodule update --init --recursive'\n") - - sys.exit(1) - - -def main(): - script_path = os.path.abspath(inspect.getfile(inspect.getmodule(main))) - project_path = os.path.dirname(script_path) - Scripting.waf_entry_point(os.getcwd(), Context.WAFVERSION, project_path) - - -if __name__ == '__main__': - main() diff --git a/waflib b/waflib deleted file mode 160000 index b600c92..0000000 --- a/waflib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b600c928b221a001faeab7bd92786d0b25714bc8 diff --git a/wscript b/wscript deleted file mode 100644 index 13a0fbb..0000000 --- a/wscript +++ /dev/null @@ -1,643 +0,0 @@ -#!/usr/bin/env python - -import glob -import os -import shutil -import sys - -from waflib import Build, Options, Logs -from waflib.extras import autowaf - -# Library and package version (UNIX style major, minor, micro) -# major increment <=> incompatible changes -# minor increment <=> compatible changes (additions) -# micro increment <=> no interface changes -LILV_VERSION = '0.24.15' -LILV_MAJOR_VERSION = '0' - -# Mandatory waf variables -APPNAME = 'lilv' # Package name for waf dist -VERSION = LILV_VERSION # Package version for waf dist -top = '.' # Source directory -out = 'build' # Build directory - -# Release variables -uri = 'http://drobilla.net/sw/lilv' -dist_pattern = 'http://download.drobilla.net/lilv-%d.%d.%d.tar.bz2' -post_tags = ['Hacking', 'LAD', 'LV2', 'Lilv'] - -tests = [ - 'test_bad_port_index', - 'test_bad_port_symbol', - 'test_classes', - 'test_discovery', - 'test_filesystem', - 'test_get_symbol', - 'test_no_author', - 'test_no_verify', - 'test_plugin', - 'test_port', - 'test_preset', - 'test_project', - 'test_project_no_author', - 'test_prototype', - 'test_reload_bundle', - 'test_replace_version', - 'test_state', - 'test_string', - 'test_ui', - 'test_util', - 'test_value', - 'test_verify', - 'test_world', -] - -test_plugins = [ - 'bad_syntax', - 'failed_instantiation', - 'failed_lib_descriptor', - 'lib_descriptor', - 'missing_descriptor', - 'missing_name', - 'missing_plugin', - 'missing_port', - 'missing_port_name', - 'new_version', - 'old_version' -] - - -def options(ctx): - ctx.load('compiler_c') - ctx.load('compiler_cxx') - ctx.load('python') - opt = ctx.configuration_options() - ctx.add_flags( - opt, - {'no-utils': 'do not build command line utilities', - 'no-bindings': 'do not build python bindings', - 'dyn-manifest': 'build support for dynamic manifests', - 'no-bash-completion': 'do not install bash completion script', - 'static': 'build static library', - 'no-shared': 'do not build shared library', - 'static-progs': 'build programs as static binaries'}) - - opt.add_option('--default-lv2-path', type='string', default='', - dest='default_lv2_path', - help='default LV2 path to use if LV2_PATH is unset') - - -def configure(conf): - conf.load('compiler_c', cache=True) - try: - conf.load('compiler_cxx', cache=True) - conf.define('LILV_CXX', True) - except Exception: - pass - - if not Options.options.no_bindings: - try: - conf.load('python', cache=True) - conf.check_python_version((2, 6, 0)) - conf.env.LILV_PYTHON = 1 - except Exception as e: - Logs.warn('Failed to configure Python (%s)\n' % e) - - conf.load('autowaf', cache=True) - autowaf.set_c_lang(conf, 'c99') - - if conf.env.DOCS: - conf.load('sphinx') - - conf.env.LILV_MAJOR_VERSION = LILV_MAJOR_VERSION - - conf.env.BASH_COMPLETION = not Options.options.no_bash_completion - conf.env.BUILD_UTILS = not Options.options.no_utils - conf.env.BUILD_SHARED = not Options.options.no_shared - conf.env.STATIC_PROGS = Options.options.static_progs - conf.env.BUILD_STATIC = (Options.options.static or - Options.options.static_progs) - - if not conf.env.BUILD_SHARED and not conf.env.BUILD_STATIC: - conf.fatal('Neither a shared nor a static build requested') - - if Options.options.strict: - # Check for programs used by lint target - conf.find_program("flake8", var="FLAKE8", mandatory=False) - conf.find_program("clang-tidy", var="CLANG_TIDY", mandatory=False) - conf.find_program("iwyu_tool", var="IWYU_TOOL", mandatory=False) - - if Options.options.ultra_strict: - autowaf.add_compiler_flags(conf.env, '*', { - 'clang': [ - '-Wno-documentation-unknown-command', - '-Wno-nullability-extension', - ] - }) - - autowaf.add_compiler_flags(conf.env, 'c', { - 'clang': [ - '-Wno-cast-align', - '-Wno-cast-qual', - '-Wno-double-promotion', - '-Wno-float-equal', - '-Wno-format-nonliteral', - '-Wno-implicit-float-conversion', - '-Wno-implicit-int-conversion', - '-Wno-padded', - '-Wno-reserved-id-macro', - '-Wno-shorten-64-to-32', - '-Wno-sign-conversion', - '-Wno-switch-enum', - '-Wno-vla', - ], - 'gcc': [ - '-Wno-cast-align', - '-Wno-cast-qual', - '-Wno-conversion', - '-Wno-double-promotion', - '-Wno-float-equal', - '-Wno-padded', - '-Wno-stack-protector', - '-Wno-suggest-attribute=const', - '-Wno-suggest-attribute=pure', - '-Wno-switch-enum', - '-Wno-vla', - ], - 'msvc': [ - '/wd4061', # enumerator in switch is not explicitly handled - '/wd4365', # signed/unsigned mismatch - '/wd4514', # unreferenced inline function has been removed - '/wd4774', # format string is not a string literal - '/wd4820', # padding added after construct - '/wd4996', # POSIX name for this item is deprecated - ], - }) - - autowaf.add_compiler_flags(conf.env, 'cxx', { - 'clang': [ - '-Wno-extra-semi', - ], - 'gcc': [ - '-Wno-effc++', - '-Wno-extra-semi', - '-Wno-pedantic', - ], - }) - - conf.check_pkg('lv2 >= 1.18.0', uselib_store='LV2') - conf.check_pkg('serd-0 >= 0.30.0', uselib_store='SERD') - conf.check_pkg('sord-0 >= 0.14.0', uselib_store='SORD') - conf.check_pkg('sratom-0 >= 0.4.0', uselib_store='SRATOM') - conf.check_pkg('sndfile >= 1.0.0', uselib_store='SNDFILE', mandatory=False) - - defines = ['_POSIX_C_SOURCE=200809L', '_BSD_SOURCE', '_DEFAULT_SOURCE'] - if conf.env.DEST_OS == 'darwin': - defines += ['_DARWIN_C_SOURCE'] - - rt_lib = ['rt'] - if conf.env.DEST_OS == 'darwin' or conf.env.DEST_OS == 'win32': - rt_lib = [] - - conf.check_function('c', 'lstat', - header_name = ['sys/stat.h'], - defines = defines, - define_name = 'HAVE_LSTAT', - return_type = 'int', - arg_types = 'const char*, struct stat*', - mandatory = False) - - conf.check_function('c', 'flock', - header_name = 'sys/file.h', - defines = defines, - define_name = 'HAVE_FLOCK', - return_type = 'int', - arg_types = 'int, int', - mandatory = False) - - conf.check_function('c', 'fileno', - header_name = 'stdio.h', - defines = defines, - define_name = 'HAVE_FILENO', - return_type = 'int', - arg_types = 'FILE*', - mandatory = False) - - conf.check_function('c', 'clock_gettime', - header_name = ['sys/time.h', 'time.h'], - defines = ['_POSIX_C_SOURCE=200809L'], - define_name = 'HAVE_CLOCK_GETTIME', - uselib_store = 'CLOCK_GETTIME', - lib = rt_lib, - return_type = 'int', - arg_types = 'clockid_t, struct timespec*', - mandatory = False) - - conf.check_cc(define_name = 'HAVE_LIBDL', - lib = 'dl', - mandatory = False) - - if Options.options.dyn_manifest: - conf.define('LILV_DYN_MANIFEST', 1) - - lilv_path_sep = ':' - lilv_dir_sep = '/' - if conf.env.DEST_OS == 'win32': - lilv_path_sep = ';' - lilv_dir_sep = '\\\\' - - conf.define('LILV_PATH_SEP', lilv_path_sep) - conf.define('LILV_DIR_SEP', lilv_dir_sep) - - # Set default LV2 path - lv2_path = Options.options.default_lv2_path - if lv2_path == '': - if conf.env.DEST_OS == 'darwin': - lv2_path = lilv_path_sep.join(['~/Library/Audio/Plug-Ins/LV2', - '~/.lv2', - '/usr/local/lib/lv2', - '/usr/lib/lv2', - '/Library/Audio/Plug-Ins/LV2']) - elif conf.env.DEST_OS == 'haiku': - lv2_path = lilv_path_sep.join(['~/.lv2', - '/boot/common/add-ons/lv2']) - elif conf.env.DEST_OS == 'win32': - lv2_path = lilv_path_sep.join(['%APPDATA%\\\\LV2', - '%COMMONPROGRAMFILES%\\\\LV2']) - else: - libdirname = os.path.basename(conf.env.LIBDIR) - lv2_path = lilv_path_sep.join(['~/.lv2', - '/usr/%s/lv2' % libdirname, - '/usr/local/%s/lv2' % libdirname]) - - if sys.platform == 'win32': - lv2_path = lv2_path.replace('%', '%%') - - conf.define('LILV_DEFAULT_LV2_PATH', lv2_path) - - # Set up environment for building/using as a subproject - autowaf.set_lib_env(conf, 'lilv', LILV_VERSION, - include_path=str(conf.path.find_node('include'))) - - conf.define('LILV_NO_DEFAULT_CONFIG', 1) - - autowaf.display_summary( - conf, - {'Default LV2_PATH': lv2_path, - 'Utilities': bool(conf.env.BUILD_UTILS), - 'Unit tests': bool(conf.env.BUILD_TESTS), - 'Dynamic manifest support': conf.is_defined('LILV_DYN_MANIFEST'), - 'Python bindings': bool(conf.env.LILV_PYTHON)}) - - -def build_util(bld, name, defines, libs=''): - obj = bld(features = 'c cprogram', - source = name + '.c', - includes = ['.', 'include', './src', './utils'], - use = 'liblilv', - uselib = 'SERD SORD SRATOM LV2 ' + libs, - 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 - bld.install_files(includedir, bld.path.ant_glob('include/lilv/*.h')) - bld.install_files(includedir, bld.path.ant_glob('include/lilv/*.hpp')) - - lib_source = ''' - src/collections.c - src/filesystem.c - src/instance.c - src/lib.c - src/node.c - src/plugin.c - src/pluginclass.c - src/port.c - src/query.c - src/scalepoint.c - src/state.c - src/ui.c - src/util.c - src/world.c - src/zix/tree.c - '''.split() - - lib = [] - libflags = ['-fvisibility=hidden'] - defines = [] - if bld.is_defined('HAVE_LIBDL'): - lib += ['dl'] - if bld.env.DEST_OS == 'win32': - lib = [] - if bld.env.MSVC_COMPILER: - libflags = [] - - # Pkgconfig file - autowaf.build_pc(bld, 'LILV', LILV_VERSION, LILV_MAJOR_VERSION, [], - {'LILV_MAJOR_VERSION': LILV_MAJOR_VERSION, - 'LILV_PKG_DEPS': 'lv2 serd-0 sord-0 sratom-0', - 'LILV_PKG_LIBS': ' -l'.join([''] + lib)}) - - # Shared Library - if bld.env.BUILD_SHARED: - obj = bld(features = 'c cshlib', - export_includes = ['.', 'include'], - source = lib_source, - includes = ['.', 'include', './src'], - name = 'liblilv', - target = 'lilv-%s' % LILV_MAJOR_VERSION, - vnum = LILV_VERSION, - install_path = '${LIBDIR}', - defines = ['LILV_INTERNAL', 'ZIX_STATIC'], - cflags = libflags, - lib = lib, - uselib = 'SERD SORD SRATOM LV2') - - # Static library - if bld.env.BUILD_STATIC: - obj = bld(features = 'c cstlib', - export_includes = ['.', 'include'], - source = lib_source, - includes = ['.', 'include', './src'], - name = 'liblilv_static', - target = 'lilv-%s' % LILV_MAJOR_VERSION, - vnum = LILV_VERSION, - install_path = '${LIBDIR}', - defines = defines + ['LILV_STATIC', - 'LILV_INTERNAL', - 'ZIX_STATIC', - 'ZIX_INTERNAL'], - uselib = 'SERD SORD SRATOM LV2') - - # Python bindings - if bld.env.LILV_PYTHON: - bld(features = 'subst', - is_copy = True, - source = 'bindings/python/lilv.py', - target = 'lilv.py', - install_path = '${PYTHONDIR}') - - if bld.env.BUILD_TESTS: - import re - - test_libs = lib - test_cflags = [''] - test_linkflags = [''] - if not bld.env.NO_COVERAGE: - test_cflags += ['--coverage'] - test_linkflags += ['--coverage'] - - # Copy skeleton LV2 bundle for tests - for name in ('manifest.ttl', 'lv2core.ttl'): - bld(features = 'subst', - is_copy = True, - source = 'test/core.lv2/' + name, - target = 'test/test_lv2_path/core.lv2/' + name, - install_path = None) - - # Make a pattern for shared objects without the 'lib' prefix - module_pattern = re.sub('^lib', '', bld.env.cshlib_PATTERN) - shlib_ext = module_pattern[module_pattern.rfind('.'):] - - for p in ['test'] + test_plugins: - obj = bld(features = 'c cshlib', - source = 'test/%s.lv2/%s.c' % (p, p), - name = p, - target = 'test/%s.lv2/%s' % (p, p), - install_path = None, - defines = defines + ['LILV_STATIC', 'ZIX_STATIC'], - cflags = test_cflags, - linkflags = test_linkflags, - lib = test_libs, - uselib = 'LV2') - obj.env.cshlib_PATTERN = module_pattern - - for p in test_plugins: - if not bld.path.find_node('test/%s.lv2/test_%s.c' % (p, p)): - continue - - obj = bld(features = 'c cprogram', - source = 'test/%s.lv2/test_%s.c' % (p, p), - target = 'test/test_%s' % p, - includes = ['.', 'include', './src'], - use = 'liblilv_profiled', - install_path = None, - defines = defines + ['LILV_STATIC', 'ZIX_STATIC'], - cflags = test_cflags, - linkflags = test_linkflags, - lib = test_libs, - uselib = 'SERD SORD SRATOM LV2') - - # Test plugin data files - for p in ['test'] + test_plugins: - for i in ['manifest.ttl.in', p + '.ttl.in']: - bundle = 'test/%s.lv2/' % p - bld(features = 'subst', - source = bundle + i, - target = bundle + i.replace('.in', ''), - install_path = None, - SHLIB_EXT = shlib_ext) - - # Static profiled library (for unit test code coverage) - obj = bld(features = 'c cstlib', - source = lib_source, - includes = ['.', 'include', './src'], - name = 'liblilv_profiled', - target = 'lilv_profiled', - install_path = None, - defines = defines + ['LILV_STATIC', - 'LILV_INTERNAL', - 'ZIX_STATIC', - 'ZIX_INTERNAL', - ], - cflags = test_cflags, - linkflags = test_linkflags, - lib = test_libs, - uselib = 'SERD SORD SRATOM LV2') - - # Unit test program - testdir = bld.path.get_bld().make_node('test').abspath() - bpath = os.path.join(testdir, 'test.lv2') - bpath = bpath.replace('\\', '/') - testdir = testdir.replace('\\', '/') - for test in tests: - obj = bld(features = 'c cprogram', - source = ['test/%s.c' % test, - 'test/lilv_test_utils.c'], - includes = ['.', 'include', './src'], - use = 'liblilv_profiled', - lib = test_libs, - uselib = 'SERD SORD SRATOM LV2', - target = 'test/' + test, - install_path = None, - defines = (defines + - ['LILV_STATIC'] + - ['LILV_TEST_BUNDLE=\"%s/\"' % bpath] + - ['LILV_TEST_DIR=\"%s/\"' % testdir] + - ['ZIX_STATIC']), - cflags = test_cflags, - linkflags = test_linkflags) - - # C++ API test - if 'COMPILER_CXX' in bld.env: - obj = bld(features = 'cxx cxxprogram', - source = 'test/lilv_cxx_test.cpp', - includes = ['.', 'include', './src'], - use = 'liblilv_profiled', - lib = test_libs, - uselib = 'SERD SORD SRATOM LV2', - target = 'test/lilv_cxx_test', - install_path = None, - defines = ['LILV_STATIC', 'ZIX_STATIC'], - cxxflags = test_cflags, - linkflags = test_linkflags) - - if bld.env.LILV_PYTHON: - test_bundle = 'bindings/bindings_test_plugin.lv2/' - - # Copy Python unittest files - for i in ['test_api.py']: - bld(features = 'subst', - is_copy = True, - source = 'bindings/test/python/' + i, - target = 'bindings/' + i, - install_path = None) - - # Build bindings test plugin - obj = bld(features = 'c cshlib', - source = 'bindings/test/bindings_test_plugin.c', - name = 'bindings_test_plugin', - target = test_bundle + '/bindings_test_plugin', - install_path = None, - defines = defines, - cflags = test_cflags, - linkflags = test_linkflags, - lib = test_libs, - uselib = 'LV2') - obj.env.cshlib_PATTERN = module_pattern - - # Bindings test plugin data files - for i in ['manifest.ttl.in', 'bindings_test_plugin.ttl.in']: - bld(features = 'subst', - source = 'bindings/test/' + i, - target = test_bundle + i.replace('.in', ''), - install_path = None, - SHLIB_EXT = shlib_ext) - - # Utilities - if bld.env.BUILD_UTILS: - utils = ''' - utils/lilv-bench - utils/lv2info - utils/lv2ls - ''' - for i in utils.split(): - build_util(bld, i, defines) - - if bld.env.HAVE_SNDFILE: - obj = build_util(bld, 'utils/lv2apply', defines, 'SNDFILE') - - # lv2bench (less portable than other utilities) - if (bld.env.DEST_OS != 'win32' and - bld.is_defined('HAVE_CLOCK_GETTIME') and - not bld.env.STATIC_PROGS): - obj = build_util(bld, 'utils/lv2bench', defines) - if bld.env.DEST_OS != 'darwin': - obj.lib = ['rt'] - - # Documentation - if bld.env.DOCS: - bld.recurse('doc/c') - - # Man pages - bld.install_files('${MANDIR}/man1', bld.path.ant_glob('doc/*.1')) - - # Bash completion - if bld.env.BASH_COMPLETION: - bld.install_as('${SYSCONFDIR}/bash_completion.d/lilv', - 'utils/lilv.bash_completion') - - bld.add_post_fun(autowaf.run_ldconfig) - - -def test(tst): - with tst.group('unit') as check: - for test in tests: - check(['./test/' + test]) - - if tst.is_defined('LILV_CXX'): - check(['./test/lilv_cxx_test']) - - if tst.env.LILV_PYTHON: - with tst.group('python') as check: - check([tst.env.PYTHON[0], '-m', 'unittest', 'discover', 'bindings/']) - - with tst.group('plugin') as check: - for p in test_plugins: - prog_name = tst.env.cprogram_PATTERN % ('test_' + p) - if os.path.exists(os.path.join('test', prog_name)): - check(['./test/test_' + p, 'test/%s.lv2/' % p]) - - try: - shutil.rmtree('state') - except Exception: - pass - - -class LintContext(Build.BuildContext): - fun = cmd = 'lint' - - -def lint(ctx): - "checks code for style issues" - import subprocess - - st = 0 - - if "FLAKE8" in ctx.env: - Logs.info("Running flake8") - st = subprocess.call([ctx.env.FLAKE8[0], - "wscript", - "--ignore", - "E101,E129,W191,E221,W504,E251,E241,E741"]) - else: - Logs.warn("Not running flake8") - - if "IWYU_TOOL" in ctx.env: - Logs.info("Running include-what-you-use") - cmd = [ctx.env.IWYU_TOOL[0], "-o", "clang", "-p", "build"] - output = subprocess.check_output(cmd).decode('utf-8') - if 'error: ' in output: - sys.stdout.write(output) - st += 1 - else: - Logs.warn("Not running include-what-you-use") - - if "CLANG_TIDY" in ctx.env and "clang" in ctx.env.CC[0]: - Logs.info("Running clang-tidy") - sources = glob.glob('src/*.c') + glob.glob('tests/*.c') - sources = list(map(os.path.abspath, sources)) - procs = [] - for source in sources: - cmd = [ctx.env.CLANG_TIDY[0], "--quiet", "-p=.", source] - procs += [subprocess.Popen(cmd, cwd="build")] - - for proc in procs: - stdout, stderr = proc.communicate() - st += proc.returncode - else: - Logs.warn("Not running clang-tidy") - - if st != 0: - sys.exit(st) -- cgit v1.2.1