summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--.gitignore5
-rw-r--r--AUTHORS11
-rw-r--r--COPYING38
-rw-r--r--INSTALL66
-rw-r--r--NEWS231
-rw-r--r--README8
-rw-r--r--bindings/lilv.i66
-rw-r--r--bindings/numpy.i1746
-rw-r--r--bindings/python/Makefile186
-rw-r--r--bindings/python/conf.py263
-rw-r--r--bindings/python/index.rst9
-rw-r--r--bindings/python/lilv.py1775
-rwxr-xr-xbindings/python/lv2_apply.py159
-rwxr-xr-xbindings/python/lv2_list.py9
-rw-r--r--bindings/test/bindings_test_plugin.c196
-rw-r--r--bindings/test/bindings_test_plugin.ttl.in62
-rw-r--r--bindings/test/manifest.ttl.in7
-rw-r--r--bindings/test/python/test_api.py290
-rw-r--r--doc/layout.xml187
-rw-r--r--doc/lv2apply.134
-rw-r--r--doc/lv2info.133
-rw-r--r--doc/lv2ls.130
-rw-r--r--doc/reference.doxygen.in2415
-rw-r--r--doc/style.css691
-rw-r--r--lilv.pc.in11
-rw-r--r--lilv.ttl29
-rw-r--r--lilv/lilv.h1844
-rw-r--r--lilv/lilvmm.hpp345
-rw-r--r--src/collections.c224
-rw-r--r--src/instance.c110
-rw-r--r--src/lib.c112
-rw-r--r--src/lilv_internal.h445
-rw-r--r--src/node.c397
-rw-r--r--src/plugin.c1138
-rw-r--r--src/pluginclass.c88
-rw-r--r--src/port.c268
-rw-r--r--src/query.c139
-rw-r--r--src/scalepoint.c49
-rw-r--r--src/state.c1352
-rw-r--r--src/ui.c111
-rw-r--r--src/util.c659
-rw-r--r--src/world.c1212
-rw-r--r--src/zix/common.h88
-rw-r--r--src/zix/tree.c716
-rw-r--r--src/zix/tree.h148
-rw-r--r--test/bad_syntax.lv2/bad_syntax.c93
-rw-r--r--test/bad_syntax.lv2/bad_syntax.ttl.in22
-rw-r--r--test/bad_syntax.lv2/manifest.ttl.in7
-rw-r--r--test/bad_syntax.lv2/test_bad_syntax.c45
-rw-r--r--test/failed_instantiation.lv2/failed_instantiation.c70
-rw-r--r--test/failed_instantiation.lv2/failed_instantiation.ttl.in40
-rw-r--r--test/failed_instantiation.lv2/manifest.ttl.in7
-rw-r--r--test/failed_instantiation.lv2/test_failed_instantiation.c45
-rw-r--r--test/failed_lib_descriptor.lv2/failed_lib_descriptor.c30
-rw-r--r--test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in38
-rw-r--r--test/failed_lib_descriptor.lv2/manifest.ttl.in7
-rw-r--r--test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c46
-rw-r--r--test/lib_descriptor.lv2/lib_descriptor.c112
-rw-r--r--test/lib_descriptor.lv2/lib_descriptor.ttl.in41
-rw-r--r--test/lib_descriptor.lv2/manifest.ttl.in7
-rw-r--r--test/lib_descriptor.lv2/test_lib_descriptor.c59
-rw-r--r--test/lilv_cxx_test.cpp23
-rw-r--r--test/lilv_test.c2299
-rw-r--r--test/missing_descriptor.lv2/manifest.ttl.in7
-rw-r--r--test/missing_descriptor.lv2/missing_descriptor.c21
-rw-r--r--test/missing_descriptor.lv2/missing_descriptor.ttl.in38
-rw-r--r--test/missing_descriptor.lv2/test_missing_descriptor.c46
-rw-r--r--test/missing_name.lv2/manifest.ttl.in7
-rw-r--r--test/missing_name.lv2/missing_name.c93
-rw-r--r--test/missing_name.lv2/missing_name.ttl.in37
-rw-r--r--test/missing_name.lv2/test_missing_name.c47
-rw-r--r--test/missing_plugin.lv2/manifest.ttl.in7
-rw-r--r--test/missing_plugin.lv2/missing_plugin.c43
-rw-r--r--test/missing_plugin.lv2/missing_plugin.ttl.in38
-rw-r--r--test/missing_plugin.lv2/test_missing_plugin.c46
-rw-r--r--test/missing_port.lv2/manifest.ttl.in7
-rw-r--r--test/missing_port.lv2/missing_port.c93
-rw-r--r--test/missing_port.lv2/missing_port.ttl.in31
-rw-r--r--test/missing_port.lv2/test_missing_port.c45
-rw-r--r--test/missing_port_name.lv2/manifest.ttl.in7
-rw-r--r--test/missing_port_name.lv2/missing_port_name.c93
-rw-r--r--test/missing_port_name.lv2/missing_port_name.ttl.in30
-rw-r--r--test/missing_port_name.lv2/test_missing_port_name.c49
-rw-r--r--test/new_version.lv2/manifest.ttl.in7
-rw-r--r--test/new_version.lv2/new_version.c93
-rw-r--r--test/new_version.lv2/new_version.ttl.in40
-rw-r--r--test/old_version.lv2/manifest.ttl.in7
-rw-r--r--test/old_version.lv2/old_version.c93
-rw-r--r--test/old_version.lv2/old_version.ttl.in40
-rw-r--r--test/test.lv2/manifest.ttl.in7
-rw-r--r--test/test.lv2/test.c394
-rw-r--r--test/test.lv2/test.ttl.in46
-rw-r--r--utils/bench.h52
-rw-r--r--utils/lilv-bench.c38
-rw-r--r--utils/lilv.bash_completion59
-rw-r--r--utils/lv2apply.c354
-rw-r--r--utils/lv2bench.c268
-rw-r--r--utils/lv2info.c460
-rw-r--r--utils/lv2ls.c93
-rw-r--r--utils/uri_table.h73
-rw-r--r--waflib/.gitignore2
-rw-r--r--waflib/Build.py (renamed from Build.py)0
-rw-r--r--waflib/COPYING25
-rw-r--r--waflib/ConfigSet.py (renamed from ConfigSet.py)0
-rw-r--r--waflib/Configure.py (renamed from Configure.py)0
-rw-r--r--waflib/Context.py (renamed from Context.py)0
-rw-r--r--waflib/Errors.py (renamed from Errors.py)0
-rw-r--r--waflib/Logs.py (renamed from Logs.py)0
-rw-r--r--waflib/Node.py (renamed from Node.py)0
-rw-r--r--waflib/Options.py (renamed from Options.py)0
-rw-r--r--waflib/README.md (renamed from README.md)0
-rw-r--r--waflib/Runner.py (renamed from Runner.py)0
-rw-r--r--waflib/Scripting.py (renamed from Scripting.py)0
-rw-r--r--waflib/Task.py (renamed from Task.py)0
-rw-r--r--waflib/TaskGen.py (renamed from TaskGen.py)0
-rw-r--r--waflib/Tools/__init__.py (renamed from Tools/__init__.py)0
-rw-r--r--waflib/Tools/ar.py (renamed from Tools/ar.py)0
-rw-r--r--waflib/Tools/asm.py (renamed from Tools/asm.py)0
-rw-r--r--waflib/Tools/bison.py (renamed from Tools/bison.py)0
-rw-r--r--waflib/Tools/c.py (renamed from Tools/c.py)0
-rw-r--r--waflib/Tools/c_aliases.py (renamed from Tools/c_aliases.py)0
-rw-r--r--waflib/Tools/c_config.py (renamed from Tools/c_config.py)0
-rw-r--r--waflib/Tools/c_osx.py (renamed from Tools/c_osx.py)0
-rw-r--r--waflib/Tools/c_preproc.py (renamed from Tools/c_preproc.py)0
-rw-r--r--waflib/Tools/c_tests.py (renamed from Tools/c_tests.py)0
-rw-r--r--waflib/Tools/ccroot.py (renamed from Tools/ccroot.py)0
-rw-r--r--waflib/Tools/clang.py (renamed from Tools/clang.py)0
-rw-r--r--waflib/Tools/clangxx.py (renamed from Tools/clangxx.py)0
-rw-r--r--waflib/Tools/compiler_c.py (renamed from Tools/compiler_c.py)0
-rw-r--r--waflib/Tools/compiler_cxx.py (renamed from Tools/compiler_cxx.py)0
-rw-r--r--waflib/Tools/compiler_d.py (renamed from Tools/compiler_d.py)0
-rw-r--r--waflib/Tools/compiler_fc.py (renamed from Tools/compiler_fc.py)0
-rw-r--r--waflib/Tools/cs.py (renamed from Tools/cs.py)0
-rw-r--r--waflib/Tools/cxx.py (renamed from Tools/cxx.py)0
-rw-r--r--waflib/Tools/d.py (renamed from Tools/d.py)0
-rw-r--r--waflib/Tools/d_config.py (renamed from Tools/d_config.py)0
-rw-r--r--waflib/Tools/d_scan.py (renamed from Tools/d_scan.py)0
-rw-r--r--waflib/Tools/dbus.py (renamed from Tools/dbus.py)0
-rw-r--r--waflib/Tools/dmd.py (renamed from Tools/dmd.py)0
-rw-r--r--waflib/Tools/errcheck.py (renamed from Tools/errcheck.py)0
-rw-r--r--waflib/Tools/fc.py (renamed from Tools/fc.py)0
-rw-r--r--waflib/Tools/fc_config.py (renamed from Tools/fc_config.py)0
-rw-r--r--waflib/Tools/fc_scan.py (renamed from Tools/fc_scan.py)0
-rw-r--r--waflib/Tools/flex.py (renamed from Tools/flex.py)0
-rw-r--r--waflib/Tools/g95.py (renamed from Tools/g95.py)0
-rw-r--r--waflib/Tools/gas.py (renamed from Tools/gas.py)0
-rw-r--r--waflib/Tools/gcc.py (renamed from Tools/gcc.py)0
-rw-r--r--waflib/Tools/gdc.py (renamed from Tools/gdc.py)0
-rw-r--r--waflib/Tools/gfortran.py (renamed from Tools/gfortran.py)0
-rw-r--r--waflib/Tools/glib2.py (renamed from Tools/glib2.py)0
-rw-r--r--waflib/Tools/gnu_dirs.py (renamed from Tools/gnu_dirs.py)0
-rw-r--r--waflib/Tools/gxx.py (renamed from Tools/gxx.py)0
-rw-r--r--waflib/Tools/icc.py (renamed from Tools/icc.py)0
-rw-r--r--waflib/Tools/icpc.py (renamed from Tools/icpc.py)0
-rw-r--r--waflib/Tools/ifort.py (renamed from Tools/ifort.py)0
-rw-r--r--waflib/Tools/intltool.py (renamed from Tools/intltool.py)0
-rw-r--r--waflib/Tools/irixcc.py (renamed from Tools/irixcc.py)0
-rw-r--r--waflib/Tools/javaw.py (renamed from Tools/javaw.py)0
-rw-r--r--waflib/Tools/ldc2.py (renamed from Tools/ldc2.py)0
-rw-r--r--waflib/Tools/lua.py (renamed from Tools/lua.py)0
-rw-r--r--waflib/Tools/md5_tstamp.py (renamed from Tools/md5_tstamp.py)0
-rw-r--r--waflib/Tools/msvc.py (renamed from Tools/msvc.py)0
-rw-r--r--waflib/Tools/nasm.py (renamed from Tools/nasm.py)0
-rw-r--r--waflib/Tools/nobuild.py (renamed from Tools/nobuild.py)0
-rw-r--r--waflib/Tools/perl.py (renamed from Tools/perl.py)0
-rw-r--r--waflib/Tools/python.py (renamed from Tools/python.py)0
-rw-r--r--waflib/Tools/qt5.py (renamed from Tools/qt5.py)0
-rw-r--r--waflib/Tools/ruby.py (renamed from Tools/ruby.py)0
-rw-r--r--waflib/Tools/suncc.py (renamed from Tools/suncc.py)0
-rw-r--r--waflib/Tools/suncxx.py (renamed from Tools/suncxx.py)0
-rw-r--r--waflib/Tools/tex.py (renamed from Tools/tex.py)0
-rw-r--r--waflib/Tools/vala.py (renamed from Tools/vala.py)0
-rw-r--r--waflib/Tools/waf_unit_test.py (renamed from Tools/waf_unit_test.py)0
-rw-r--r--waflib/Tools/winres.py (renamed from Tools/winres.py)0
-rw-r--r--waflib/Tools/xlc.py (renamed from Tools/xlc.py)0
-rw-r--r--waflib/Tools/xlcxx.py (renamed from Tools/xlcxx.py)0
-rw-r--r--waflib/Utils.py (renamed from Utils.py)0
-rw-r--r--waflib/__init__.py (renamed from __init__.py)0
-rw-r--r--waflib/ansiterm.py (renamed from ansiterm.py)0
-rw-r--r--waflib/extras/__init__.py (renamed from extras/__init__.py)0
-rw-r--r--waflib/extras/autowaf.py (renamed from extras/autowaf.py)0
-rw-r--r--waflib/extras/batched_cc.py (renamed from extras/batched_cc.py)0
-rw-r--r--waflib/extras/biber.py (renamed from extras/biber.py)0
-rw-r--r--waflib/extras/bjam.py (renamed from extras/bjam.py)0
-rw-r--r--waflib/extras/blender.py (renamed from extras/blender.py)0
-rw-r--r--waflib/extras/boo.py (renamed from extras/boo.py)0
-rw-r--r--waflib/extras/boost.py (renamed from extras/boost.py)0
-rw-r--r--waflib/extras/build_file_tracker.py (renamed from extras/build_file_tracker.py)0
-rw-r--r--waflib/extras/build_logs.py (renamed from extras/build_logs.py)0
-rw-r--r--waflib/extras/buildcopy.py (renamed from extras/buildcopy.py)0
-rw-r--r--waflib/extras/c_bgxlc.py (renamed from extras/c_bgxlc.py)0
-rw-r--r--waflib/extras/c_dumbpreproc.py (renamed from extras/c_dumbpreproc.py)0
-rw-r--r--waflib/extras/c_emscripten.py (renamed from extras/c_emscripten.py)0
-rw-r--r--waflib/extras/c_nec.py (renamed from extras/c_nec.py)0
-rw-r--r--waflib/extras/cabal.py (renamed from extras/cabal.py)0
-rw-r--r--waflib/extras/cfg_altoptions.py (renamed from extras/cfg_altoptions.py)0
-rw-r--r--waflib/extras/clang_compilation_database.py (renamed from extras/clang_compilation_database.py)0
-rw-r--r--waflib/extras/codelite.py (renamed from extras/codelite.py)0
-rw-r--r--waflib/extras/color_gcc.py (renamed from extras/color_gcc.py)0
-rw-r--r--waflib/extras/color_rvct.py (renamed from extras/color_rvct.py)0
-rw-r--r--waflib/extras/compat15.py (renamed from extras/compat15.py)0
-rw-r--r--waflib/extras/cppcheck.py (renamed from extras/cppcheck.py)0
-rw-r--r--waflib/extras/cpplint.py (renamed from extras/cpplint.py)0
-rw-r--r--waflib/extras/cross_gnu.py (renamed from extras/cross_gnu.py)0
-rw-r--r--waflib/extras/cython.py (renamed from extras/cython.py)0
-rw-r--r--waflib/extras/dcc.py (renamed from extras/dcc.py)0
-rw-r--r--waflib/extras/distnet.py (renamed from extras/distnet.py)0
-rw-r--r--waflib/extras/doxygen.py (renamed from extras/doxygen.py)0
-rw-r--r--waflib/extras/dpapi.py (renamed from extras/dpapi.py)0
-rw-r--r--waflib/extras/eclipse.py (renamed from extras/eclipse.py)0
-rw-r--r--waflib/extras/erlang.py (renamed from extras/erlang.py)0
-rw-r--r--waflib/extras/fast_partial.py (renamed from extras/fast_partial.py)0
-rw-r--r--waflib/extras/fc_bgxlf.py (renamed from extras/fc_bgxlf.py)0
-rw-r--r--waflib/extras/fc_cray.py (renamed from extras/fc_cray.py)0
-rw-r--r--waflib/extras/fc_nag.py (renamed from extras/fc_nag.py)0
-rw-r--r--waflib/extras/fc_nec.py (renamed from extras/fc_nec.py)0
-rw-r--r--waflib/extras/fc_open64.py (renamed from extras/fc_open64.py)0
-rw-r--r--waflib/extras/fc_pgfortran.py (renamed from extras/fc_pgfortran.py)0
-rw-r--r--waflib/extras/fc_solstudio.py (renamed from extras/fc_solstudio.py)0
-rw-r--r--waflib/extras/fc_xlf.py (renamed from extras/fc_xlf.py)0
-rw-r--r--waflib/extras/file_to_object.py (renamed from extras/file_to_object.py)0
-rw-r--r--waflib/extras/fluid.py (renamed from extras/fluid.py)0
-rw-r--r--waflib/extras/freeimage.py (renamed from extras/freeimage.py)0
-rw-r--r--waflib/extras/fsb.py (renamed from extras/fsb.py)0
-rw-r--r--waflib/extras/fsc.py (renamed from extras/fsc.py)0
-rw-r--r--waflib/extras/gccdeps.py (renamed from extras/gccdeps.py)0
-rw-r--r--waflib/extras/gdbus.py (renamed from extras/gdbus.py)0
-rw-r--r--waflib/extras/gob2.py (renamed from extras/gob2.py)0
-rw-r--r--waflib/extras/halide.py (renamed from extras/halide.py)0
-rwxr-xr-xwaflib/extras/javatest.py (renamed from extras/javatest.py)0
-rw-r--r--waflib/extras/kde4.py (renamed from extras/kde4.py)0
-rw-r--r--waflib/extras/local_rpath.py (renamed from extras/local_rpath.py)0
-rw-r--r--waflib/extras/lv2.py (renamed from extras/lv2.py)0
-rw-r--r--waflib/extras/make.py (renamed from extras/make.py)0
-rw-r--r--waflib/extras/midl.py (renamed from extras/midl.py)0
-rw-r--r--waflib/extras/msvcdeps.py (renamed from extras/msvcdeps.py)0
-rw-r--r--waflib/extras/msvs.py (renamed from extras/msvs.py)0
-rw-r--r--waflib/extras/netcache_client.py (renamed from extras/netcache_client.py)0
-rw-r--r--waflib/extras/objcopy.py (renamed from extras/objcopy.py)0
-rw-r--r--waflib/extras/ocaml.py (renamed from extras/ocaml.py)0
-rw-r--r--waflib/extras/package.py (renamed from extras/package.py)0
-rw-r--r--waflib/extras/parallel_debug.py (renamed from extras/parallel_debug.py)0
-rw-r--r--waflib/extras/pch.py (renamed from extras/pch.py)0
-rw-r--r--waflib/extras/pep8.py (renamed from extras/pep8.py)0
-rw-r--r--waflib/extras/pgicc.py (renamed from extras/pgicc.py)0
-rw-r--r--waflib/extras/pgicxx.py (renamed from extras/pgicxx.py)0
-rw-r--r--waflib/extras/proc.py (renamed from extras/proc.py)0
-rw-r--r--waflib/extras/protoc.py (renamed from extras/protoc.py)0
-rw-r--r--waflib/extras/pyqt5.py (renamed from extras/pyqt5.py)0
-rw-r--r--waflib/extras/pytest.py (renamed from extras/pytest.py)0
-rw-r--r--waflib/extras/qnxnto.py (renamed from extras/qnxnto.py)0
-rw-r--r--waflib/extras/qt4.py (renamed from extras/qt4.py)0
-rw-r--r--waflib/extras/relocation.py (renamed from extras/relocation.py)0
-rw-r--r--waflib/extras/remote.py (renamed from extras/remote.py)0
-rw-r--r--waflib/extras/resx.py (renamed from extras/resx.py)0
-rw-r--r--waflib/extras/review.py (renamed from extras/review.py)0
-rw-r--r--waflib/extras/rst.py (renamed from extras/rst.py)0
-rw-r--r--waflib/extras/run_do_script.py (renamed from extras/run_do_script.py)0
-rw-r--r--waflib/extras/run_m_script.py (renamed from extras/run_m_script.py)0
-rw-r--r--waflib/extras/run_py_script.py (renamed from extras/run_py_script.py)0
-rw-r--r--waflib/extras/run_r_script.py (renamed from extras/run_r_script.py)0
-rw-r--r--waflib/extras/sas.py (renamed from extras/sas.py)0
-rw-r--r--waflib/extras/satellite_assembly.py (renamed from extras/satellite_assembly.py)0
-rw-r--r--waflib/extras/scala.py (renamed from extras/scala.py)0
-rw-r--r--waflib/extras/slow_qt4.py (renamed from extras/slow_qt4.py)0
-rw-r--r--waflib/extras/softlink_libs.py (renamed from extras/softlink_libs.py)0
-rw-r--r--waflib/extras/stale.py (renamed from extras/stale.py)0
-rw-r--r--waflib/extras/stracedeps.py (renamed from extras/stracedeps.py)0
-rw-r--r--waflib/extras/swig.py (renamed from extras/swig.py)0
-rw-r--r--waflib/extras/syms.py (renamed from extras/syms.py)0
-rw-r--r--waflib/extras/ticgt.py (renamed from extras/ticgt.py)0
-rw-r--r--waflib/extras/unity.py (renamed from extras/unity.py)0
-rw-r--r--waflib/extras/use_config.py (renamed from extras/use_config.py)0
-rw-r--r--waflib/extras/valadoc.py (renamed from extras/valadoc.py)0
-rw-r--r--waflib/extras/waf_xattr.py (renamed from extras/waf_xattr.py)0
-rw-r--r--waflib/extras/why.py (renamed from extras/why.py)0
-rw-r--r--waflib/extras/win32_opts.py (renamed from extras/win32_opts.py)0
-rw-r--r--waflib/extras/wix.py (renamed from extras/wix.py)0
-rw-r--r--waflib/extras/xcode6.py (renamed from extras/xcode6.py)0
-rw-r--r--waflib/fixpy2.py (renamed from fixpy2.py)0
-rwxr-xr-xwaflib/processor.py (renamed from processor.py)0
-rwxr-xr-xwaflib/waf16
-rw-r--r--wscript495
284 files changed, 24414 insertions, 27 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..f063da3
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+waf binary \ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 8d35cb3..9b1df0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
-__pycache__
-*.pyc
+build/**
+.waf-*
+.lock-waf*
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..3cea4ab
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,11 @@
+Author:
+ David Robillard <d@drobilla.net>
+
+GTK2 GUI and I18N support:
+ Lars Luthman <lars.luthman@gmail.com>
+
+Dynamic manifest support:
+ Stefano D'Angelo
+
+Plugin execution via Python bindings:
+ Kaspar Emanuel <kaspar.emanuel@gmail.com> \ No newline at end of file
diff --git a/COPYING b/COPYING
index a4147d2..908a4e0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,25 +1,13 @@
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
+Copyright 2011-2017 David Robillard <http://drobilla.net>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..9b54f51
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,66 @@
+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 <http://semver.org/>.
+
+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/NEWS b/NEWS
new file mode 100644
index 0000000..fa335a9
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,231 @@
+lilv (0.24.5) unstable;
+
+ * Fix GCC8 warnings
+ * Fix creating directories across drives on Windows
+ * Don't print errors when saving state if correct links already exist
+ * Fix issues with loading state with saved files from the model
+
+ -- David Robillard <d@drobilla.net> Sun, 16 Sep 2018 11:36:45 +0200
+
+lilv (0.24.4) stable;
+
+ * Fix saving state when broken links are encountered
+ * Don't attempt to load remote or non-Turtle files
+ * lv2apply: Activate plugin before running
+ * lv2apply: Use default values when they are not nan
+ * lv2bench: Improve support for plugins with sequence ports
+ * lv2bench: Support running a single plugin given on the command line
+ * Gracefully handle plugins with missing binary URIs
+ * Remove use of deprecated readdir_r
+ * Install Python bindings when configured without tests
+ (thanks Clement Skau)
+
+ -- David Robillard <d@drobilla.net> Sun, 22 Jul 2018 20:42:00 +0200
+
+lilv (0.24.2) stable;
+
+ * Fix saving state to paths that contain URI delimiters (#, ?, etc)
+ * Fix comparison of restored states with paths
+
+ -- David Robillard <d@drobilla.net> Wed, 04 Jan 2017 11:48:08 -0500
+
+lilv (0.24.0) stable;
+
+ * Add new hand-crafted Pythonic bindings with full test coverage
+ * Add lv2apply utility for applying plugins to audio files
+ * Add lilv_world_get_symbol()
+ * Add lilv_state_set_metadata() for adding state banks/comments/etc
+ (based on patch from Hanspeter Portner)
+ * Fix crash when state contains non-POD properties
+ * Fix crash when NULL predicate is passed to lilv_world_find_nodes()
+ * Fix state file versioning
+ * Unload contained resources when bundle is unloaded
+ * Do not instantiate plugin when data fails to parse
+ * Support re-loading plugins
+ * Replace bundles if bundle with newer plugin version is loaded
+ (based on patch from Robin Gareus)
+ * Fix loading dyn-manifest from bundles with spaces in their path
+ * Check lv2:binary predicate for UIs
+ * Add LILV_URI_ATOM_PORT and LILV_URI_CV_PORT defines
+ * Fix documentation installation
+ * Fix outdated comment references to lilv_uri_to_path()
+
+ -- David Robillard <d@drobilla.net> Mon, 19 Sep 2016 22:24:57 -0400
+
+lilv (0.22.0) stable;
+
+ * Fix loading files with spaces in their path
+ * Add lilv_file_uri_parse() for correct URI to path conversion
+ * Add lilv_node_get_path() for convenient file URI path access
+ * Add lilv_state_emit_port_values() for special port value handling
+ * Add lilv_state_get_uri()
+ * Add lilv_state_delete() for deleting user saved presets
+ * Add lilv_free() for systems picky about such things
+ * Fix lilv_world_ask() to work with wildcards
+ * Fix creation of duplicate manifest entries when saving state
+ * Fix bindings for Python 3
+ * Load discovered owl ontologies as specifications
+ * Expose lilv_world_load_specifications() and
+ lilv_world_load_plugin_classes()
+ * Tolerate passing NULL to lilv_state_restore()
+ * Preserve absolute paths in state if no link directory is given
+ * Fix a few minor/unlikely memory errors
+ * Configure based on compiler target OS for cross-compilation
+ * Fix lilv_realpath() on pre-POSIX-2008 systems
+ * Fix directory walking on some systems (thanks Matt Fischer)
+ * Windows fixes (thanks John Emmas)
+ * Minor documentation improvements
+ * Upgrade to waf 1.8.14
+
+ -- David Robillard <d@drobilla.net> Thu, 08 Oct 2015 15:39:29 -0400
+
+lilv (0.20.0) stable;
+
+ * Don't load files multiple times if they are listed as rdfs:seeAlso for
+ several plugins
+ * Call lv2_lib_descriptor separately for different bundle paths
+ (fix loading several dynamic plugins like Ingen at once)
+ * Tolerate calling lilv_node_as_uri or lilv_node_as_blank on NULL
+ * Add convenient lilv_new_file_uri for creating file URIs
+ * Fix use of lv2info -m and -p options to write plugin data
+ (useful for porting plugins bridges with NASPRO)
+ * Fix issues with lilv_plugin_get_author_name and friends
+ (thanks Filipe Coelho)
+ * Improved/working lv2_apply.py to apply plugin to a .wav
+ (thanks Joe Button)
+ * Add lilv_world_unload_bundle() and lilv_world_unload_resource()
+ * Fix several minor memory leaks
+ * Improve test coverage
+ * Upgrade to waf 1.7.16
+
+ -- David Robillard <d@drobilla.net> Fri, 08 Aug 2014 18:21:32 -0400
+
+lilv (0.18.0) stable;
+
+ * Allow lilv_state_restore() to be used without passing an instance,
+ for restoring port values via a callback only
+ * Fix unlikely memory leak in lilv_plugin_instantiate()
+ * Support denoting latency ports with lv2:designation lv2:latency
+ * Allow passing NULL port_class to lilv_plugin_get_port_by_designation
+ * Call GetProcAddress with correct calling convention on Windows
+ * Add support for running plugins from Python by Kaspar Emanuel
+ * Clean up after test suite so multiple runs are successful
+ * Add lilv_port_get_node() for using world query functions with ports
+ * lv2info: Don't display invalid control maxes and defaults
+ (patch from Robin Gareus)
+ * lilvmm.hpp: Add wrappers for UI API
+
+ -- David Robillard <d@drobilla.net> Sat, 04 Jan 2014 16:06:42 -0500
+
+lilv (0.16.0) stable;
+
+ * Add lilv_world_ask() for easily checking if a statement exists
+ * Add lilv_world_get() and lilv_port_get() for easily getting one value
+ * Add lilv_nodes_merge()
+ * Make lilv_plugin_get_port_by_designation() return a const pointer
+ * Require a URI for lilv_state_to_string() and fail gracefully otherwise
+ * Fail gracefully when lilv_state_new_from_string() is called on NULL
+ * Make state loading functions fall back to lv2:default for port values,
+ so a plugin description can be loaded as default state
+ * Ignore state ports with no value instead of printing an error
+ * Support atom:supports in lilv_port_supports_event()
+ * Add va_list variant of lilv_plugin_get_num_ports_of_class()
+ * Fix several plugin functions that failed to load data if called first
+ * Correctly depend on serd at build time (fix compilation in odd cases)
+ * Disable timestamps in HTML documentation for reproducible build
+ * lilvmm.hpp: Support varargs for Plugin::get_num_ports_of_class()
+ * lilvmm.hpp: Add several missing methods
+ * Update to waf 1.7.8 and autowaf r90 (install docs to versioned directory)
+
+ -- David Robillard <d@drobilla.net> Mon, 18 Feb 2013 16:43:10 -0500
+
+lilv (0.14.4) stable;
+
+ * Deprecate old flawed Lilv::Instance constructors
+ * Fix documentation for ui_type parameter of lilv_ui_is_supported()
+ * Fix crash when lv2info is run with an invalid URI argument
+ * Gracefully handle failure to save plugin state and print error message
+ * Reduce memory usage (per node)
+ * Simpler node implementation always backed by a SordNode
+ * Make all 'zix' symbols private to avoid symbol clashes in static builds
+ * Add lv2bench utility
+ * Fix various hyper-strict warnings
+ * Do not require a C++ compiler to build
+ * Add option to build utilities as static binaries
+ * Upgrade to waf 1.7.2
+ * lilvmm.hpp: Make Lilv::Instance handle features and failed instantiations
+ * lilvmm.hpp: Add Lilv::Instance::get_handle()
+ * lilvmm.hpp: Add Lilv::Instance::get_extension_data()
+
+ -- David Robillard <d@drobilla.net> Thu, 23 Aug 2012 01:38:29 -0400
+
+lilv (0.14.2) stable;
+
+ * Fix dynmanifest support
+
+ -- David Robillard <d@drobilla.net> Thu, 19 Apr 2012 16:11:31 -0400
+
+lilv (0.14.0) stable;
+
+ * Add lilv_plugin_get_extension_data
+ * Use path variables in pkgconfig files
+ * Install man page to DATADIR (e.g. PREFIX/share/man, not PREFIX/man)
+ * Make Lilv::uri_to_path static inline (fix linking errors)
+ * Use correct URI for dcterms:replaces (for hiding old plugins):
+ "http://purl.org/dc/terms/replaces"
+ * Fix compilation on BSD
+ * Only load dynmanifest libraries once per bundle, not once per plugin
+ * Fix lilv_world_find_nodes to work with wildcard subjects
+ * Add lilv_plugin_get_related to get resources related to plugins that
+ are not directly rdfs:seeAlso linked (e.g. presets)
+ * Add lilv_world_load_resource for related resources (e.g. presets)
+ * Print presets in lv2info
+ * Remove locale smashing kludges and use new serd functions for converting
+ nodes to/from numbers.
+ * Add LilvState API for handling plugin state. This makes it simple to
+ save and restore plugin state both in memory and on disk, as well as
+ save presets in a host-sharable way since the disk format is identical
+ to the LV2 presets format.
+ * Update old references to lv2_list (now lv2ls)
+ * Support compilation as C++ under MSVC++.
+ * Remove use of wordexp.
+ * Add lilv_plugin_get_port_by_designation() and lilv_port_get_index() as an
+ improved generic alternative to lilv_plugin_get_latency_port_index().
+ * Add lilv_plugin_get_project() and get author information from project if
+ it is not given directly on the plugin.
+
+ -- David Robillard <d@drobilla.net> Wed, 18 Apr 2012 20:06:28 -0400
+
+lilv (0.5.0) stable;
+
+ * Remove glib dependency
+ * Add lv2core as a pkg-config dependency (for lv2.h header include)
+ * Obey prefix when installing bash completion script
+ * Support integer minimum, maximum, and default port values in
+ lilv_plugin_get_port_ranges_float
+ * Add ability to build static library
+
+ -- David Robillard <d@drobilla.net> Thu, 29 Sep 2011 00:00:00 -0400
+
+lilv (0.4.4) stable;
+
+ * Fix building python bindings
+ * Make free methods tolerate being called on NULL
+ * Remove lv2jack (replaced by jalv)
+ * Fix parsing extra plugin data files in other bundles
+ * Fix lilv_ui_is_supported when Lilv is built independently
+
+ -- David Robillard <d@drobilla.net> Sat, 11 Jun 2011 11:20:11 -0400
+
+lilv (0.4.2) stable;
+
+ * Fix compilation issues on some systems
+ * Fix build system Python 3 compatibility
+
+ -- David Robillard <d@drobilla.net> Wed, 25 May 2011 19:00:00 -0400
+
+lilv (0.4.0) stable;
+
+ * Initial version (forked from SLV2)
+
+ -- David Robillard <d@drobilla.net> Tue, 24 May 2011 23:00:00 -0400
diff --git a/README b/README
new file mode 100644
index 0000000..807fc2b
--- /dev/null
+++ b/README
@@ -0,0 +1,8 @@
+Lilv
+====
+
+Lilv is a C library to make the use of LV2 plugins as simple as possible for
+applications.
+For more information, see <http://drobilla.net/software/lilv>.
+
+ -- David Robillard <d@drobilla.net>
diff --git a/bindings/lilv.i b/bindings/lilv.i
new file mode 100644
index 0000000..f6254a7
--- /dev/null
+++ b/bindings/lilv.i
@@ -0,0 +1,66 @@
+%module lilv
+%typedef unsigned uint32_t;
+%{
+#define SWIG_FILE_WITH_INIT
+#include "lilv/lilv.h"
+#include "lilv/lilvmm.hpp"
+%}
+
+%include "numpy.i"
+%init %{
+ import_array();
+%}
+%apply (float* INPLACE_ARRAY1) {(void* data_location)}
+
+%feature("compactdefaultargs") %{
+ lilv_plugin_get_num_ports_of_class;
+ get_num_ports_of_class;
+%}
+%varargs(3, LilvNode* node = NULL) lilv_plugin_get_num_ports_of_class;
+%varargs(3, LilvNode* node = NULL) get_num_ports_of_class;
+%typemap(in, numinputs=0) LilvNode *node3 ""; // Make sure it's NULL terminated
+
+%include "lilv/lilv.h"
+%include "lilv/lilvmm.hpp"
+
+namespace Lilv {
+
+%extend Plugins {
+%pythoncode %{
+ def __iter__(self):
+ class Iterator(object):
+ def __init__(self, plugins):
+ self.plugins = plugins
+ self.iter = plugins.begin()
+
+ def __next__(self):
+ if self.plugins.is_end(self.iter):
+ raise StopIteration
+ plugin = self.plugins.get(self.iter)
+ self.iter = self.plugins.next(self.iter)
+ return plugin
+
+ def next(self):
+ "Python 2 iterator protocol"
+ return Iterator.__next__(self)
+
+ return Iterator(self)
+
+ def get_by_uri(self, *args):
+ """get_by_uri(self, LilvNode uri) -> PluginClass"""
+ ret = _lilv.Plugins_get_by_uri(self, *args)
+ if ret.me is None:
+ return None
+ else:
+ return ret
+%}
+};
+
+%extend Node {
+%pythoncode %{
+ def __str__(self):
+ return self.get_turtle_token()
+%}
+};
+
+} /* namespace Lilv */
diff --git a/bindings/numpy.i b/bindings/numpy.i
new file mode 100644
index 0000000..d695b36
--- /dev/null
+++ b/bindings/numpy.i
@@ -0,0 +1,1746 @@
+/* -*- C -*- (not really, but good for syntax highlighting) */
+#ifdef SWIGPYTHON
+
+%{
+#ifndef SWIG_FILE_WITH_INIT
+# define NO_IMPORT_ARRAY
+#endif
+#include "stdio.h"
+#include <numpy/arrayobject.h>
+%}
+
+/**********************************************************************/
+
+%fragment("NumPy_Backward_Compatibility", "header")
+{
+/* Support older NumPy data type names
+*/
+%#if NDARRAY_VERSION < 0x01000000
+%#define NPY_BOOL PyArray_BOOL
+%#define NPY_BYTE PyArray_BYTE
+%#define NPY_UBYTE PyArray_UBYTE
+%#define NPY_SHORT PyArray_SHORT
+%#define NPY_USHORT PyArray_USHORT
+%#define NPY_INT PyArray_INT
+%#define NPY_UINT PyArray_UINT
+%#define NPY_LONG PyArray_LONG
+%#define NPY_ULONG PyArray_ULONG
+%#define NPY_LONGLONG PyArray_LONGLONG
+%#define NPY_ULONGLONG PyArray_ULONGLONG
+%#define NPY_FLOAT PyArray_FLOAT
+%#define NPY_DOUBLE PyArray_DOUBLE
+%#define NPY_LONGDOUBLE PyArray_LONGDOUBLE
+%#define NPY_CFLOAT PyArray_CFLOAT
+%#define NPY_CDOUBLE PyArray_CDOUBLE
+%#define NPY_CLONGDOUBLE PyArray_CLONGDOUBLE
+%#define NPY_OBJECT PyArray_OBJECT
+%#define NPY_STRING PyArray_STRING
+%#define NPY_UNICODE PyArray_UNICODE
+%#define NPY_VOID PyArray_VOID
+%#define NPY_NTYPES PyArray_NTYPES
+%#define NPY_NOTYPE PyArray_NOTYPE
+%#define NPY_CHAR PyArray_CHAR
+%#define NPY_USERDEF PyArray_USERDEF
+%#define npy_intp intp
+
+%#define NPY_MAX_BYTE MAX_BYTE
+%#define NPY_MIN_BYTE MIN_BYTE
+%#define NPY_MAX_UBYTE MAX_UBYTE
+%#define NPY_MAX_SHORT MAX_SHORT
+%#define NPY_MIN_SHORT MIN_SHORT
+%#define NPY_MAX_USHORT MAX_USHORT
+%#define NPY_MAX_INT MAX_INT
+%#define NPY_MIN_INT MIN_INT
+%#define NPY_MAX_UINT MAX_UINT
+%#define NPY_MAX_LONG MAX_LONG
+%#define NPY_MIN_LONG MIN_LONG
+%#define NPY_MAX_ULONG MAX_ULONG
+%#define NPY_MAX_LONGLONG MAX_LONGLONG
+%#define NPY_MIN_LONGLONG MIN_LONGLONG
+%#define NPY_MAX_ULONGLONG MAX_ULONGLONG
+%#define NPY_MAX_INTP MAX_INTP
+%#define NPY_MIN_INTP MIN_INTP
+
+%#define NPY_FARRAY FARRAY
+%#define NPY_F_CONTIGUOUS F_CONTIGUOUS
+%#endif
+}
+
+/**********************************************************************/
+
+/* The following code originally appeared in
+ * enthought/kiva/agg/src/numeric.i written by Eric Jones. It was
+ * translated from C++ to C by John Hunter. Bill Spotz has modified
+ * it to fix some minor bugs, upgrade from Numeric to numpy (all
+ * versions), add some comments and functionality, and convert from
+ * direct code insertion to SWIG fragments.
+ */
+
+%fragment("NumPy_Macros", "header")
+{
+/* Macros to extract array attributes.
+ */
+%#define is_array(a) ((a) && PyArray_Check((PyArrayObject *)a))
+%#define array_type(a) (int)(PyArray_TYPE(a))
+%#define array_numdims(a) (((PyArrayObject *)a)->nd)
+%#define array_dimensions(a) (((PyArrayObject *)a)->dimensions)
+%#define array_size(a,i) (((PyArrayObject *)a)->dimensions[i])
+%#define array_data(a) (((PyArrayObject *)a)->data)
+%#define array_is_contiguous(a) (PyArray_ISCONTIGUOUS(a))
+%#define array_is_native(a) (PyArray_ISNOTSWAPPED(a))
+%#define array_is_fortran(a) (PyArray_ISFORTRAN(a))
+}
+
+/**********************************************************************/
+
+%fragment("NumPy_Utilities", "header")
+{
+ /* Given a PyObject, return a string describing its type.
+ */
+ const char* pytype_string(PyObject* py_obj) {
+ if (py_obj == NULL ) return "C NULL value";
+ if (py_obj == Py_None ) return "Python None" ;
+ if (PyCallable_Check(py_obj)) return "callable" ;
+ if (PyString_Check( py_obj)) return "string" ;
+ if (PyInt_Check( py_obj)) return "int" ;
+ if (PyFloat_Check( py_obj)) return "float" ;
+ if (PyDict_Check( py_obj)) return "dict" ;
+ if (PyList_Check( py_obj)) return "list" ;
+ if (PyTuple_Check( py_obj)) return "tuple" ;
+ if (PyModule_Check( py_obj)) return "module" ;
+%#if PY_MAJOR_VERSION < 3
+ if (PyFile_Check( py_obj)) return "file" ;
+ if (PyInstance_Check(py_obj)) return "instance" ;
+%#endif
+
+ return "unkown type";
+ }
+
+ /* Given a NumPy typecode, return a string describing the type.
+ */
+ const char* typecode_string(int typecode) {
+ static const char* type_names[25] = {"bool", "byte", "unsigned byte",
+ "short", "unsigned short", "int",
+ "unsigned int", "long", "unsigned long",
+ "long long", "unsigned long long",
+ "float", "double", "long double",
+ "complex float", "complex double",
+ "complex long double", "object",
+ "string", "unicode", "void", "ntypes",
+ "notype", "char", "unknown"};
+ return typecode < 24 ? type_names[typecode] : type_names[24];
+ }
+
+ /* Make sure input has correct numpy type. Allow character and byte
+ * to match. Also allow int and long to match. This is deprecated.
+ * You should use PyArray_EquivTypenums() instead.
+ */
+ int type_match(int actual_type, int desired_type) {
+ return PyArray_EquivTypenums(actual_type, desired_type);
+ }
+}
+
+/**********************************************************************/
+
+%fragment("NumPy_Object_to_Array", "header",
+ fragment="NumPy_Backward_Compatibility",
+ fragment="NumPy_Macros",
+ fragment="NumPy_Utilities")
+{
+ /* Given a PyObject pointer, cast it to a PyArrayObject pointer if
+ * legal. If not, set the python error string appropriately and
+ * return NULL.
+ */
+ PyArrayObject* obj_to_array_no_conversion(PyObject* input, int typecode)
+ {
+ PyArrayObject* ary = NULL;
+ if (is_array(input) && (typecode == NPY_NOTYPE ||
+ PyArray_EquivTypenums(array_type(input), typecode)))
+ {
+ ary = (PyArrayObject*) input;
+ }
+ else if is_array(input)
+ {
+ const char* desired_type = typecode_string(typecode);
+ const char* actual_type = typecode_string(array_type(input));
+ PyErr_Format(PyExc_TypeError,
+ "Array of type '%s' required. Array of type '%s' given",
+ desired_type, actual_type);
+ ary = NULL;
+ }
+ else
+ {
+ const char * desired_type = typecode_string(typecode);
+ const char * actual_type = pytype_string(input);
+ PyErr_Format(PyExc_TypeError,
+ "Array of type '%s' required. A '%s' was given",
+ desired_type, actual_type);
+ ary = NULL;
+ }
+ return ary;
+ }
+
+ /* Convert the given PyObject to a NumPy array with the given
+ * typecode. On success, return a valid PyArrayObject* with the
+ * correct type. On failure, the python error string will be set and
+ * the routine returns NULL.
+ */
+ PyArrayObject* obj_to_array_allow_conversion(PyObject* input, int typecode,
+ int* is_new_object)
+ {
+ PyArrayObject* ary = NULL;
+ PyObject* py_obj;
+ if (is_array(input) && (typecode == NPY_NOTYPE ||
+ PyArray_EquivTypenums(array_type(input),typecode)))
+ {
+ ary = (PyArrayObject*) input;
+ *is_new_object = 0;
+ }
+ else
+ {
+ py_obj = PyArray_FROMANY(input, typecode, 0, 0, NPY_DEFAULT);
+ /* If NULL, PyArray_FromObject will have set python error value.*/
+ ary = (PyArrayObject*) py_obj;
+ *is_new_object = 1;
+ }
+ return ary;
+ }
+
+ /* Given a PyArrayObject, check to see if it is contiguous. If so,
+ * return the input pointer and flag it as not a new object. If it is
+ * not contiguous, create a new PyArrayObject using the original data,
+ * flag it as a new object and return the pointer.
+ */
+ PyArrayObject* make_contiguous(PyArrayObject* ary, int* is_new_object,
+ int min_dims, int max_dims)
+ {
+ PyArrayObject* result;
+ if (array_is_contiguous(ary))
+ {
+ result = ary;
+ *is_new_object = 0;
+ }
+ else
+ {
+ result = (PyArrayObject*) PyArray_ContiguousFromObject((PyObject*)ary,
+ array_type(ary),
+ min_dims,
+ max_dims);
+ *is_new_object = 1;
+ }
+ return result;
+ }
+
+ /* Given a PyArrayObject, check to see if it is Fortran-contiguous.
+ * If so, return the input pointer, but do not flag it as not a new
+ * object. If it is not Fortran-contiguous, create a new
+ * PyArrayObject using the original data, flag it as a new object
+ * and return the pointer.
+ */
+ PyArrayObject* make_fortran(PyArrayObject* ary, int* is_new_object,
+ int min_dims, int max_dims)
+ {
+ PyArrayObject* result;
+ if (array_is_fortran(ary))
+ {
+ result = ary;
+ *is_new_object = 0;
+ }
+ else
+ {
+ Py_INCREF(ary->descr);
+ result = (PyArrayObject*) PyArray_FromArray(ary, ary->descr, NPY_FORTRAN);
+ *is_new_object = 1;
+ }
+ return result;
+ }
+
+ /* Convert a given PyObject to a contiguous PyArrayObject of the
+ * specified type. If the input object is not a contiguous
+ * PyArrayObject, a new one will be created and the new object flag
+ * will be set.
+ */
+ PyArrayObject* obj_to_array_contiguous_allow_conversion(PyObject* input,
+ int typecode,
+ int* is_new_object)
+ {
+ int is_new1 = 0;
+ int is_new2 = 0;
+ PyArrayObject* ary2;
+ PyArrayObject* ary1 = obj_to_array_allow_conversion(input, typecode,
+ &is_new1);
+ if (ary1)
+ {
+ ary2 = make_contiguous(ary1, &is_new2, 0, 0);
+ if ( is_new1 && is_new2)
+ {
+ Py_DECREF(ary1);
+ }
+ ary1 = ary2;
+ }
+ *is_new_object = is_new1 || is_new2;
+ return ary1;
+ }
+
+ /* Convert a given PyObject to a Fortran-ordered PyArrayObject of the
+ * specified type. If the input object is not a Fortran-ordered
+ * PyArrayObject, a new one will be created and the new object flag
+ * will be set.
+ */
+ PyArrayObject* obj_to_array_fortran_allow_conversion(PyObject* input,
+ int typecode,
+ int* is_new_object)
+ {
+ int is_new1 = 0;
+ int is_new2 = 0;
+ PyArrayObject* ary2;
+ PyArrayObject* ary1 = obj_to_array_allow_conversion(input, typecode,
+ &is_new1);
+ if (ary1)
+ {
+ ary2 = make_fortran(ary1, &is_new2, 0, 0);
+ if (is_new1 && is_new2)
+ {
+ Py_DECREF(ary1);
+ }
+ ary1 = ary2;
+ }
+ *is_new_object = is_new1 || is_new2;
+ return ary1;
+ }
+
+ /* The following code was added by Ilmar M. Wilbers for forcing a copy of the
+ * object even when it is a NumPy array. This is meant for use with the
+ * IN_ARRAY typemaps, and allows the user to perform changes on an array
+ * without these chenges being reflected in the calling code.
+ */
+
+ /* Convert the given PyObject to a NumPy array with the given
+ * typecode as a copy. On success, return a valid PyArrayObject* with the
+ * correct type. On failure, the python error string will be set and
+ * the routine returns NULL.
+ */
+ PyArrayObject* obj_to_array_force_conversion(PyObject* input, int typecode,
+ int* is_new_object)
+ {
+ PyArrayObject* ary = NULL;
+ PyObject* py_obj;
+ if (is_array(input) && (typecode == NPY_NOTYPE ||
+ PyArray_EquivTypenums(array_type(input),typecode)))
+ {
+ py_obj = PyArray_Copy((PyArrayObject*) input);
+ ary = (PyArrayObject*) py_obj;
+ *is_new_object = 1;
+ }
+ else
+ {
+ py_obj = PyArray_FROMANY(input, typecode, 0, 0, NPY_DEFAULT);
+ /* If NULL, PyArray_FromObject will have set python error value.*/
+ ary = (PyArrayObject*) py_obj;
+ *is_new_object = 1;
+ }
+ return ary;
+ }
+
+ /* Convert a given PyObject to a contiguous PyArrayObject of the
+ * specified type. If the input object is not a contiguous
+ * PyArrayObject, a new one will be created and the new object flag
+ * will be set.
+ */
+ PyArrayObject* obj_to_array_contiguous_force_conversion(PyObject* input,
+ int typecode,
+ int* is_new_object)
+ {
+ int is_new1 = 0;
+ int is_new2 = 0;
+ PyArrayObject* ary2;
+ PyArrayObject* ary1 = obj_to_array_force_conversion(input, typecode,
+ &is_new1);
+ if (ary1)
+ {
+ ary2 = make_contiguous(ary1, &is_new2, 0, 0);
+ if ( is_new1 && is_new2)
+ {
+ Py_DECREF(ary1);
+ }
+ ary1 = ary2;
+ }
+ *is_new_object = is_new1 || is_new2;
+ return ary1;
+ }
+
+ /* Convert a given PyObject to a Fortran-ordered PyArrayObject of the
+ * specified type. If the input object is not a Fortran-ordered
+ * PyArrayObject, a new one will be created and the new object flag
+ * will be set.
+ */
+ PyArrayObject* obj_to_array_fortran_force_conversion(PyObject* input,
+ int typecode,
+ int* is_new_object)
+ {
+ int is_new1 = 0;
+ int is_new2 = 0;
+ PyArrayObject* ary2;
+ PyArrayObject* ary1 = obj_to_array_force_conversion(input, typecode,
+ &is_new1);
+ if (ary1)
+ {
+ ary2 = make_fortran(ary1, &is_new2, 0, 0);
+ if (is_new1 && is_new2)
+ {
+ Py_DECREF(ary1);
+ }
+ ary1 = ary2;
+ }
+ *is_new_object = is_new1 || is_new2;
+ return ary1;
+ }
+ /* End modifications by Ilmar M. Wilbers
+ */
+
+} /* end fragment */
+
+
+/**********************************************************************/
+
+%fragment("NumPy_Array_Requirements", "header",
+ fragment="NumPy_Backward_Compatibility",
+ fragment="NumPy_Macros")
+{
+ /* Test whether a python object is contiguous. If array is
+ * contiguous, return 1. Otherwise, set the python error string and
+ * return 0.
+ */
+ int require_contiguous(PyArrayObject* ary)
+ {
+ int contiguous = 1;
+ if (!array_is_contiguous(ary))
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "Array must be contiguous. A non-contiguous array was given");
+ contiguous = 0;
+ }
+ return contiguous;
+ }
+
+ /* Require that a numpy array is not byte-swapped. If the array is
+ * not byte-swapped, return 1. Otherwise, set the python error string
+ * and return 0.
+ */
+ int require_native(PyArrayObject* ary)
+ {
+ int native = 1;
+ if (!array_is_native(ary))
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "Array must have native byteorder. "
+ "A byte-swapped array was given");
+ native = 0;
+ }
+ return native;
+ }
+
+ /* Require the given PyArrayObject to have a specified number of
+ * dimensions. If the array has the specified number of dimensions,
+ * return 1. Otherwise, set the python error string and return 0.
+ */
+ int require_dimensions(PyArrayObject* ary, int exact_dimensions)
+ {
+ int success = 1;
+ if (array_numdims(ary) != exact_dimensions)
+ {
+ PyErr_Format(PyExc_TypeError,
+ "Array must have %d dimensions. Given array has %d dimensions",
+ exact_dimensions, array_numdims(ary));
+ success = 0;
+ }
+ return success;
+ }
+
+ /* Require the given PyArrayObject to have one of a list of specified
+ * number of dimensions. If the array has one of the specified number
+ * of dimensions, return 1. Otherwise, set the python error string
+ * and return 0.
+ */
+ int require_dimensions_n(PyArrayObject* ary, int* exact_dimensions, int n)
+ {
+ int success = 0;
+ int i;
+ char dims_str[255] = "";
+ char s[255];
+ for (i = 0; i < n && !success; i++)
+ {
+ if (array_numdims(ary) == exact_dimensions[i])
+ {
+ success = 1;
+ }
+ }
+ if (!success)
+ {
+ for (i = 0; i < n-1; i++)
+ {
+ sprintf(s, "%d, ", exact_dimensions[i]);
+ strcat(dims_str,s);
+ }
+ sprintf(s, " or %d", exact_dimensions[n-1]);
+ strcat(dims_str,s);
+ PyErr_Format(PyExc_TypeError,
+ "Array must have %s dimensions. Given array has %d dimensions",
+ dims_str, array_numdims(ary));
+ }
+ return success;
+ }
+
+ /* Require the given PyArrayObject to have a specified shape. If the
+ * array has the specified shape, return 1. Otherwise, set the python
+ * error string and return 0.
+ */
+ int require_size(PyArrayObject* ary, npy_intp* size, int n)
+ {
+ int i;
+ int success = 1;
+ int len;
+ char desired_dims[255] = "[";
+ char s[255];
+ char actual_dims[255] = "[";
+ for(i=0; i < n;i++)
+ {
+ if (size[i] != -1 && size[i] != array_size(ary,i))
+ {
+ success = 0;
+ }
+ }
+ if (!success)
+ {
+ for (i = 0; i < n; i++)
+ {
+ if (size[i] == -1)
+ {
+ sprintf(s, "*,");
+ }
+ else
+ {
+ sprintf(s, "%ld,", (long int)size[i]);
+ }
+ strcat(desired_dims,s);
+ }
+ len = strlen(desired_dims);
+ desired_dims[len-1] = ']';
+ for (i = 0; i < n; i++)
+ {
+ sprintf(s, "%ld,", (long int)array_size(ary,i));
+ strcat(actual_dims,s);
+ }
+ len = strlen(actual_dims);
+ actual_dims[len-1] = ']';
+ PyErr_Format(PyExc_TypeError,
+ "Array must have shape of %s. Given array has shape of %s",
+ desired_dims, actual_dims);
+ }
+ return success;
+ }
+
+ /* Require the given PyArrayObject to to be FORTRAN ordered. If the
+ * the PyArrayObject is already FORTRAN ordered, do nothing. Else,
+ * set the FORTRAN ordering flag and recompute the strides.
+ */
+ int require_fortran(PyArrayObject* ary)
+ {
+ int success = 1;
+ int nd = array_numdims(ary);
+ int i;
+ if (array_is_fortran(ary)) return success;
+ /* Set the FORTRAN ordered flag */
+ ary->flags = NPY_FARRAY;
+ /* Recompute the strides */
+ ary->strides[0] = ary->strides[nd-1];
+ for (i=1; i < nd; ++i)
+ ary->strides[i] = ary->strides[i-1] * array_size(ary,i-1);
+ return success;
+ }
+}
+
+/* Combine all NumPy fragments into one for convenience */
+%fragment("NumPy_Fragments", "header",
+ fragment="NumPy_Backward_Compatibility",
+ fragment="NumPy_Macros",
+ fragment="NumPy_Utilities",
+ fragment="NumPy_Object_to_Array",
+ fragment="NumPy_Array_Requirements") { }
+
+/* End John Hunter translation (with modifications by Bill Spotz)
+ */
+
+/* %numpy_typemaps() macro
+ *
+ * This macro defines a family of 42 typemaps that allow C arguments
+ * of the form
+ *
+ * (DATA_TYPE IN_ARRAY1[ANY])
+ * (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+ * (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+ *
+ * (DATA_TYPE IN_ARRAY2[ANY][ANY])
+ * (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+ * (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+ *
+ * (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+ * (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3)
+ * (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3)
+ *
+ * (DATA_TYPE INPLACE_ARRAY1[ANY])
+ * (DATA_TYPE* INPLACE_ARRAY1)
+ * (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1)
+ * (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1)
+ *
+ * (DATA_TYPE INPLACE_ARRAY2[ANY][ANY])
+ * (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2)
+ * (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2)
+ *
+ * (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY])
+ * (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3)
+ * (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3)
+ *
+ * (DATA_TYPE ARGOUT_ARRAY1[ANY])
+ * (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1)
+ * (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1)
+ *
+ * (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY])
+ *
+ * (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY])
+ *
+ * (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1)
+ * (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1)
+ *
+ * (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+ * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2)
+ * (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+ * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2)
+ *
+ * (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+ * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3)
+ * (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+ * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3)
+ *
+ * where "DATA_TYPE" is any type supported by the NumPy module, and
+ * "DIM_TYPE" is any int-like type suitable for specifying dimensions.
+ * The difference between "ARRAY" typemaps and "FARRAY" typemaps is
+ * that the "FARRAY" typemaps expect FORTRAN ordering of
+ * multidimensional arrays. In python, the dimensions will not need
+ * to be specified (except for the "DATA_TYPE* ARGOUT_ARRAY1"
+ * typemaps). The IN_ARRAYs can be a numpy array or any sequence that
+ * can be converted to a numpy array of the specified type. The
+ * INPLACE_ARRAYs must be numpy arrays of the appropriate type. The
+ * ARGOUT_ARRAYs will be returned as new numpy arrays of the
+ * appropriate type.
+ *
+ * These typemaps can be applied to existing functions using the
+ * %apply directive. For example:
+ *
+ * %apply (double* IN_ARRAY1, int DIM1) {(double* series, int length)};
+ * double prod(double* series, int length);
+ *
+ * %apply (int DIM1, int DIM2, double* INPLACE_ARRAY2)
+ * {(int rows, int cols, double* matrix )};
+ * void floor(int rows, int cols, double* matrix, double f);
+ *
+ * %apply (double IN_ARRAY3[ANY][ANY][ANY])
+ * {(double tensor[2][2][2] )};
+ * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY])
+ * {(double low[2][2][2] )};
+ * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY])
+ * {(double upp[2][2][2] )};
+ * void luSplit(double tensor[2][2][2],
+ * double low[2][2][2],
+ * double upp[2][2][2] );
+ *
+ * or directly with
+ *
+ * double prod(double* IN_ARRAY1, int DIM1);
+ *
+ * void floor(int DIM1, int DIM2, double* INPLACE_ARRAY2, double f);
+ *
+ * void luSplit(double IN_ARRAY3[ANY][ANY][ANY],
+ * double ARGOUT_ARRAY3[ANY][ANY][ANY],
+ * double ARGOUT_ARRAY3[ANY][ANY][ANY]);
+ */
+
+%define %numpy_typemaps(DATA_TYPE, DATA_TYPECODE, DIM_TYPE)
+
+/************************/
+/* Input Array Typemaps */
+/************************/
+
+/* Typemap suite for (DATA_TYPE IN_ARRAY1[ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE IN_ARRAY1[ANY])
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE IN_ARRAY1[ANY])
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[1] = { $1_dim0 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 1) ||
+ !require_size(array, size, 1)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(freearg)
+ (DATA_TYPE IN_ARRAY1[ANY])
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[1] = { -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 1) ||
+ !require_size(array, size, 1)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[1] = {-1};
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 1) ||
+ !require_size(array, size, 1)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE IN_ARRAY2[ANY][ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE IN_ARRAY2[ANY][ANY])
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE IN_ARRAY2[ANY][ANY])
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { $1_dim0, $1_dim1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(freearg)
+ (DATA_TYPE IN_ARRAY2[ANY][ANY])
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { -1, -1 };
+ array = obj_to_array_fortran_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2) || !require_fortran(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2) || !require_fortran(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { $1_dim0, $1_dim1, $1_dim2 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(freearg)
+ (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2,
+ * DIM_TYPE DIM3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { -1, -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+ $4 = (DIM_TYPE) array_size(array,2);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3,
+ * DATA_TYPE* IN_ARRAY3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { -1, -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DIM_TYPE) array_size(array,2);
+ $4 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2,
+ * DIM_TYPE DIM3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { -1, -1, -1 };
+ array = obj_to_array_fortran_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3) | !require_fortran(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+ $4 = (DIM_TYPE) array_size(array,2);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3,
+ * DATA_TYPE* IN_FARRAY3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { -1, -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3) || !require_fortran(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DIM_TYPE) array_size(array,2);
+ $4 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/***************************/
+/* In-Place Array Typemaps */
+/***************************/
+
+/* Typemap suite for (DATA_TYPE INPLACE_ARRAY1[ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE INPLACE_ARRAY1[ANY])
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE INPLACE_ARRAY1[ANY])
+ (PyArrayObject* array=NULL)
+{
+ npy_intp size[1] = { $1_dim0 };
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,1) || !require_size(array, size, 1) ||
+ !require_contiguous(array) || !require_native(array)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_ARRAY1)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_ARRAY1)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,1) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1)
+ (PyArrayObject* array=NULL, int i=1)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,1) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = 1;
+ for (i=0; i < array_numdims(array); ++i) $2 *= array_size(array,i);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1)
+ (PyArrayObject* array=NULL, int i=0)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,1) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = 1;
+ for (i=0; i < array_numdims(array); ++i) $1 *= array_size(array,i);
+ $2 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE INPLACE_ARRAY2[ANY][ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE INPLACE_ARRAY2[ANY][ANY])
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE INPLACE_ARRAY2[ANY][ANY])
+ (PyArrayObject* array=NULL)
+{
+ npy_intp size[2] = { $1_dim0, $1_dim1 };
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_size(array, size, 2) ||
+ !require_contiguous(array) || !require_native(array)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_contiguous(array) ||
+ !require_native(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_contiguous(array)
+ || !require_native(array) || !require_fortran(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_contiguous(array) ||
+ !require_native(array) || !require_fortran(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY])
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY])
+ (PyArrayObject* array=NULL)
+{
+ npy_intp size[3] = { $1_dim0, $1_dim1, $1_dim2 };
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_size(array, size, 3) ||
+ !require_contiguous(array) || !require_native(array)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2,
+ * DIM_TYPE DIM3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_contiguous(array) ||
+ !require_native(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+ $4 = (DIM_TYPE) array_size(array,2);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3,
+ * DATA_TYPE* INPLACE_ARRAY3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DIM_TYPE) array_size(array,2);
+ $4 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2,
+ * DIM_TYPE DIM3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_contiguous(array) ||
+ !require_native(array) || !require_fortran(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+ $4 = (DIM_TYPE) array_size(array,2);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3,
+ * DATA_TYPE* INPLACE_FARRAY3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_contiguous(array)
+ || !require_native(array) || !require_fortran(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DIM_TYPE) array_size(array,2);
+ $4 = (DATA_TYPE*) array_data(array);
+}
+
+/*************************/
+/* Argout Array Typemaps */
+/*************************/
+
+/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY1[ANY])
+ */
+%typemap(in,numinputs=0,
+ fragment="NumPy_Backward_Compatibility,NumPy_Macros")
+ (DATA_TYPE ARGOUT_ARRAY1[ANY])
+ (PyObject * array = NULL)
+{
+ npy_intp dims[1] = { $1_dim0 };
+ array = PyArray_SimpleNew(1, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(argout)
+ (DATA_TYPE ARGOUT_ARRAY1[ANY])
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/* Typemap suite for (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1)
+ */
+%typemap(in,numinputs=1,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1)
+ (PyObject * array = NULL)
+{
+ npy_intp dims[1];
+ if (!PyInt_Check($input))
+ {
+ const char* typestring = pytype_string($input);
+ PyErr_Format(PyExc_TypeError,
+ "Int dimension expected. '%s' given.",
+ typestring);
+ SWIG_fail;
+ }
+ $2 = (DIM_TYPE) PyInt_AsLong($input);
+ dims[0] = (npy_intp) $2;
+ array = PyArray_SimpleNew(1, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+}
+%typemap(argout)
+ (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1)
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1)
+ */
+%typemap(in,numinputs=1,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1)
+ (PyObject * array = NULL)
+{
+ npy_intp dims[1];
+ if (!PyInt_Check($input))
+ {
+ const char* typestring = pytype_string($input);
+ PyErr_Format(PyExc_TypeError,
+ "Int dimension expected. '%s' given.",
+ typestring);
+ SWIG_fail;
+ }
+ $1 = (DIM_TYPE) PyInt_AsLong($input);
+ dims[0] = (npy_intp) $1;
+ array = PyArray_SimpleNew(1, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $2 = (DATA_TYPE*) array_data(array);
+}
+%typemap(argout)
+ (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1)
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY])
+ */
+%typemap(in,numinputs=0,
+ fragment="NumPy_Backward_Compatibility,NumPy_Macros")
+ (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY])
+ (PyObject * array = NULL)
+{
+ npy_intp dims[2] = { $1_dim0, $1_dim1 };
+ array = PyArray_SimpleNew(2, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(argout)
+ (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY])
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY])
+ */
+%typemap(in,numinputs=0,
+ fragment="NumPy_Backward_Compatibility,NumPy_Macros")
+ (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY])
+ (PyObject * array = NULL)
+{
+ npy_intp dims[3] = { $1_dim0, $1_dim1, $1_dim2 };
+ array = PyArray_SimpleNew(3, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(argout)
+ (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY])
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/*****************************/
+/* Argoutview Array Typemaps */
+/*****************************/
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1 )
+ (DATA_TYPE* data_temp , DIM_TYPE dim_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1)
+{
+ npy_intp dims[1] = { *$2 };
+ PyObject * array = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$1));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1 , DATA_TYPE** ARGOUTVIEW_ARRAY1)
+ (DIM_TYPE dim_temp, DATA_TYPE* data_temp )
+{
+ $1 = &dim_temp;
+ $2 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1)
+{
+ npy_intp dims[1] = { *$1 };
+ PyObject * array = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$2));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 )
+ (DATA_TYPE* data_temp , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim1_temp;
+ $3 = &dim2_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+{
+ npy_intp dims[2] = { *$2, *$3 };
+ PyObject * array = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEW_ARRAY2)
+ (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp )
+{
+ $1 = &dim1_temp;
+ $2 = &dim2_temp;
+ $3 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2)
+{
+ npy_intp dims[2] = { *$1, *$2 };
+ PyObject * array = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 )
+ (DATA_TYPE* data_temp , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim1_temp;
+ $3 = &dim2_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements")
+ (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+{
+ npy_intp dims[2] = { *$2, *$3 };
+ PyObject * obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1));
+ PyArrayObject * array = (PyArrayObject*) obj;
+ if (!array || !require_fortran(array)) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,obj);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEW_FARRAY2)
+ (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp )
+{
+ $1 = &dim1_temp;
+ $2 = &dim2_temp;
+ $3 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements")
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2)
+{
+ npy_intp dims[2] = { *$1, *$2 };
+ PyObject * obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3));
+ PyArrayObject * array = (PyArrayObject*) obj;
+ if (!array || !require_fortran(array)) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,obj);
+}
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2,
+ DIM_TYPE* DIM3)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+ (DATA_TYPE* data_temp, DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim1_temp;
+ $3 = &dim2_temp;
+ $4 = &dim3_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+{
+ npy_intp dims[3] = { *$2, *$3, *$4 };
+ PyObject * array = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3,
+ DATA_TYPE** ARGOUTVIEW_ARRAY3)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3)
+ (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp)
+{
+ $1 = &dim1_temp;
+ $2 = &dim2_temp;
+ $3 = &dim3_temp;
+ $4 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3)
+{
+ npy_intp dims[3] = { *$1, *$2, *$3 };
+ PyObject * array = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$3));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2,
+ DIM_TYPE* DIM3)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+ (DATA_TYPE* data_temp, DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim1_temp;
+ $3 = &dim2_temp;
+ $4 = &dim3_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements")
+ (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+{
+ npy_intp dims[3] = { *$2, *$3, *$4 };
+ PyObject * obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1));
+ PyArrayObject * array = (PyArrayObject*) obj;
+ if (!array || require_fortran(array)) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,obj);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3,
+ DATA_TYPE** ARGOUTVIEW_FARRAY3)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3)
+ (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp)
+{
+ $1 = &dim1_temp;
+ $2 = &dim2_temp;
+ $3 = &dim3_temp;
+ $4 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements")
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3)
+{
+ npy_intp dims[3] = { *$1, *$2, *$3 };
+ PyObject * obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$3));
+ PyArrayObject * array = (PyArrayObject*) obj;
+ if (!array || require_fortran(array)) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,obj);
+}
+
+%enddef /* %numpy_typemaps() macro */
+/* *************************************************************** */
+
+/* Concrete instances of the %numpy_typemaps() macro: Each invocation
+ * below applies all of the typemaps above to the specified data type.
+ */
+%numpy_typemaps(signed char , NPY_BYTE , int)
+%numpy_typemaps(unsigned char , NPY_UBYTE , int)
+%numpy_typemaps(short , NPY_SHORT , int)
+%numpy_typemaps(unsigned short , NPY_USHORT , int)
+%numpy_typemaps(int , NPY_INT , int)
+%numpy_typemaps(unsigned int , NPY_UINT , int)
+%numpy_typemaps(long , NPY_LONG , int)
+%numpy_typemaps(unsigned long , NPY_ULONG , int)
+%numpy_typemaps(long long , NPY_LONGLONG , int)
+%numpy_typemaps(unsigned long long, NPY_ULONGLONG, int)
+%numpy_typemaps(float , NPY_FLOAT , int)
+%numpy_typemaps(double , NPY_DOUBLE , int)
+
+/* ***************************************************************
+ * The follow macro expansion does not work, because C++ bool is 4
+ * bytes and NPY_BOOL is 1 byte
+ *
+ * %numpy_typemaps(bool, NPY_BOOL, int)
+ */
+
+/* ***************************************************************
+ * On my Mac, I get the following warning for this macro expansion:
+ * 'swig/python detected a memory leak of type 'long double *', no destructor found.'
+ *
+ * %numpy_typemaps(long double, NPY_LONGDOUBLE, int)
+ */
+
+/* ***************************************************************
+ * Swig complains about a syntax error for the following macro
+ * expansions:
+ *
+ * %numpy_typemaps(complex float, NPY_CFLOAT , int)
+ *
+ * %numpy_typemaps(complex double, NPY_CDOUBLE, int)
+ *
+ * %numpy_typemaps(complex long double, NPY_CLONGDOUBLE, int)
+ */
+
+#endif /* SWIGPYTHON */
diff --git a/bindings/python/Makefile b/bindings/python/Makefile
new file mode 100644
index 0000000..e63c124
--- /dev/null
+++ b/bindings/python/Makefile
@@ -0,0 +1,186 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+modules.rst lilv.rst:
+ mkdir -p lilv
+ ln -s -t lilv ../lilv.py
+ sphinx-apidoc -o . lilv
+
+clean:
+ rm -rf $(BUILDDIR)/*
+ rm -f lilv/lilv.py
+ rm -rf lilv
+ rm -f lilv.rst
+ rm -f modules.rst
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml: modules.rst lilv.rst
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Lilv.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Lilv.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Lilv"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Lilv"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/bindings/python/conf.py b/bindings/python/conf.py
new file mode 100644
index 0000000..576919e
--- /dev/null
+++ b/bindings/python/conf.py
@@ -0,0 +1,263 @@
+# -*- coding: utf-8 -*-
+#
+# Lilv documentation build configuration file, created by
+# sphinx-quickstart on Sun Sep 4 18:25:58 2016.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.ifconfig',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Lilv'
+copyright = u'2016, David Robillard'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.24.2'
+# The full version, including alpha/beta/rc tags.
+release = '0.24.2'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#html_theme = ''
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+html_theme_options = { 'nosidebar': True }
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Lilvdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'Lilv.tex', u'Lilv Documentation',
+ u'David Robillard', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'lilv', u'Lilv Documentation',
+ [u'David Robillard'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'Lilv', u'Lilv Documentation',
+ u'David Robillard', 'Lilv', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/bindings/python/index.rst b/bindings/python/index.rst
new file mode 100644
index 0000000..4616054
--- /dev/null
+++ b/bindings/python/index.rst
@@ -0,0 +1,9 @@
+Lilv Python Documentation
+=========================
+
+
+.. toctree::
+
+.. automodule:: lilv
+ :noindex:
+ :members:
diff --git a/bindings/python/lilv.py b/bindings/python/lilv.py
new file mode 100644
index 0000000..024bfe7
--- /dev/null
+++ b/bindings/python/lilv.py
@@ -0,0 +1,1775 @@
+"""Lilv Python interface"""
+
+__author__ = "David Robillard"
+__copyright__ = "Copyright 2016 David Robillard"
+__license__ = "ISC"
+__version__ = "0.22.1"
+__maintainer__ = "David Robillard"
+__email__ = "d@drobilla.net"
+__status__ = "Production"
+
+import ctypes
+import os
+import sys
+
+from ctypes import Structure, CDLL, POINTER, CFUNCTYPE
+from ctypes import c_bool, c_double, c_float, c_int, c_size_t, c_uint, c_uint32
+from ctypes import c_char, c_char_p, c_void_p
+from ctypes import byref
+
+# Load lilv library
+
+_lib = CDLL("liblilv-0.so")
+
+# Set namespaced aliases for all lilv functions
+
+class String(str):
+ # Wrapper for string parameters to pass as raw C UTF-8 strings
+ def from_param(cls, obj):
+ return obj.encode('utf-8')
+
+ from_param = classmethod(from_param)
+
+def _as_uri(obj):
+ if type(obj) in [Plugin, PluginClass, UI]:
+ return obj.get_uri()
+ else:
+ return obj
+
+free = _lib.lilv_free
+# uri_to_path = _lib.lilv_uri_to_path
+file_uri_parse = _lib.lilv_file_uri_parse
+new_uri = _lib.lilv_new_uri
+new_file_uri = _lib.lilv_new_file_uri
+new_string = _lib.lilv_new_string
+new_int = _lib.lilv_new_int
+new_float = _lib.lilv_new_float
+new_bool = _lib.lilv_new_bool
+node_free = _lib.lilv_node_free
+node_duplicate = _lib.lilv_node_duplicate
+node_equals = _lib.lilv_node_equals
+node_get_turtle_token = _lib.lilv_node_get_turtle_token
+node_is_uri = _lib.lilv_node_is_uri
+node_as_uri = _lib.lilv_node_as_uri
+node_is_blank = _lib.lilv_node_is_blank
+node_as_blank = _lib.lilv_node_as_blank
+node_is_literal = _lib.lilv_node_is_literal
+node_is_string = _lib.lilv_node_is_string
+node_as_string = _lib.lilv_node_as_string
+node_get_path = _lib.lilv_node_get_path
+node_is_float = _lib.lilv_node_is_float
+node_as_float = _lib.lilv_node_as_float
+node_is_int = _lib.lilv_node_is_int
+node_as_int = _lib.lilv_node_as_int
+node_is_bool = _lib.lilv_node_is_bool
+node_as_bool = _lib.lilv_node_as_bool
+plugin_classes_free = _lib.lilv_plugin_classes_free
+plugin_classes_size = _lib.lilv_plugin_classes_size
+plugin_classes_begin = _lib.lilv_plugin_classes_begin
+plugin_classes_get = _lib.lilv_plugin_classes_get
+plugin_classes_next = _lib.lilv_plugin_classes_next
+plugin_classes_is_end = _lib.lilv_plugin_classes_is_end
+plugin_classes_get_by_uri = _lib.lilv_plugin_classes_get_by_uri
+scale_points_free = _lib.lilv_scale_points_free
+scale_points_size = _lib.lilv_scale_points_size
+scale_points_begin = _lib.lilv_scale_points_begin
+scale_points_get = _lib.lilv_scale_points_get
+scale_points_next = _lib.lilv_scale_points_next
+scale_points_is_end = _lib.lilv_scale_points_is_end
+uis_free = _lib.lilv_uis_free
+uis_size = _lib.lilv_uis_size
+uis_begin = _lib.lilv_uis_begin
+uis_get = _lib.lilv_uis_get
+uis_next = _lib.lilv_uis_next
+uis_is_end = _lib.lilv_uis_is_end
+uis_get_by_uri = _lib.lilv_uis_get_by_uri
+nodes_free = _lib.lilv_nodes_free
+nodes_size = _lib.lilv_nodes_size
+nodes_begin = _lib.lilv_nodes_begin
+nodes_get = _lib.lilv_nodes_get
+nodes_next = _lib.lilv_nodes_next
+nodes_is_end = _lib.lilv_nodes_is_end
+nodes_get_first = _lib.lilv_nodes_get_first
+nodes_contains = _lib.lilv_nodes_contains
+nodes_merge = _lib.lilv_nodes_merge
+plugins_size = _lib.lilv_plugins_size
+plugins_begin = _lib.lilv_plugins_begin
+plugins_get = _lib.lilv_plugins_get
+plugins_next = _lib.lilv_plugins_next
+plugins_is_end = _lib.lilv_plugins_is_end
+plugins_get_by_uri = _lib.lilv_plugins_get_by_uri
+world_new = _lib.lilv_world_new
+world_set_option = _lib.lilv_world_set_option
+world_free = _lib.lilv_world_free
+world_load_all = _lib.lilv_world_load_all
+world_load_bundle = _lib.lilv_world_load_bundle
+world_load_specifications = _lib.lilv_world_load_specifications
+world_load_plugin_classes = _lib.lilv_world_load_plugin_classes
+world_unload_bundle = _lib.lilv_world_unload_bundle
+world_load_resource = _lib.lilv_world_load_resource
+world_unload_resource = _lib.lilv_world_unload_resource
+world_get_plugin_class = _lib.lilv_world_get_plugin_class
+world_get_plugin_classes = _lib.lilv_world_get_plugin_classes
+world_get_all_plugins = _lib.lilv_world_get_all_plugins
+world_find_nodes = _lib.lilv_world_find_nodes
+world_get = _lib.lilv_world_get
+world_ask = _lib.lilv_world_ask
+plugin_verify = _lib.lilv_plugin_verify
+plugin_get_uri = _lib.lilv_plugin_get_uri
+plugin_get_bundle_uri = _lib.lilv_plugin_get_bundle_uri
+plugin_get_data_uris = _lib.lilv_plugin_get_data_uris
+plugin_get_library_uri = _lib.lilv_plugin_get_library_uri
+plugin_get_name = _lib.lilv_plugin_get_name
+plugin_get_class = _lib.lilv_plugin_get_class
+plugin_get_value = _lib.lilv_plugin_get_value
+plugin_has_feature = _lib.lilv_plugin_has_feature
+plugin_get_supported_features = _lib.lilv_plugin_get_supported_features
+plugin_get_required_features = _lib.lilv_plugin_get_required_features
+plugin_get_optional_features = _lib.lilv_plugin_get_optional_features
+plugin_has_extension_data = _lib.lilv_plugin_has_extension_data
+plugin_get_extension_data = _lib.lilv_plugin_get_extension_data
+plugin_get_num_ports = _lib.lilv_plugin_get_num_ports
+plugin_get_port_ranges_float = _lib.lilv_plugin_get_port_ranges_float
+plugin_has_latency = _lib.lilv_plugin_has_latency
+plugin_get_latency_port_index = _lib.lilv_plugin_get_latency_port_index
+plugin_get_port_by_index = _lib.lilv_plugin_get_port_by_index
+plugin_get_port_by_symbol = _lib.lilv_plugin_get_port_by_symbol
+plugin_get_port_by_designation = _lib.lilv_plugin_get_port_by_designation
+plugin_get_project = _lib.lilv_plugin_get_project
+plugin_get_author_name = _lib.lilv_plugin_get_author_name
+plugin_get_author_email = _lib.lilv_plugin_get_author_email
+plugin_get_author_homepage = _lib.lilv_plugin_get_author_homepage
+plugin_is_replaced = _lib.lilv_plugin_is_replaced
+plugin_get_related = _lib.lilv_plugin_get_related
+port_get_node = _lib.lilv_port_get_node
+port_get_value = _lib.lilv_port_get_value
+port_get = _lib.lilv_port_get
+port_get_properties = _lib.lilv_port_get_properties
+port_has_property = _lib.lilv_port_has_property
+port_supports_event = _lib.lilv_port_supports_event
+port_get_index = _lib.lilv_port_get_index
+port_get_symbol = _lib.lilv_port_get_symbol
+port_get_name = _lib.lilv_port_get_name
+port_get_classes = _lib.lilv_port_get_classes
+port_is_a = _lib.lilv_port_is_a
+port_get_range = _lib.lilv_port_get_range
+port_get_scale_points = _lib.lilv_port_get_scale_points
+state_new_from_world = _lib.lilv_state_new_from_world
+state_new_from_file = _lib.lilv_state_new_from_file
+state_new_from_string = _lib.lilv_state_new_from_string
+state_new_from_instance = _lib.lilv_state_new_from_instance
+state_free = _lib.lilv_state_free
+state_equals = _lib.lilv_state_equals
+state_get_num_properties = _lib.lilv_state_get_num_properties
+state_get_plugin_uri = _lib.lilv_state_get_plugin_uri
+state_get_uri = _lib.lilv_state_get_uri
+state_get_label = _lib.lilv_state_get_label
+state_set_label = _lib.lilv_state_set_label
+state_set_metadata = _lib.lilv_state_set_metadata
+state_emit_port_values = _lib.lilv_state_emit_port_values
+state_restore = _lib.lilv_state_restore
+state_save = _lib.lilv_state_save
+state_to_string = _lib.lilv_state_to_string
+state_delete = _lib.lilv_state_delete
+scale_point_get_label = _lib.lilv_scale_point_get_label
+scale_point_get_value = _lib.lilv_scale_point_get_value
+plugin_class_get_parent_uri = _lib.lilv_plugin_class_get_parent_uri
+plugin_class_get_uri = _lib.lilv_plugin_class_get_uri
+plugin_class_get_label = _lib.lilv_plugin_class_get_label
+plugin_class_get_children = _lib.lilv_plugin_class_get_children
+plugin_instantiate = _lib.lilv_plugin_instantiate
+instance_free = _lib.lilv_instance_free
+plugin_get_uis = _lib.lilv_plugin_get_uis
+ui_get_uri = _lib.lilv_ui_get_uri
+ui_get_classes = _lib.lilv_ui_get_classes
+ui_is_a = _lib.lilv_ui_is_a
+ui_is_supported = _lib.lilv_ui_is_supported
+ui_get_bundle_uri = _lib.lilv_ui_get_bundle_uri
+ui_get_binary_uri = _lib.lilv_ui_get_binary_uri
+
+## LV2 types
+
+LV2_Handle = POINTER(None)
+LV2_URID_Map_Handle = POINTER(None)
+LV2_URID_Unmap_Handle = POINTER(None)
+LV2_URID = c_uint32
+
+class LV2_Feature(Structure):
+ __slots__ = [ 'URI', 'data' ]
+ _fields_ = [('URI', c_char_p),
+ ('data', POINTER(None))]
+
+class LV2_Descriptor(Structure):
+ __slots__ = [ 'URI',
+ 'instantiate',
+ 'connect_port',
+ 'activate',
+ 'run',
+ 'deactivate',
+ 'cleanup',
+ 'extension_data' ]
+
+LV2_Descriptor._fields_ = [
+ ('URI', c_char_p),
+ ('instantiate', CFUNCTYPE(LV2_Handle, POINTER(LV2_Descriptor),
+ c_double, c_char_p, POINTER(POINTER(LV2_Feature)))),
+ ('connect_port', CFUNCTYPE(None, LV2_Handle, c_uint32, POINTER(None))),
+ ('activate', CFUNCTYPE(None, LV2_Handle)),
+ ('run', CFUNCTYPE(None, LV2_Handle, c_uint32)),
+ ('deactivate', CFUNCTYPE(None, LV2_Handle)),
+ ('cleanup', CFUNCTYPE(None, LV2_Handle)),
+ ('extension_data', CFUNCTYPE(c_void_p, c_char_p)),
+]
+
+class LV2_URID_Map(Structure):
+ __slots__ = [ 'handle', 'map' ]
+ _fields_ = [
+ ('handle', LV2_URID_Map_Handle),
+ ('map', CFUNCTYPE(LV2_URID, LV2_URID_Map_Handle, c_char_p)),
+ ]
+
+class LV2_URID_Unmap(Structure):
+ __slots__ = [ 'handle', 'unmap' ]
+ _fields_ = [
+ ('handle', LV2_URID_Unmap_Handle),
+ ('unmap', CFUNCTYPE(c_char_p, LV2_URID_Unmap_Handle, LV2_URID)),
+ ]
+
+# Lilv types
+
+class Plugin(Structure):
+ """LV2 Plugin."""
+ def __init__(self, world, plugin):
+ self.world = world
+ self.plugin = plugin
+
+ def __eq__(self, other):
+ return self.get_uri() == other.get_uri()
+
+ def verify(self):
+ """Check if `plugin` is valid.
+
+ This is not a rigorous validator, but can be used to reject some malformed
+ plugins that could cause bugs (e.g. plugins with missing required fields).
+
+ Note that normal hosts do NOT need to use this - lilv does not
+ load invalid plugins into plugin lists. This is included for plugin
+ testing utilities, etc.
+ """
+ return plugin_verify(self.plugin)
+
+ def get_uri(self):
+ """Get the URI of `plugin`.
+
+ Any serialization that refers to plugins should refer to them by this.
+ Hosts SHOULD NOT save any filesystem paths, plugin indexes, etc. in saved
+ files pass save only the URI.
+
+ The URI is a globally unique identifier for one specific plugin. Two
+ plugins with the same URI are compatible in port signature, and should
+ be guaranteed to work in a compatible and consistent way. If a plugin
+ is upgraded in an incompatible way (eg if it has different ports), it
+ MUST have a different URI than it's predecessor.
+ """
+ return Node.wrap(node_duplicate(plugin_get_uri(self.plugin)))
+
+ def get_bundle_uri(self):
+ """Get the (resolvable) URI of the plugin's "main" bundle.
+
+ This returns the URI of the bundle where the plugin itself was found. Note
+ that the data for a plugin may be spread over many bundles, that is,
+ get_data_uris() may return URIs which are not within this bundle.
+
+ Typical hosts should not need to use this function.
+ Note this always returns a fully qualified URI. If you want a local
+ filesystem path, use lilv.file_uri_parse().
+ """
+ return Node.wrap(node_duplicate(plugin_get_bundle_uri(self.plugin)))
+
+ def get_data_uris(self):
+ """Get the (resolvable) URIs of the RDF data files that define a plugin.
+
+ Typical hosts should not need to use this function.
+ Note this always returns fully qualified URIs. If you want local
+ filesystem paths, use lilv.file_uri_parse().
+ """
+ return Nodes(plugin_get_data_uris(self.plugin))
+
+ def get_library_uri(self):
+ """Get the (resolvable) URI of the shared library for `plugin`.
+
+ Note this always returns a fully qualified URI. If you want a local
+ filesystem path, use lilv.file_uri_parse().
+ """
+ return Node.wrap(node_duplicate(plugin_get_library_uri(self.plugin)))
+
+ def get_name(self):
+ """Get the name of `plugin`.
+
+ This returns the name (doap:name) of the plugin. The name may be
+ translated according to the current locale, this value MUST NOT be used
+ as a plugin identifier (use the URI for that).
+ """
+ return Node.wrap(plugin_get_name(self.plugin))
+
+ def get_class(self):
+ """Get the class this plugin belongs to (e.g. Filters)."""
+ return PluginClass(plugin_get_class(self.plugin))
+
+ def get_value(self, predicate):
+ """Get a value associated with the plugin in a plugin's data files.
+
+ `predicate` must be either a URI or a QName.
+
+ Returns the ?object of all triples found of the form:
+
+ plugin-uri predicate ?object
+
+ May return None if the property was not found, or if object(s) is not
+ sensibly represented as a LilvNodes (e.g. blank nodes).
+ """
+ return Nodes(plugin_get_value(self.plugin, predicate.node))
+
+ def has_feature(self, feature_uri):
+ """Return whether a feature is supported by a plugin.
+
+ This will return true if the feature is an optional or required feature
+ of the plugin.
+ """
+ return plugin_has_feature(self.plugin, feature_uri.node)
+
+ def get_supported_features(self):
+ """Get the LV2 Features supported (required or optionally) by a plugin.
+
+ A feature is "supported" by a plugin if it is required OR optional.
+
+ Since required features have special rules the host must obey, this function
+ probably shouldn't be used by normal hosts. Using get_optional_features()
+ and get_required_features() separately is best in most cases.
+ """
+ return Nodes(plugin_get_supported_features(self.plugin))
+
+ def get_required_features(self):
+ """Get the LV2 Features required by a plugin.
+
+ If a feature is required by a plugin, hosts MUST NOT use the plugin if they do not
+ understand (or are unable to support) that feature.
+
+ All values returned here MUST be return plugin_(self.plugin)ed to the plugin's instantiate method
+ (along with data, if necessary, as defined by the feature specification)
+ or plugin instantiation will fail.
+ """
+ return Nodes(plugin_get_required_features(self.plugin))
+
+ def get_optional_features(self):
+ """Get the LV2 Features optionally supported by a plugin.
+
+ Hosts MAY ignore optional plugin features for whatever reasons. Plugins
+ MUST operate (at least somewhat) if they are instantiated without being
+ passed optional features.
+ """
+ return Nodes(plugin_get_optional_features(self.plugin))
+
+ def has_extension_data(self, uri):
+ """Return whether or not a plugin provides a specific extension data."""
+ return plugin_has_extension_data(self.plugin, uri.node)
+
+ def get_extension_data(self):
+ """Get a sequence of all extension data provided by a plugin.
+
+ This can be used to find which URIs get_extension_data()
+ will return a value for without instantiating the plugin.
+ """
+ return Nodes(plugin_get_extension_data(self.plugin))
+
+ def get_num_ports(self):
+ """Get the number of ports on this plugin."""
+ return plugin_get_num_ports(self.plugin)
+
+ # def get_port_ranges_float(self, min_values, max_values, def_values):
+ # """Get the port ranges (minimum, maximum and default values) for all ports.
+
+ # `min_values`, `max_values` and `def_values` must either point to an array
+ # of N floats, where N is the value returned by get_num_ports()
+ # for this plugin, or None. The elements of the array will be set to the
+ # the minimum, maximum and default values of the ports on this plugin,
+ # with array index corresponding to port index. If a port doesn't have a
+ # minimum, maximum or default value, or the port's type is not float, the
+ # corresponding array element will be set to NAN.
+
+ # This is a convenience method for the common case of getting the range of
+ # all float ports on a plugin, and may be significantly faster than
+ # repeated calls to Port.get_range().
+ # """
+ # plugin_get_port_ranges_float(self.plugin, min_values, max_values, def_values)
+
+ def get_num_ports_of_class(self, *args):
+ """Get the number of ports on this plugin that are members of some class(es)."""
+ args = list(map(lambda x: x.node, args))
+ args += (None,)
+ return plugin_get_num_ports_of_class(self.plugin, *args)
+
+ def has_latency(self):
+ """Return whether or not the plugin introduces (and reports) latency.
+
+ The index of the latency port can be found with
+ get_latency_port() ONLY if this function returns true.
+ """
+ return plugin_has_latency(self.plugin)
+
+ def get_latency_port_index(self):
+ """Return the index of the plugin's latency port.
+
+ Returns None if the plugin has no latency port.
+
+ Any plugin that introduces unwanted latency that should be compensated for
+ (by hosts with the ability/need) MUST provide this port, which is a control
+ rate output port that reports the latency for each cycle in frames.
+ """
+ return plugin_get_latency_port_index(self.plugin) if self.has_latency() else None
+
+ def get_port(self, key):
+ """Get a port on `plugin` by index or symbol."""
+ if type(key) == int:
+ return self.get_port_by_index(key)
+ else:
+ return self.get_port_by_symbol(key)
+
+ def get_port_by_index(self, index):
+ """Get a port on `plugin` by `index`."""
+ return Port.wrap(self, plugin_get_port_by_index(self.plugin, index))
+
+ def get_port_by_symbol(self, symbol):
+ """Get a port on `plugin` by `symbol`.
+
+ Note this function is slower than get_port_by_index(),
+ especially on plugins with a very large number of ports.
+ """
+ if type(symbol) == str:
+ symbol = self.world.new_string(symbol)
+ return Port.wrap(self, plugin_get_port_by_symbol(self.plugin, symbol.node))
+
+ def get_port_by_designation(self, port_class, designation):
+ """Get a port on `plugin` by its lv2:designation.
+
+ The designation of a port describes the meaning, assignment, allocation or
+ role of the port, e.g. "left channel" or "gain". If found, the port with
+ matching `port_class` and `designation` is be returned, otherwise None is
+ returned. The `port_class` can be used to distinguish the input and output
+ ports for a particular designation. If `port_class` is None, any port with
+ the given designation will be returned.
+ """
+ return Port.wrap(self,
+ plugin_get_port_by_designation(self.plugin,
+ port_class.node,
+ designation.node))
+
+ def get_project(self):
+ """Get the project the plugin is a part of.
+
+ More information about the project can be read via find_nodes(),
+ typically using properties from DOAP (e.g. doap:name).
+ """
+ return Node.wrap(plugin_get_project(self.plugin))
+
+ def get_author_name(self):
+ """Get the full name of the plugin's author.
+
+ Returns None if author name is not present.
+ """
+ return Node.wrap(plugin_get_author_name(self.plugin))
+
+ def get_author_email(self):
+ """Get the email address of the plugin's author.
+
+ Returns None if author email address is not present.
+ """
+ return Node.wrap(plugin_get_author_email(self.plugin))
+
+ def get_author_homepage(self):
+ """Get the address of the plugin author's home page.
+
+ Returns None if author homepage is not present.
+ """
+ return Node.wrap(plugin_get_author_homepage(self.plugin))
+
+ def is_replaced(self):
+ """Return true iff `plugin` has been replaced by another plugin.
+
+ The plugin will still be usable, but hosts should hide them from their
+ user interfaces to prevent users from using deprecated plugins.
+ """
+ return plugin_is_replaced(self.plugin)
+
+ def get_related(self, resource_type):
+ """Get the resources related to `plugin` with lv2:appliesTo.
+
+ Some plugin-related resources are not linked directly to the plugin with
+ rdfs:seeAlso and thus will not be automatically loaded along with the plugin
+ data (usually for performance reasons). All such resources of the given @c
+ type related to `plugin` can be accessed with this function.
+
+ If `resource_type` is None, all such resources will be returned, regardless of type.
+
+ To actually load the data for each returned resource, use world.load_resource().
+ """
+ return Nodes(plugin_get_related(self.plugin, resource_type))
+
+ def get_uis(self):
+ """Get all UIs for `plugin`."""
+ return UIs(plugin_get_uis(self.plugin))
+
+class PluginClass(Structure):
+ """Plugin Class (type/category)."""
+ def __init__(self, plugin_class):
+ self.plugin_class = plugin_class
+
+ def __str__(self):
+ return self.get_uri().__str__()
+
+ def get_parent_uri(self):
+ """Get the URI of this class' superclass.
+
+ May return None if class has no parent.
+ """
+ return Node.wrap(node_duplicate(plugin_class_get_parent_uri(self.plugin_class)))
+
+ def get_uri(self):
+ """Get the URI of this plugin class."""
+ return Node.wrap(node_duplicate(plugin_class_get_uri(self.plugin_class)))
+
+ def get_label(self):
+ """Get the label of this plugin class, ie "Oscillators"."""
+ return Node.wrap(node_duplicate(plugin_class_get_label(self.plugin_class)))
+
+ def get_children(self):
+ """Get the subclasses of this plugin class."""
+ return PluginClasses(plugin_class_get_children(self.plugin_class))
+
+class Port(Structure):
+ """Port on a Plugin."""
+ @classmethod
+ def wrap(cls, plugin, port):
+ return Port(plugin, port) if plugin and port else None
+
+ def __init__(self, plugin, port):
+ self.plugin = plugin
+ self.port = port
+
+ def get_node(self):
+ """Get the RDF node of `port`.
+
+ Ports nodes may be may be URIs or blank nodes.
+ """
+ return Node.wrap(node_duplicate(port_get_node(self.plugin, self.port)))
+
+ def get_value(self, predicate):
+ """Port analog of Plugin.get_value()."""
+ return Nodes(port_get_value(self.plugin.plugin, self.port, predicate.node))
+
+ def get(self, predicate):
+ """Get a single property value of a port.
+
+ This is equivalent to lilv_nodes_get_first(lilv_port_get_value(...)) but is
+ simpler to use in the common case of only caring about one value. The
+ caller is responsible for freeing the returned node.
+ """
+ return Node.wrap(port_get(self.plugin.plugin, self.port, predicate.node))
+
+ def get_properties(self):
+ """Return the LV2 port properties of a port."""
+ return Nodes(port_get_properties(self.plugin.plugin, self.port))
+
+ def has_property(self, property_uri):
+ """Return whether a port has a certain property."""
+ return port_has_property(self.plugin.plugin, self.port, property_uri.node)
+
+ def supports_event(self, event_type):
+ """Return whether a port supports a certain event type.
+
+ More precisely, this returns true iff the port has an atom:supports or an
+ ev:supportsEvent property with `event_type` as the value.
+ """
+ return port_supports_event(self.plugin.plugin, self.port, event_type.node)
+
+ def get_index(self):
+ """Get the index of a port.
+
+ The index is only valid for the life of the plugin and may change between
+ versions. For a stable identifier, use the symbol.
+ """
+ return port_get_index(self.plugin.plugin, self.port)
+
+ def get_symbol(self):
+ """Get the symbol of a port.
+
+ The 'symbol' is a short string, a valid C identifier.
+ """
+ return Node.wrap(node_duplicate(port_get_symbol(self.plugin.plugin, self.port)))
+
+ def get_name(self):
+ """Get the name of a port.
+
+ This is guaranteed to return the untranslated name (the doap:name in the
+ data file without a language tag).
+ """
+ return Node.wrap(port_get_name(self.plugin.plugin, self.port))
+
+ def get_classes(self):
+ """Get all the classes of a port.
+
+ This can be used to determine if a port is an input, output, audio,
+ control, midi, etc, etc, though it's simpler to use is_a().
+ The returned list does not include lv2:Port, which is implied.
+ Returned value is shared and must not be destroyed by caller.
+ """
+ return Nodes(port_get_classes(self.plugin.plugin, self.port))
+
+ def is_a(self, port_class):
+ """Determine if a port is of a given class (input, output, audio, etc).
+
+ For convenience/performance/extensibility reasons, hosts are expected to
+ create a LilvNode for each port class they "care about". Well-known type
+ URI strings are defined (e.g. LILV_URI_INPUT_PORT) for convenience, but
+ this function is designed so that Lilv is usable with any port types
+ without requiring explicit support in Lilv.
+ """
+ return port_is_a(self.plugin.plugin, self.port, port_class.node)
+
+ def get_range(self):
+ """Return the default, minimum, and maximum values of a port as a tuple."""
+ pdef = POINTER(Node)()
+ pmin = POINTER(Node)()
+ pmax = POINTER(Node)()
+ port_get_range(self.plugin.plugin, self.port, byref(pdef), byref(pmin), byref(pmax))
+ return (Node(pdef.contents) if pdef else None,
+ Node(pmin.contents) if pmin else None,
+ Node(pmax.contents) if pmax else None)
+
+ def get_scale_points(self):
+ """Get the scale points (enumeration values) of a port.
+
+ This returns a collection of 'interesting' named values of a port
+ (e.g. appropriate entries for a UI selector associated with this port).
+ Returned value may be None if `port` has no scale points.
+ """
+ return ScalePoints(port_get_scale_points(self.plugin.plugin, self.port))
+
+class ScalePoint(Structure):
+ """Scale point (detent)."""
+ def __init__(self, point):
+ self.point = point
+
+ def get_label(self):
+ """Get the label of this scale point (enumeration value)."""
+ return Node.wrap(scale_point_get_label(self.point))
+
+ def get_value(self):
+ """Get the value of this scale point (enumeration value)."""
+ return Node.wrap(scale_point_get_value(self.point))
+
+class UI(Structure):
+ """Plugin UI."""
+ def __init__(self, ui):
+ self.ui = ui
+
+ def __str__(self):
+ return str(self.get_uri())
+
+ def __eq__(self, other):
+ return self.get_uri() == _as_uri(other)
+
+ def get_uri(self):
+ """Get the URI of a Plugin UI."""
+ return Node.wrap(node_duplicate(ui_get_uri(self.ui)))
+
+ def get_classes(self):
+ """Get the types (URIs of RDF classes) of a Plugin UI.
+
+ Note that in most cases is_supported() should be used, which avoids
+ the need to use this function (and type specific logic).
+ """
+ return Nodes(ui_get_classes(self.ui))
+
+ def is_a(self, class_uri):
+ """Check whether a plugin UI has a given type."""
+ return ui_is_a(self.ui, class_uri.node)
+
+ def get_bundle_uri(self):
+ """Get the URI of the UI's bundle."""
+ return Node.wrap(node_duplicate(ui_get_bundle_uri(self.ui)))
+
+ def get_binary_uri(self):
+ """Get the URI for the UI's shared library."""
+ return Node.wrap(node_duplicate(ui_get_binary_uri(self.ui)))
+
+class Node(Structure):
+ """Data node (URI, string, integer, etc.).
+
+ A Node can be converted to the corresponding Python datatype, and all nodes
+ can be converted to strings, for example::
+
+ >>> world = lilv.World()
+ >>> i = world.new_int(42)
+ >>> print(i)
+ 42
+ >>> int(i) * 2
+ 84
+ """
+ @classmethod
+ def wrap(cls, node):
+ return Node(node) if node else None
+
+ def __init__(self, node):
+ self.node = node
+
+ def __del__(self):
+ if hasattr(self, 'node'):
+ node_free(self.node)
+
+ def __eq__(self, other):
+ otype = type(other)
+ if otype in [str, int, float]:
+ return otype(self) == other
+ return node_equals(self.node, other.node)
+
+ def __ne__(self, other):
+ return not node_equals(self.node, other.node)
+
+ def __str__(self):
+ return node_as_string(self.node).decode('utf-8')
+
+ def __int__(self):
+ if not self.is_int():
+ raise ValueError('node %s is not an integer' % str(self))
+ return node_as_int(self.node)
+
+ def __float__(self):
+ if not self.is_float():
+ raise ValueError('node %s is not a float' % str(self))
+ return node_as_float(self.node)
+
+ def __bool__(self):
+ if not self.is_bool():
+ raise ValueError('node %s is not a bool' % str(self))
+ return node_as_bool(self.node)
+ __nonzero__ = __bool__
+
+ def get_turtle_token(self):
+ """Return this value as a Turtle/SPARQL token."""
+ return node_get_turtle_token(self.node).decode('utf-8')
+
+ def is_uri(self):
+ """Return whether the value is a URI (resource)."""
+ return node_is_uri(self.node)
+
+ def is_blank(self):
+ """Return whether the value is a blank node (resource with no URI)."""
+ return node_is_blank(self.node)
+
+ def is_literal(self):
+ """Return whether this value is a literal (i.e. not a URI)."""
+ return node_is_literal(self.node)
+
+ def is_string(self):
+ """Return whether this value is a string literal.
+
+ Returns true if value is a string value (and not numeric).
+ """
+ return node_is_string(self.node)
+
+ def get_path(self, hostname=None):
+ """Return the path of a file URI node.
+
+ Returns None if value is not a file URI."""
+ return node_get_path(self.node, hostname).decode('utf-8')
+
+ def is_float(self):
+ """Return whether this value is a decimal literal."""
+ return node_is_float(self.node)
+
+ def is_int(self):
+ """Return whether this value is an integer literal."""
+ return node_is_int(self.node)
+
+ def is_bool(self):
+ """Return whether this value is a boolean."""
+ return node_is_bool(self.node)
+
+class Iter(Structure):
+ """Collection iterator."""
+ def __init__(self, collection, iterator, constructor, iter_get, iter_next, iter_is_end):
+ self.collection = collection
+ self.iterator = iterator
+ self.constructor = constructor
+ self.iter_get = iter_get
+ self.iter_next = iter_next
+ self.iter_is_end = iter_is_end
+
+ def get(self):
+ """Get the current item."""
+ return self.constructor(self.iter_get(self.collection, self.iterator))
+
+ def next(self):
+ """Move to and return the next item."""
+ if self.is_end():
+ raise StopIteration
+ elem = self.get()
+ self.iterator = self.iter_next(self.collection, self.iterator)
+ return elem
+
+ def is_end(self):
+ """Return true if the end of the collection has been reached."""
+ return self.iter_is_end(self.collection, self.iterator)
+
+ __next__ = next
+
+class Collection(Structure):
+ # Base class for all lilv collection wrappers.
+ def __init__(self, collection, iter_begin, constructor, iter_get, iter_next, is_end):
+ self.collection = collection
+ self.constructor = constructor
+ self.iter_begin = iter_begin
+ self.iter_get = iter_get
+ self.iter_next = iter_next
+ self.is_end = is_end
+
+ def __iter__(self):
+ return Iter(self.collection, self.iter_begin(self.collection), self.constructor,
+ self.iter_get, self.iter_next, self.is_end)
+
+ def __getitem__(self, index):
+ if index >= len(self):
+ raise IndexError
+ pos = 0
+ for i in self:
+ if pos == index:
+ return i
+ pos += 1
+
+ def begin(self):
+ return self.__iter__()
+
+ def get(self, iterator):
+ return iterator.get()
+
+class Plugins(Collection):
+ """Collection of plugins."""
+ def __init__(self, world, collection):
+ def constructor(plugin):
+ return Plugin(world, plugin)
+
+ super(Plugins, self).__init__(collection, plugins_begin, constructor, plugins_get, plugins_next, plugins_is_end)
+ self.world = world
+
+ def __contains__(self, key):
+ return bool(self.get_by_uri(_as_uri(key)))
+
+ def __len__(self):
+ return plugins_size(self.collection)
+
+ def __getitem__(self, key):
+ if type(key) == int:
+ return super(Plugins, self).__getitem__(key)
+ return self.get_by_uri(key)
+
+ def get_by_uri(self, uri):
+ plugin = plugins_get_by_uri(self.collection, uri.node)
+ return Plugin(self.world, plugin) if plugin else None
+
+class PluginClasses(Collection):
+ """Collection of plugin classes."""
+ def __init__(self, collection):
+ super(PluginClasses, self).__init__(
+ collection, plugin_classes_begin, PluginClass,
+ plugin_classes_get, plugin_classes_next, plugin_classes_is_end)
+
+ def __contains__(self, key):
+ return bool(self.get_by_uri(_as_uri(key)))
+
+ def __len__(self):
+ return plugin_classes_size(self.collection)
+
+ def __getitem__(self, key):
+ if type(key) == int:
+ return super(PluginClasses, self).__getitem__(key)
+ return self.get_by_uri(key)
+
+ def get_by_uri(self, uri):
+ plugin_class = plugin_classes_get_by_uri(self.collection, uri.node)
+ return PluginClass(plugin_class) if plugin_class else None
+
+class ScalePoints(Collection):
+ """Collection of scale points."""
+ def __init__(self, collection):
+ super(ScalePoints, self).__init__(
+ collection, scale_points_begin, ScalePoint,
+ scale_points_get, scale_points_next, scale_points_is_end)
+
+ def __len__(self):
+ return scale_points_size(self.collection)
+
+class UIs(Collection):
+ """Collection of plugin UIs."""
+ def __init__(self, collection):
+ super(UIs, self).__init__(collection, uis_begin, UI,
+ uis_get, uis_next, uis_is_end)
+
+ def __contains__(self, uri):
+ return bool(self.get_by_uri(_as_uri(uri)))
+
+ def __len__(self):
+ return uis_size(self.collection)
+
+ def __getitem__(self, key):
+ if type(key) == int:
+ return super(UIs, self).__getitem__(key)
+ return self.get_by_uri(key)
+
+ def get_by_uri(self, uri):
+ ui = uis_get_by_uri(self.collection, uri.node)
+ return UI(ui) if ui else None
+
+class Nodes(Collection):
+ """Collection of data nodes."""
+ @classmethod
+ def constructor(ignore, node):
+ return Node(node_duplicate(node))
+
+ def __init__(self, collection):
+ super(Nodes, self).__init__(collection, nodes_begin, Nodes.constructor,
+ nodes_get, nodes_next, nodes_is_end)
+
+ def __contains__(self, value):
+ return nodes_contains(self.collection, value.node)
+
+ def __len__(self):
+ return nodes_size(self.collection)
+
+ def merge(self, b):
+ return Nodes(nodes_merge(self.collection, b.collection))
+
+class Namespace():
+ """Namespace prefix.
+
+ Use attribute syntax to easily create URIs within this namespace, for
+ example::
+
+ >>> world = lilv.World()
+ >>> ns = Namespace(world, "http://example.org/")
+ >>> print(ns.foo)
+ http://example.org/foo
+ """
+ def __init__(self, world, prefix):
+ self.world = world
+ self.prefix = prefix
+
+ def __eq__(self, other):
+ return str(self) == str(other)
+
+ def __str__(self):
+ return self.prefix
+
+ def __getattr__(self, suffix):
+ return self.world.new_uri(self.prefix + suffix)
+
+class World(Structure):
+ """Library context.
+
+ Includes a set of namespaces as the instance variable `ns`, so URIs can be constructed like::
+
+ uri = world.ns.lv2.Plugin
+
+ :ivar ns: Common LV2 namespace prefixes: atom, doap, foaf, lilv, lv2, midi, owl, rdf, rdfs, ui, xsd.
+ """
+ def __init__(self):
+ world = self
+
+ # Define Namespaces class locally so available prefixes are documented
+ class Namespaces():
+ """Set of namespaces.
+
+ Use to easily construct uris, like: ns.lv2.InputPort"""
+
+ atom = Namespace(world, 'http://lv2plug.in/ns/ext/atom#')
+ doap = Namespace(world, 'http://usefulinc.com/ns/doap#')
+ foaf = Namespace(world, 'http://xmlns.com/foaf/0.1/')
+ lilv = Namespace(world, 'http://drobilla.net/ns/lilv#')
+ lv2 = Namespace(world, 'http://lv2plug.in/ns/lv2core#')
+ midi = Namespace(world, 'http://lv2plug.in/ns/ext/midi#')
+ owl = Namespace(world, 'http://www.w3.org/2002/07/owl#')
+ rdf = Namespace(world, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#')
+ rdfs = Namespace(world, 'http://www.w3.org/2000/01/rdf-schema#')
+ ui = Namespace(world, 'http://lv2plug.in/ns/extensions/ui#')
+ xsd = Namespace(world, 'http://www.w3.org/2001/XMLSchema#')
+
+ self.world = _lib.lilv_world_new()
+ self.ns = Namespaces()
+
+ def __del__(self):
+ world_free(self.world)
+
+ def set_option(self, uri, value):
+ """Set a world option.
+
+ Currently recognized options:
+ lilv.OPTION_FILTER_LANG
+ lilv.OPTION_DYN_MANIFEST
+ """
+ return world_set_option(self, uri, value.node)
+
+ def load_all(self):
+ """Load all installed LV2 bundles on the system.
+
+ This is the recommended way for hosts to load LV2 data. It implements the
+ established/standard best practice for discovering all LV2 data on the
+ system. The environment variable LV2_PATH may be used to control where
+ this function will look for bundles.
+
+ Hosts should use this function rather than explicitly load bundles, except
+ in special circumstances (e.g. development utilities, or hosts that ship
+ with special plugin bundles which are installed to a known location).
+ """
+ world_load_all(self.world)
+
+ def load_bundle(self, bundle_uri):
+ """Load a specific bundle.
+
+ `bundle_uri` must be a fully qualified URI to the bundle directory,
+ with the trailing slash, eg. file:///usr/lib/lv2/foo.lv2/
+
+ Normal hosts should not need this function (use load_all()).
+
+ Hosts MUST NOT attach any long-term significance to bundle paths
+ (e.g. in save files), since there are no guarantees they will remain
+ unchanged between (or even during) program invocations. Plugins (among
+ other things) MUST be identified by URIs (not paths) in save files.
+ """
+ world_load_bundle(self.world, bundle_uri.node)
+
+ def load_specifications(self):
+ """Load all specifications from currently loaded bundles.
+
+ This is for hosts that explicitly load specific bundles, its use is not
+ necessary when using load_all(). This function parses the
+ specifications and adds them to the model.
+ """
+ world_load_specifications(self.world)
+
+ def load_plugin_classes(self):
+ """Load all plugin classes from currently loaded specifications.
+
+ Must be called after load_specifications(). This is for hosts
+ that explicitly load specific bundles, its use is not necessary when using
+ load_all().
+ """
+ world_load_plugin_classes(self.world)
+
+ def unload_bundle(self, bundle_uri):
+ """Unload a specific bundle.
+
+ This unloads statements loaded by load_bundle(). Note that this
+ is not necessarily all information loaded from the bundle. If any resources
+ have been separately loaded with load_resource(), they must be
+ separately unloaded with unload_resource().
+ """
+ return world_unload_bundle(self.world, bundle_uri.node)
+
+ def load_resource(self, resource):
+ """Load all the data associated with the given `resource`.
+
+ The resource must be a subject (i.e. a URI or a blank node).
+ Returns the number of files parsed, or -1 on error.
+
+ All accessible data files linked to `resource` with rdfs:seeAlso will be
+ loaded into the world model.
+ """
+ return world_load_resource(self.world, _as_uri(resource).node)
+
+ def unload_resource(self, resource):
+ """Unload all the data associated with the given `resource`.
+
+ The resource must be a subject (i.e. a URI or a blank node).
+
+ This unloads all data loaded by a previous call to
+ load_resource() with the given `resource`.
+ """
+ return world_unload_resource(self.world, _as_uri(resource).node)
+
+ def get_plugin_class(self):
+ """Get the parent of all other plugin classes, lv2:Plugin."""
+ return PluginClass(world_get_plugin_class(self.world))
+
+ def get_plugin_classes(self):
+ """Return a list of all found plugin classes."""
+ return PluginClasses(world_get_plugin_classes(self.world))
+
+ def get_all_plugins(self):
+ """Return a list of all found plugins.
+
+ The returned list contains just enough references to query
+ or instantiate plugins. The data for a particular plugin will not be
+ loaded into memory until a call to an lilv_plugin_* function results in
+ a query (at which time the data is cached with the LilvPlugin so future
+ queries are very fast).
+
+ The returned list and the plugins it contains are owned by `world`
+ and must not be freed by caller.
+ """
+ return Plugins(self, _lib.lilv_world_get_all_plugins(self.world))
+
+ def find_nodes(self, subject, predicate, obj):
+ """Find nodes matching a triple pattern.
+
+ Either `subject` or `object` may be None (i.e. a wildcard), but not both.
+ Returns all matches for the wildcard field, or None.
+ """
+ return Nodes(world_find_nodes(self.world,
+ subject.node if subject is not None else None,
+ predicate.node if predicate is not None else None,
+ obj.node if obj is not None else None))
+
+ def get(self, subject, predicate, obj):
+ """Find a single node that matches a pattern.
+
+ Exactly one of `subject`, `predicate`, `object` must be None.
+
+ Returns the first matching node, or None if no matches are found.
+ """
+ return Node.wrap(world_get(self.world,
+ subject.node if subject is not None else None,
+ predicate.node if predicate is not None else None,
+ obj.node if obj is not None else None))
+
+ def ask(self, subject, predicate, obj):
+ """Return true iff a statement matching a certain pattern exists.
+
+ This is useful for checking if particular statement exists without having to
+ bother with collections and memory management.
+ """
+ return world_ask(self.world,
+ subject.node if subject is not None else None,
+ predicate.node if predicate is not None else None,
+ obj.node if obj is not None else None)
+
+ def new_uri(self, uri):
+ """Create a new URI node."""
+ return Node.wrap(_lib.lilv_new_uri(self.world, uri))
+
+ def new_file_uri(self, host, path):
+ """Create a new file URI node. The host may be None."""
+ return Node.wrap(_lib.lilv_new_file_uri(self.world, host, path))
+
+ def new_string(self, string):
+ """Create a new string node."""
+ return Node.wrap(_lib.lilv_new_string(self.world, string))
+
+ def new_int(self, val):
+ """Create a new int node."""
+ return Node.wrap(_lib.lilv_new_int(self.world, val))
+
+ def new_float(self, val):
+ """Create a new float node."""
+ return Node.wrap(_lib.lilv_new_float(self.world, val))
+
+ def new_bool(self, val):
+ """Create a new bool node."""
+ return Node.wrap(_lib.lilv_new_bool(self.world, val))
+
+class Instance(Structure):
+ """Plugin instance."""
+ __slots__ = [ 'lv2_descriptor', 'lv2_handle', 'pimpl', 'plugin', 'rate', 'instance' ]
+ _fields_ = [
+ ('lv2_descriptor', POINTER(LV2_Descriptor)),
+ ('lv2_handle', LV2_Handle),
+ ('pimpl', POINTER(None)),
+ ]
+
+ def __init__(self, plugin, rate, features=None):
+ self.plugin = plugin
+ self.rate = rate
+ self.instance = plugin_instantiate(plugin.plugin, rate, features)
+
+ def get_uri(self):
+ """Get the URI of the plugin which `instance` is an instance of.
+
+ Returned string is shared and must not be modified or deleted.
+ """
+ return self.get_descriptor().URI
+
+ def connect_port(self, port_index, data):
+ """Connect a port to a data location.
+
+ This may be called regardless of whether the plugin is activated,
+ activation and deactivation does not destroy port connections.
+ """
+ import numpy
+ if data is None:
+ self.get_descriptor().connect_port(
+ self.get_handle(),
+ port_index,
+ data)
+ elif type(data) == numpy.ndarray:
+ self.get_descriptor().connect_port(
+ self.get_handle(),
+ port_index,
+ data.ctypes.data_as(POINTER(c_float)))
+ else:
+ raise Exception("Unsupported data type")
+
+ def activate(self):
+ """Activate a plugin instance.
+
+ This resets all state information in the plugin, except for port data
+ locations (as set by connect_port()). This MUST be called
+ before calling run().
+ """
+ if self.get_descriptor().activate:
+ self.get_descriptor().activate(self.get_handle())
+
+ def run(self, sample_count):
+ """Run `instance` for `sample_count` frames.
+
+ If the hint lv2:hardRTCapable is set for this plugin, this function is
+ guaranteed not to block.
+ """
+ self.get_descriptor().run(self.get_handle(), sample_count)
+
+ def deactivate(self):
+ """Deactivate a plugin instance.
+
+ Note that to run the plugin after this you must activate it, which will
+ reset all state information (except port connections).
+ """
+ if self.get_descriptor().deactivate:
+ self.get_descriptor().deactivate(self.get_handle())
+
+ def get_extension_data(self, uri):
+ """Get extension data from the plugin instance.
+
+ The type and semantics of the data returned is specific to the particular
+ extension, though in all cases it is shared and must not be deleted.
+ """
+ if self.get_descriptor().extension_data:
+ return self.get_descriptor().extension_data(str(uri))
+
+ def get_descriptor(self):
+ """Get the LV2_Descriptor of the plugin instance.
+
+ Normally hosts should not need to access the LV2_Descriptor directly,
+ use the lilv_instance_* functions.
+ """
+ return self.instance[0].lv2_descriptor[0]
+
+ def get_handle(self):
+ """Get the LV2_Handle of the plugin instance.
+
+ Normally hosts should not need to access the LV2_Handle directly,
+ use the lilv_instance_* functions.
+ """
+ return self.instance[0].lv2_handle
+
+class State(Structure):
+ """Plugin state (TODO)."""
+ pass
+
+class VariadicFunction(object):
+ # Wrapper for calling C variadic functions
+ def __init__(self, function, restype, argtypes):
+ self.function = function
+ self.function.restype = restype
+ self.argtypes = argtypes
+
+ def __call__(self, *args):
+ fixed_args = []
+ i = 0
+ for argtype in self.argtypes:
+ fixed_args.append(argtype.from_param(args[i]))
+ i += 1
+ return self.function(*fixed_args + list(args[i:]))
+
+# Set return and argument types for lilv C functions
+
+free.argtypes = [POINTER(None)]
+free.restype = None
+
+# uri_to_path.argtypes = [String]
+# uri_to_path.restype = c_char_p
+
+file_uri_parse.argtypes = [String, POINTER(POINTER(c_char))]
+file_uri_parse.restype = c_char_p
+
+new_uri.argtypes = [POINTER(World), String]
+new_uri.restype = POINTER(Node)
+
+new_file_uri.argtypes = [POINTER(World), c_char_p, String]
+new_file_uri.restype = POINTER(Node)
+
+new_string.argtypes = [POINTER(World), String]
+new_string.restype = POINTER(Node)
+
+new_int.argtypes = [POINTER(World), c_int]
+new_int.restype = POINTER(Node)
+
+new_float.argtypes = [POINTER(World), c_float]
+new_float.restype = POINTER(Node)
+
+new_bool.argtypes = [POINTER(World), c_bool]
+new_bool.restype = POINTER(Node)
+
+node_free.argtypes = [POINTER(Node)]
+node_free.restype = None
+
+node_duplicate.argtypes = [POINTER(Node)]
+node_duplicate.restype = POINTER(Node)
+
+node_equals.argtypes = [POINTER(Node), POINTER(Node)]
+node_equals.restype = c_bool
+
+node_get_turtle_token.argtypes = [POINTER(Node)]
+node_get_turtle_token.restype = c_char_p
+
+node_is_uri.argtypes = [POINTER(Node)]
+node_is_uri.restype = c_bool
+
+node_as_uri.argtypes = [POINTER(Node)]
+node_as_uri.restype = c_char_p
+
+node_is_blank.argtypes = [POINTER(Node)]
+node_is_blank.restype = c_bool
+
+node_as_blank.argtypes = [POINTER(Node)]
+node_as_blank.restype = c_char_p
+
+node_is_literal.argtypes = [POINTER(Node)]
+node_is_literal.restype = c_bool
+
+node_is_string.argtypes = [POINTER(Node)]
+node_is_string.restype = c_bool
+
+node_as_string.argtypes = [POINTER(Node)]
+node_as_string.restype = c_char_p
+
+node_get_path.argtypes = [POINTER(Node), POINTER(POINTER(c_char))]
+node_get_path.restype = c_char_p
+
+node_is_float.argtypes = [POINTER(Node)]
+node_is_float.restype = c_bool
+
+node_as_float.argtypes = [POINTER(Node)]
+node_as_float.restype = c_float
+
+node_is_int.argtypes = [POINTER(Node)]
+node_is_int.restype = c_bool
+
+node_as_int.argtypes = [POINTER(Node)]
+node_as_int.restype = c_int
+
+node_is_bool.argtypes = [POINTER(Node)]
+node_is_bool.restype = c_bool
+
+node_as_bool.argtypes = [POINTER(Node)]
+node_as_bool.restype = c_bool
+
+plugin_classes_free.argtypes = [POINTER(PluginClasses)]
+plugin_classes_free.restype = None
+
+plugin_classes_size.argtypes = [POINTER(PluginClasses)]
+plugin_classes_size.restype = c_uint
+
+plugin_classes_begin.argtypes = [POINTER(PluginClasses)]
+plugin_classes_begin.restype = POINTER(Iter)
+
+plugin_classes_get.argtypes = [POINTER(PluginClasses), POINTER(Iter)]
+plugin_classes_get.restype = POINTER(PluginClass)
+
+plugin_classes_next.argtypes = [POINTER(PluginClasses), POINTER(Iter)]
+plugin_classes_next.restype = POINTER(Iter)
+
+plugin_classes_is_end.argtypes = [POINTER(PluginClasses), POINTER(Iter)]
+plugin_classes_is_end.restype = c_bool
+
+plugin_classes_get_by_uri.argtypes = [POINTER(PluginClasses), POINTER(Node)]
+plugin_classes_get_by_uri.restype = POINTER(PluginClass)
+
+scale_points_free.argtypes = [POINTER(ScalePoints)]
+scale_points_free.restype = None
+
+scale_points_size.argtypes = [POINTER(ScalePoints)]
+scale_points_size.restype = c_uint
+
+scale_points_begin.argtypes = [POINTER(ScalePoints)]
+scale_points_begin.restype = POINTER(Iter)
+
+scale_points_get.argtypes = [POINTER(ScalePoints), POINTER(Iter)]
+scale_points_get.restype = POINTER(ScalePoint)
+
+scale_points_next.argtypes = [POINTER(ScalePoints), POINTER(Iter)]
+scale_points_next.restype = POINTER(Iter)
+
+scale_points_is_end.argtypes = [POINTER(ScalePoints), POINTER(Iter)]
+scale_points_is_end.restype = c_bool
+
+uis_free.argtypes = [POINTER(UIs)]
+uis_free.restype = None
+
+uis_size.argtypes = [POINTER(UIs)]
+uis_size.restype = c_uint
+
+uis_begin.argtypes = [POINTER(UIs)]
+uis_begin.restype = POINTER(Iter)
+
+uis_get.argtypes = [POINTER(UIs), POINTER(Iter)]
+uis_get.restype = POINTER(UI)
+
+uis_next.argtypes = [POINTER(UIs), POINTER(Iter)]
+uis_next.restype = POINTER(Iter)
+
+uis_is_end.argtypes = [POINTER(UIs), POINTER(Iter)]
+uis_is_end.restype = c_bool
+
+uis_get_by_uri.argtypes = [POINTER(UIs), POINTER(Node)]
+uis_get_by_uri.restype = POINTER(UI)
+
+nodes_free.argtypes = [POINTER(Nodes)]
+nodes_free.restype = None
+
+nodes_size.argtypes = [POINTER(Nodes)]
+nodes_size.restype = c_uint
+
+nodes_begin.argtypes = [POINTER(Nodes)]
+nodes_begin.restype = POINTER(Iter)
+
+nodes_get.argtypes = [POINTER(Nodes), POINTER(Iter)]
+nodes_get.restype = POINTER(Node)
+
+nodes_next.argtypes = [POINTER(Nodes), POINTER(Iter)]
+nodes_next.restype = POINTER(Iter)
+
+nodes_is_end.argtypes = [POINTER(Nodes), POINTER(Iter)]
+nodes_is_end.restype = c_bool
+
+nodes_get_first.argtypes = [POINTER(Nodes)]
+nodes_get_first.restype = POINTER(Node)
+
+nodes_contains.argtypes = [POINTER(Nodes), POINTER(Node)]
+nodes_contains.restype = c_bool
+
+nodes_merge.argtypes = [POINTER(Nodes), POINTER(Nodes)]
+nodes_merge.restype = POINTER(Nodes)
+
+plugins_size.argtypes = [POINTER(Plugins)]
+plugins_size.restype = c_uint
+
+plugins_begin.argtypes = [POINTER(Plugins)]
+plugins_begin.restype = POINTER(Iter)
+
+plugins_get.argtypes = [POINTER(Plugins), POINTER(Iter)]
+plugins_get.restype = POINTER(Plugin)
+
+plugins_next.argtypes = [POINTER(Plugins), POINTER(Iter)]
+plugins_next.restype = POINTER(Iter)
+
+plugins_is_end.argtypes = [POINTER(Plugins), POINTER(Iter)]
+plugins_is_end.restype = c_bool
+
+plugins_get_by_uri.argtypes = [POINTER(Plugins), POINTER(Node)]
+plugins_get_by_uri.restype = POINTER(Plugin)
+
+world_new.argtypes = []
+world_new.restype = POINTER(World)
+
+world_set_option.argtypes = [POINTER(World), String, POINTER(Node)]
+world_set_option.restype = None
+
+world_free.argtypes = [POINTER(World)]
+world_free.restype = None
+
+world_load_all.argtypes = [POINTER(World)]
+world_load_all.restype = None
+
+world_load_bundle.argtypes = [POINTER(World), POINTER(Node)]
+world_load_bundle.restype = None
+
+world_load_specifications.argtypes = [POINTER(World)]
+world_load_specifications.restype = None
+
+world_load_plugin_classes.argtypes = [POINTER(World)]
+world_load_plugin_classes.restype = None
+
+world_unload_bundle.argtypes = [POINTER(World), POINTER(Node)]
+world_unload_bundle.restype = c_int
+
+world_load_resource.argtypes = [POINTER(World), POINTER(Node)]
+world_load_resource.restype = c_int
+
+world_unload_resource.argtypes = [POINTER(World), POINTER(Node)]
+world_unload_resource.restype = c_int
+
+world_get_plugin_class.argtypes = [POINTER(World)]
+world_get_plugin_class.restype = POINTER(PluginClass)
+
+world_get_plugin_classes.argtypes = [POINTER(World)]
+world_get_plugin_classes.restype = POINTER(PluginClasses)
+
+world_get_all_plugins.argtypes = [POINTER(World)]
+world_get_all_plugins.restype = POINTER(Plugins)
+
+world_find_nodes.argtypes = [POINTER(World), POINTER(Node), POINTER(Node), POINTER(Node)]
+world_find_nodes.restype = POINTER(Nodes)
+
+world_get.argtypes = [POINTER(World), POINTER(Node), POINTER(Node), POINTER(Node)]
+world_get.restype = POINTER(Node)
+
+world_ask.argtypes = [POINTER(World), POINTER(Node), POINTER(Node), POINTER(Node)]
+world_ask.restype = c_bool
+
+plugin_verify.argtypes = [POINTER(Plugin)]
+plugin_verify.restype = c_bool
+
+plugin_get_uri.argtypes = [POINTER(Plugin)]
+plugin_get_uri.restype = POINTER(Node)
+
+plugin_get_bundle_uri.argtypes = [POINTER(Plugin)]
+plugin_get_bundle_uri.restype = POINTER(Node)
+
+plugin_get_data_uris.argtypes = [POINTER(Plugin)]
+plugin_get_data_uris.restype = POINTER(Nodes)
+
+plugin_get_library_uri.argtypes = [POINTER(Plugin)]
+plugin_get_library_uri.restype = POINTER(Node)
+
+plugin_get_name.argtypes = [POINTER(Plugin)]
+plugin_get_name.restype = POINTER(Node)
+
+plugin_get_class.argtypes = [POINTER(Plugin)]
+plugin_get_class.restype = POINTER(PluginClass)
+
+plugin_get_value.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_get_value.restype = POINTER(Nodes)
+
+plugin_has_feature.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_has_feature.restype = c_bool
+
+plugin_get_supported_features.argtypes = [POINTER(Plugin)]
+plugin_get_supported_features.restype = POINTER(Nodes)
+
+plugin_get_required_features.argtypes = [POINTER(Plugin)]
+plugin_get_required_features.restype = POINTER(Nodes)
+
+plugin_get_optional_features.argtypes = [POINTER(Plugin)]
+plugin_get_optional_features.restype = POINTER(Nodes)
+
+plugin_has_extension_data.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_has_extension_data.restype = c_bool
+
+plugin_get_extension_data.argtypes = [POINTER(Plugin)]
+plugin_get_extension_data.restype = POINTER(Nodes)
+
+plugin_get_num_ports.argtypes = [POINTER(Plugin)]
+plugin_get_num_ports.restype = c_uint32
+
+plugin_get_port_ranges_float.argtypes = [POINTER(Plugin), POINTER(c_float), POINTER(c_float), POINTER(c_float)]
+plugin_get_port_ranges_float.restype = None
+
+plugin_get_num_ports_of_class = VariadicFunction(_lib.lilv_plugin_get_num_ports_of_class,
+ c_uint32,
+ [POINTER(Plugin), POINTER(Node)])
+
+plugin_has_latency.argtypes = [POINTER(Plugin)]
+plugin_has_latency.restype = c_bool
+
+plugin_get_latency_port_index.argtypes = [POINTER(Plugin)]
+plugin_get_latency_port_index.restype = c_uint32
+
+plugin_get_port_by_index.argtypes = [POINTER(Plugin), c_uint32]
+plugin_get_port_by_index.restype = POINTER(Port)
+
+plugin_get_port_by_symbol.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_get_port_by_symbol.restype = POINTER(Port)
+
+plugin_get_port_by_designation.argtypes = [POINTER(Plugin), POINTER(Node), POINTER(Node)]
+plugin_get_port_by_designation.restype = POINTER(Port)
+
+plugin_get_project.argtypes = [POINTER(Plugin)]
+plugin_get_project.restype = POINTER(Node)
+
+plugin_get_author_name.argtypes = [POINTER(Plugin)]
+plugin_get_author_name.restype = POINTER(Node)
+
+plugin_get_author_email.argtypes = [POINTER(Plugin)]
+plugin_get_author_email.restype = POINTER(Node)
+
+plugin_get_author_homepage.argtypes = [POINTER(Plugin)]
+plugin_get_author_homepage.restype = POINTER(Node)
+
+plugin_is_replaced.argtypes = [POINTER(Plugin)]
+plugin_is_replaced.restype = c_bool
+
+plugin_get_related.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_get_related.restype = POINTER(Nodes)
+
+port_get_node.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_node.restype = POINTER(Node)
+
+port_get_value.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_get_value.restype = POINTER(Nodes)
+
+port_get.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_get.restype = POINTER(Node)
+
+port_get_properties.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_properties.restype = POINTER(Nodes)
+
+port_has_property.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_has_property.restype = c_bool
+
+port_supports_event.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_supports_event.restype = c_bool
+
+port_get_index.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_index.restype = c_uint32
+
+port_get_symbol.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_symbol.restype = POINTER(Node)
+
+port_get_name.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_name.restype = POINTER(Node)
+
+port_get_classes.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_classes.restype = POINTER(Nodes)
+
+port_is_a.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_is_a.restype = c_bool
+
+port_get_range.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(POINTER(Node)), POINTER(POINTER(Node)), POINTER(POINTER(Node))]
+port_get_range.restype = None
+
+port_get_scale_points.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_scale_points.restype = POINTER(ScalePoints)
+
+state_new_from_world.argtypes = [POINTER(World), POINTER(LV2_URID_Map), POINTER(Node)]
+state_new_from_world.restype = POINTER(State)
+
+state_new_from_file.argtypes = [POINTER(World), POINTER(LV2_URID_Map), POINTER(Node), String]
+state_new_from_file.restype = POINTER(State)
+
+state_new_from_string.argtypes = [POINTER(World), POINTER(LV2_URID_Map), String]
+state_new_from_string.restype = POINTER(State)
+
+LilvGetPortValueFunc = CFUNCTYPE(c_void_p, c_char_p, POINTER(None), POINTER(c_uint32), POINTER(c_uint32))
+
+state_new_from_instance.argtypes = [POINTER(Plugin), POINTER(Instance), POINTER(LV2_URID_Map), c_char_p, c_char_p, c_char_p, String, LilvGetPortValueFunc, POINTER(None), c_uint32, POINTER(POINTER(LV2_Feature))]
+state_new_from_instance.restype = POINTER(State)
+
+state_free.argtypes = [POINTER(State)]
+state_free.restype = None
+
+state_equals.argtypes = [POINTER(State), POINTER(State)]
+state_equals.restype = c_bool
+
+state_get_num_properties.argtypes = [POINTER(State)]
+state_get_num_properties.restype = c_uint
+
+state_get_plugin_uri.argtypes = [POINTER(State)]
+state_get_plugin_uri.restype = POINTER(Node)
+
+state_get_uri.argtypes = [POINTER(State)]
+state_get_uri.restype = POINTER(Node)
+
+state_get_label.argtypes = [POINTER(State)]
+state_get_label.restype = c_char_p
+
+state_set_label.argtypes = [POINTER(State), String]
+state_set_label.restype = None
+
+state_set_metadata.argtypes = [POINTER(State), c_uint32, POINTER(None), c_size_t, c_uint32, c_uint32]
+state_set_metadata.restype = c_int
+
+LilvSetPortValueFunc = CFUNCTYPE(None, c_char_p, POINTER(None), POINTER(None), c_uint32, c_uint32)
+state_emit_port_values.argtypes = [POINTER(State), LilvSetPortValueFunc, POINTER(None)]
+state_emit_port_values.restype = None
+
+state_restore.argtypes = [POINTER(State), POINTER(Instance), LilvSetPortValueFunc, POINTER(None), c_uint32, POINTER(POINTER(LV2_Feature))]
+state_restore.restype = None
+
+state_save.argtypes = [POINTER(World), POINTER(LV2_URID_Map), POINTER(LV2_URID_Unmap), POINTER(State), c_char_p, c_char_p, String]
+state_save.restype = c_int
+
+state_to_string.argtypes = [POINTER(World), POINTER(LV2_URID_Map), POINTER(LV2_URID_Unmap), POINTER(State), c_char_p, String]
+state_to_string.restype = c_char_p
+
+state_delete.argtypes = [POINTER(World), POINTER(State)]
+state_delete.restype = c_int
+
+scale_point_get_label.argtypes = [POINTER(ScalePoint)]
+scale_point_get_label.restype = POINTER(Node)
+
+scale_point_get_value.argtypes = [POINTER(ScalePoint)]
+scale_point_get_value.restype = POINTER(Node)
+
+plugin_class_get_parent_uri.argtypes = [POINTER(PluginClass)]
+plugin_class_get_parent_uri.restype = POINTER(Node)
+
+plugin_class_get_uri.argtypes = [POINTER(PluginClass)]
+plugin_class_get_uri.restype = POINTER(Node)
+
+plugin_class_get_label.argtypes = [POINTER(PluginClass)]
+plugin_class_get_label.restype = POINTER(Node)
+
+plugin_class_get_children.argtypes = [POINTER(PluginClass)]
+plugin_class_get_children.restype = POINTER(PluginClasses)
+
+plugin_instantiate.argtypes = [POINTER(Plugin), c_double, POINTER(POINTER(LV2_Feature))]
+plugin_instantiate.restype = POINTER(Instance)
+
+instance_free.argtypes = [POINTER(Instance)]
+instance_free.restype = None
+
+plugin_get_uis.argtypes = [POINTER(Plugin)]
+plugin_get_uis.restype = POINTER(UIs)
+
+ui_get_uri.argtypes = [POINTER(UI)]
+ui_get_uri.restype = POINTER(Node)
+
+ui_get_classes.argtypes = [POINTER(UI)]
+ui_get_classes.restype = POINTER(Nodes)
+
+ui_is_a.argtypes = [POINTER(UI), POINTER(Node)]
+ui_is_a.restype = c_bool
+
+LilvUISupportedFunc = CFUNCTYPE(c_uint, c_char_p, c_char_p)
+
+ui_is_supported.argtypes = [POINTER(UI), LilvUISupportedFunc, POINTER(Node), POINTER(POINTER(Node))]
+ui_is_supported.restype = c_uint
+
+ui_get_bundle_uri.argtypes = [POINTER(UI)]
+ui_get_bundle_uri.restype = POINTER(Node)
+
+ui_get_binary_uri.argtypes = [POINTER(UI)]
+ui_get_binary_uri.restype = POINTER(Node)
+
+OPTION_FILTER_LANG = 'http://drobilla.net/ns/lilv#filter-lang'
+OPTION_DYN_MANIFEST = 'http://drobilla.net/ns/lilv#dyn-manifest'
+
+# Define URI constants for compatibility with old Python bindings
+
+LILV_NS_DOAP = 'http://usefulinc.com/ns/doap#'
+LILV_NS_FOAF = 'http://xmlns.com/foaf/0.1/'
+LILV_NS_LILV = 'http://drobilla.net/ns/lilv#'
+LILV_NS_LV2 = 'http://lv2plug.in/ns/lv2core#'
+LILV_NS_OWL = 'http://www.w3.org/2002/07/owl#'
+LILV_NS_RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
+LILV_NS_RDFS = 'http://www.w3.org/2000/01/rdf-schema#'
+LILV_NS_XSD = 'http://www.w3.org/2001/XMLSchema#'
+LILV_URI_ATOM_PORT = 'http://lv2plug.in/ns/ext/atom#AtomPort'
+LILV_URI_AUDIO_PORT = 'http://lv2plug.in/ns/lv2core#AudioPort'
+LILV_URI_CONTROL_PORT = 'http://lv2plug.in/ns/lv2core#ControlPort'
+LILV_URI_CV_PORT = 'http://lv2plug.in/ns/lv2core#CVPort'
+LILV_URI_EVENT_PORT = 'http://lv2plug.in/ns/ext/event#EventPort'
+LILV_URI_INPUT_PORT = 'http://lv2plug.in/ns/lv2core#InputPort'
+LILV_URI_MIDI_EVENT = 'http://lv2plug.in/ns/ext/midi#MidiEvent'
+LILV_URI_OUTPUT_PORT = 'http://lv2plug.in/ns/lv2core#OutputPort'
+LILV_URI_PORT = 'http://lv2plug.in/ns/lv2core#Port'
+LILV_OPTION_FILTER_LANG = 'http://drobilla.net/ns/lilv#filter-lang'
+LILV_OPTION_DYN_MANIFEST = 'http://drobilla.net/ns/lilv#dyn-manifest'
diff --git a/bindings/python/lv2_apply.py b/bindings/python/lv2_apply.py
new file mode 100755
index 0000000..4c7d9b4
--- /dev/null
+++ b/bindings/python/lv2_apply.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import math
+import lilv
+import sys
+import wave
+import numpy
+
+class WavFile(object):
+ """Helper class for accessing wav file data. Should work on the most common
+ formats (8 bit unsigned, 16 bit signed, 32 bit signed). Audio data is
+ converted to float32."""
+
+ # (struct format code, is_signedtype) for each sample width:
+ WAV_SPECS = {
+ 1: ("B", False),
+ 2: ("h", True),
+ 4: ("l", True),
+ }
+
+ def __init__(self, wav_in_path):
+ self.wav_in = wave.open(wav_in_path, 'r')
+ self.framerate = self.wav_in.getframerate()
+ self.nframes = self.wav_in.getnframes()
+ self.nchannels = self.wav_in.getnchannels()
+ self.sampwidth = self.wav_in.getsampwidth()
+ wav_spec = self.WAV_SPECS[self.sampwidth]
+ self.struct_fmt_code, self.signed = wav_spec
+ self.range = 2 ** (8*self.sampwidth)
+
+ def read(self):
+ """Read data from an open wav file. Return a list of channels, where each
+ channel is a list of floats."""
+ raw_bytes = self.wav_in.readframes(self.nframes)
+ struct_fmt = "%u%s" % (len(raw_bytes) / self.sampwidth, self.struct_fmt_code)
+ data = wave.struct.unpack(struct_fmt, raw_bytes)
+ if self.signed:
+ data = [i / float(self.range/2) for i in data]
+ else:
+ data = [(i - float(range/2)) / float(range/2) for i in data]
+
+ channels = []
+ for i in range(self.nchannels):
+ channels.append([data[j] for j in range(0, len(data), self.nchannels) ])
+
+ return channels
+
+ def close(self):
+ self.wav_in.close()
+
+def main():
+ # Read command line arguments
+ if len(sys.argv) != 4:
+ print('USAGE: lv2_apply.py PLUGIN_URI INPUT_WAV OUTPUT_WAV')
+ sys.exit(1)
+
+ # Initialise Lilv
+ world = lilv.World()
+ ns = world.ns
+ world.load_all()
+
+ plugin_uri = sys.argv[1]
+ wav_in_path = sys.argv[2]
+ wav_out_path = sys.argv[3]
+
+ # Find plugin
+ plugin_uri_node = world.new_uri(plugin_uri)
+ plugins = world.get_all_plugins()
+ if plugin_uri_node not in plugins:
+ print("Unknown plugin `%s'" % plugin_uri)
+ sys.exit(1)
+
+ plugin = plugins[plugin_uri_node]
+ n_audio_in = plugin.get_num_ports_of_class(ns.lv2.InputPort, ns.lv2.AudioPort)
+ n_audio_out = plugin.get_num_ports_of_class(ns.lv2.OutputPort, ns.lv2.AudioPort)
+ if n_audio_out == 0:
+ print("Plugin has no audio outputs\n")
+ sys.exit(1)
+
+ # Open input file
+ try:
+ wav_in = WavFile(wav_in_path)
+ except:
+ print("Failed to open input `%s'\n" % wav_in_path)
+ sys.exit(1)
+
+ if wav_in.nchannels != n_audio_in:
+ print("Input has %d channels, but plugin has %d audio inputs\n" % (
+ wav_in.nchannels, n_audio_in))
+ sys.exit(1)
+
+ # Open output file
+ wav_out = wave.open(wav_out_path, 'w')
+ if not wav_out:
+ print("Failed to open output `%s'\n" % wav_out_path)
+ sys.exit(1)
+
+ # Set output file to same format as input (except possibly nchannels)
+ wav_out.setparams(wav_in.wav_in.getparams())
+ wav_out.setnchannels(n_audio_out)
+
+ print('%s => %s => %s @ %d Hz'
+ % (wav_in_path, plugin.get_name(), wav_out_path, wav_in.framerate))
+
+ instance = lilv.Instance(plugin, wav_in.framerate)
+
+ channels = wav_in.read()
+ wav_in.close()
+
+ # Connect all ports to buffers. NB if we fail to connect any buffer, lilv
+ # will segfault.
+ audio_input_buffers = []
+ audio_output_buffers = []
+ control_input_buffers = []
+ control_output_buffers = []
+ for index in range(plugin.get_num_ports()):
+ port = plugin.get_port_by_index(index)
+ if port.is_a(ns.lv2.InputPort):
+ if port.is_a(ns.lv2.AudioPort):
+ audio_input_buffers.append(numpy.array(channels[len(audio_input_buffers)], numpy.float32))
+ instance.connect_port(index, audio_input_buffers[-1])
+ elif port.is_a(ns.lv2.ControlPort):
+ default = float(port.get(ns.lv2.default))
+ control_input_buffers.append(numpy.array([default], numpy.float32))
+ instance.connect_port(index, control_input_buffers[-1])
+ else:
+ raise ValueError("Unhandled port type")
+ elif port.is_a(ns.lv2.OutputPort):
+ if port.is_a(ns.lv2.AudioPort):
+ audio_output_buffers.append(numpy.array([0] * wav_in.nframes, numpy.float32))
+ instance.connect_port(index, audio_output_buffers[-1])
+ elif port.is_a(ns.lv2.ControlPort):
+ control_output_buffers.append(numpy.array([0], numpy.float32))
+ instance.connect_port(index, control_output_buffers[-1])
+ else:
+ raise ValueError("Unhandled port type")
+
+ # Run the plugin:
+ instance.run(wav_in.nframes)
+
+ # Interleave output buffers:
+ data = numpy.dstack(audio_output_buffers).flatten()
+
+ # Return to original int range:
+ if wav_in.signed:
+ data = data * float(wav_in.range / 2)
+ else:
+ data = (data + 1) * float(wav_in.range/2)
+
+ # Write output file in chunks to stop memory usage getting out of hand:
+ CHUNK_SIZE = 8192
+ for chunk in numpy.array_split(data, CHUNK_SIZE):
+ wav_out.writeframes(wave.struct.pack("%u%s" % (len(chunk), wav_in.struct_fmt_code), *chunk.astype(int)))
+ wav_out.close()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/bindings/python/lv2_list.py b/bindings/python/lv2_list.py
new file mode 100755
index 0000000..babe1b4
--- /dev/null
+++ b/bindings/python/lv2_list.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+import lilv
+
+world = lilv.World()
+world.load_all()
+
+for i in world.get_all_plugins():
+ print(i.get_uri())
diff --git a/bindings/test/bindings_test_plugin.c b/bindings/test/bindings_test_plugin.c
new file mode 100644
index 0000000..3d6c763
--- /dev/null
+++ b/bindings/test/bindings_test_plugin.c
@@ -0,0 +1,196 @@
+/*
+ Copyright 2006-2011 David Robillard <d@drobilla.net>
+ Copyright 2006 Steve Harris <steve@plugin.org.uk>
+
+ 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 standard C headers */
+#include <math.h>
+#include <stdlib.h>
+
+/**
+ LV2 headers are based on the URI of the specification they come from, so a
+ consistent convention can be used even for unofficial extensions. The URI
+ of the core LV2 specification is <http://lv2plug.in/ns/lv2core>, by
+ replacing `http:/` with `lv2` any header in the specification bundle can be
+ included, in this case `lv2.h`.
+*/
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+/**
+ The URI is the identifier for a plugin, and how the host associates this
+ implementation in code with its description in data. In this plugin it is
+ only used once in the code, but defining the plugin URI at the top of the
+ file is a good convention to follow. If this URI does not match that used
+ in the data files, the host will fail to load the plugin.
+*/
+#define TEST_URI "http://example.org/lilv-bindings-test-plugin"
+
+/**
+ In code, ports are referred to by index. An enumeration of port indices
+ should be defined for readability.
+*/
+typedef enum {
+ TEST_CONTROL_IN = 0,
+ TEST_CONTROL_OUT = 1,
+ TEST_AUDIO_IN = 2,
+ TEST_AUDIO_OUT = 3
+} PortIndex;
+
+/**
+ Every plugin defines a private structure for the plugin instance. All data
+ associated with a plugin instance is stored here, and is available to
+ every instance method. In this simple plugin, only port buffers need to be
+ stored, since there is no additional instance data. */
+typedef struct {
+ float* buf;
+} Test;
+
+/**
+ The instantiate() function is called by the host to create a new plugin
+ instance. The host passes the plugin descriptor, sample rate, and bundle
+ path for plugins that need to load additional resources (e.g. waveforms).
+ The features parameter contains host-provided features defined in LV2
+ extensions, but this simple plugin does not use any.
+
+ This function is in the ``instantiation'' threading class, so no other
+ methods on this instance will be called concurrently with it.
+*/
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Test* test = (Test*)malloc(sizeof(Test));
+
+ return (LV2_Handle)test;
+}
+
+/**
+ The connect_port() method is called by the host to connect a particular port
+ to a buffer. The plugin must store the data location, but data may not be
+ accessed except in run().
+
+ This method is in the ``audio'' threading class, and is called in the same
+ context as run().
+*/
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+}
+
+/**
+ The activate() method is called by the host to initialise and prepare the
+ plugin instance for running. The plugin must reset all internal state
+ except for buffer locations set by connect_port(). Since this plugin has
+ no other internal state, this method does nothing.
+
+ This method is in the ``instantiation'' threading class, so no other
+ methods on this instance will be called concurrently with it.
+*/
+static void
+activate(LV2_Handle instance)
+{
+}
+
+/** Process a block of audio (audio thread, must be RT safe). */
+static void
+run(LV2_Handle instance, uint32_t n_samples)
+{
+}
+
+/**
+ The deactivate() method is the counterpart to activate() called by the host
+ after running the plugin. It indicates that the host will not call run()
+ again until another call to activate() and is mainly useful for more
+ advanced plugins with ``live'' characteristics such as those with auxiliary
+ processing threads. As with activate(), this plugin has no use for this
+ information so this method does nothing.
+
+ This method is in the ``instantiation'' threading class, so no other
+ methods on this instance will be called concurrently with it.
+*/
+static void
+deactivate(LV2_Handle instance)
+{
+}
+
+/**
+ Destroy a plugin instance (counterpart to instantiate()).
+
+ This method is in the ``instantiation'' threading class, so no other
+ methods on this instance will be called concurrently with it.
+*/
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+/**
+ The extension_data function returns any extension data supported by the
+ plugin. Note that this is not an instance method, but a function on the
+ plugin descriptor. It is usually used by plugins to implement additional
+ interfaces. This plugin does not have any extension data, so this function
+ returns NULL.
+
+ This method is in the ``discovery'' threading class, so no other functions
+ or methods in this plugin library will be called concurrently with it.
+*/
+static const void*
+extension_data(const char* uri)
+{
+ return NULL;
+}
+
+/**
+ Define the LV2_Descriptor for this plugin. It is best to define descriptors
+ statically to avoid leaking memory and non-portable shared library
+ constructors and destructors to clean up properly.
+*/
+static const LV2_Descriptor descriptor = {
+ TEST_URI,
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ deactivate,
+ cleanup,
+ extension_data
+};
+
+/**
+ The lv2_descriptor() function is the entry point to the plugin library. The
+ host will load the library and call this function repeatedly with increasing
+ indices to find all the plugins defined in the library. The index is not an
+ indentifier, the URI of the returned descriptor is used to determine the
+ identify of the plugin.
+
+ This method is in the ``discovery'' threading class, so no other functions
+ or methods in this plugin library will be called concurrently with it.
+*/
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0:
+ return &descriptor;
+ default:
+ return NULL;
+ }
+}
diff --git a/bindings/test/bindings_test_plugin.ttl.in b/bindings/test/bindings_test_plugin.ttl.in
new file mode 100644
index 0000000..e8323d5
--- /dev/null
+++ b/bindings/test/bindings_test_plugin.ttl.in
@@ -0,0 +1,62 @@
+# Lilv Bindings Test Plugin
+# Copyright 2011 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/lilv-bindings-test-plugin>
+ a lv2:Plugin ;
+ doap:name "Lilv Bindings Test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ ui:ui <http://example.org/lilv-bindings-test-plugin-ui> ;
+ lv2:port [
+ a lv2:InputPort ,
+ lv2:ControlPort ;
+ lv2:index 0 ;
+ lv2:symbol "input" ;
+ lv2:name "Input" ;
+ lv2:default 0.5 ;
+ lv2:minimum 0.0 ;
+ lv2:maximum 1.0 ;
+ lv2:scalePoint [ rdfs:label "off" ; rdf:value 0.0 ] ;
+ lv2:scalePoint [ rdfs:label "on" ; rdf:value 1.0 ] ;
+ ] , [
+ a lv2:OutputPort ,
+ lv2:ControlPort ;
+ lv2:index 1 ;
+ lv2:symbol "output" ;
+ lv2:name "Output"
+ ] , [
+ a lv2:AudioPort ,
+ lv2:InputPort ;
+ lv2:index 2 ;
+ lv2:symbol "audio_input" ;
+ lv2:name "Audio Input" ;
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 3 ;
+ lv2:symbol "audio_output" ;
+ lv2:name "Audio Output" ;
+ ] .
+
+<http://example.org/lilv-bindings-test-plugin-ui>
+ a ui:GtkUI ;
+ ui:binary <TODO> .
diff --git a/bindings/test/manifest.ttl.in b/bindings/test/manifest.ttl.in
new file mode 100644
index 0000000..9cc7fa8
--- /dev/null
+++ b/bindings/test/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/lilv-bindings-test-plugin>
+ a lv2:Plugin ;
+ lv2:binary <bindings_test_plugin@SHLIB_EXT@> ;
+ rdfs:seeAlso <bindings_test_plugin.ttl> .
diff --git a/bindings/test/python/test_api.py b/bindings/test/python/test_api.py
new file mode 100644
index 0000000..f594013
--- /dev/null
+++ b/bindings/test/python/test_api.py
@@ -0,0 +1,290 @@
+# Copyright 2016 David Robillard <d@drobilla.net>
+# Copyright 2013 Kaspar Emanuel <kaspar.emanuel@gmail.com>
+#
+# 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.
+
+import lilv
+import unittest
+import os
+
+location = "file://" + os.getcwd() + "/bindings/bindings_test_plugin.lv2/"
+
+class NodeTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ def testNodes(self):
+ aint = self.world.new_int(1)
+ aint2 = self.world.new_int(1)
+ aint3 = self.world.new_int(3)
+ afloat = self.world.new_float(2.0)
+ atrue = self.world.new_bool(True)
+ afalse = self.world.new_bool(False)
+ auri = self.world.new_uri("http://example.org")
+ afile = self.world.new_file_uri(None, "/foo/bar")
+ astring = self.world.new_string("hello")
+ self.assertEqual(auri.get_turtle_token(), '<http://example.org>')
+ self.assertTrue(aint.is_int())
+ self.assertTrue(afloat.is_float())
+ self.assertTrue(auri.is_uri())
+ self.assertTrue(astring.is_string())
+ self.assertTrue(astring.is_literal())
+ self.assertFalse(auri.is_blank())
+ self.assertTrue(int(aint) == 1)
+ self.assertTrue(float(afloat) == 2.0)
+ self.assertTrue(bool(atrue))
+ self.assertFalse(bool(afalse))
+ self.assertEqual(afile.get_path(), "/foo/bar")
+ self.assertTrue(aint == aint2)
+ self.assertTrue(aint != aint3)
+ self.assertTrue(aint != afloat)
+ with self.assertRaises(ValueError):
+ int(atrue)
+ with self.assertRaises(ValueError):
+ float(aint)
+ with self.assertRaises(ValueError):
+ bool(astring)
+
+class UriTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.world.load_all();
+ def testInvalidURI(self):
+ self.plugin_uri = self.world.new_uri("invalid_uri")
+ self.assertIsNone(self.plugin_uri)
+ def testNonExistentURI(self):
+ self.plugin_uri = self.world.new_uri("exist:does_not")
+ self.plugin = self.world.get_all_plugins().get_by_uri(self.plugin_uri)
+ self.assertEqual(self.plugin, None)
+ def testPortTypes(self):
+ self.assertIsNotNone(self.world.new_uri(lilv.LILV_URI_INPUT_PORT))
+ def testPortTypes2(self):
+ self.assertIsNotNone(self.world.new_uri(lilv.LILV_URI_OUTPUT_PORT))
+ def testPortTypes3(self):
+ self.assertIsNotNone(self.world.new_uri(lilv.LILV_URI_AUDIO_PORT))
+ def testPortTypes4(self):
+ self.assertIsNotNone(self.world.new_uri(lilv.LILV_URI_CONTROL_PORT))
+
+class PluginClassTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ def testPluginClasses(self):
+ pclass = self.world.get_plugin_class()
+ self.assertIsNotNone(pclass)
+ self.assertIsNone(pclass.get_parent_uri())
+ self.assertIsNotNone(pclass.get_uri())
+ self.assertIsNotNone(pclass.get_label())
+ self.assertEqual(str(pclass.get_uri()), str(pclass))
+ for i in pclass.get_children():
+ self.assertIsNotNone(i)
+ self.assertIsNotNone(i.get_uri())
+ self.assertIsNotNone(i.get_label())
+
+class PluginClassesTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.world.load_all()
+ def testPluginClasses(self):
+ classes = self.world.get_plugin_classes()
+ pclass = self.world.get_plugin_class()
+ self.assertIsNotNone(classes)
+ self.assertIsNotNone(pclass)
+ self.assertTrue(pclass in classes)
+ self.assertTrue(pclass.get_uri() in classes)
+ self.assertGreater(len(classes), 1)
+ self.assertIsNotNone(classes[0])
+ self.assertIsNotNone(classes[pclass.get_uri()])
+
+class LoadTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.bundle_uri = self.world.new_uri(location)
+ self.world.load_specifications()
+ self.world.load_plugin_classes()
+ def tearDown(self):
+ del self.world
+ def testLoadUnload(self):
+ self.world.load_bundle(self.bundle_uri)
+ plugins = self.world.get_all_plugins()
+ plugin = plugins.get(plugins.begin())
+ self.world.load_resource(plugin)
+ self.world.unload_resource(plugin)
+ self.world.unload_bundle(self.bundle_uri)
+
+class PluginTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.world.set_option(lilv.OPTION_FILTER_LANG, self.world.new_bool(True))
+ self.bundle_uri = self.world.new_uri(location)
+ self.assertIsNotNone(self.bundle_uri, "Invalid URI: '" + location + "'")
+ self.world.load_bundle(self.bundle_uri)
+ self.plugins = self.world.get_all_plugins()
+ self.plugin = self.plugins.get(self.plugins.begin())
+ self.assertTrue(self.plugin.verify())
+ self.assertTrue(self.plugin in self.plugins)
+ self.assertTrue(self.plugin.get_uri() in self.plugins)
+ self.assertEqual(self.plugins[self.plugin.get_uri()], self.plugin)
+ self.assertIsNotNone(self.plugin, msg="Test plugin not found at location: '" + location + "'")
+ self.assertEqual(location, str(self.plugin.get_bundle_uri()))
+ self.plugin_uri = self.plugin.get_uri()
+ self.assertEqual(self.plugin.get_uri(), self.plugin_uri, "URI equality broken")
+ self.instance = lilv.Instance(self.plugin, 48000, None)
+ self.assertIsNotNone(self.instance)
+ self.lv2_InputPort = self.world.new_uri(lilv.LILV_URI_INPUT_PORT)
+ self.lv2_OutputPort = self.world.new_uri(lilv.LILV_URI_OUTPUT_PORT)
+ self.lv2_AudioPort = self.world.new_uri(lilv.LILV_URI_AUDIO_PORT)
+ self.lv2_ControlPort = self.world.new_uri(lilv.LILV_URI_CONTROL_PORT)
+ def testGetters(self):
+ self.assertIsNotNone(self.plugin.get_bundle_uri())
+ self.assertGreater(len(self.plugin.get_data_uris()), 0)
+ self.assertIsNotNone(self.plugin.get_library_uri())
+ self.assertTrue(self.plugin.get_name().is_string())
+ self.assertTrue(self.plugin.get_class().get_uri().is_uri())
+ self.assertEqual(len(self.plugin.get_value(self.world.ns.doap.license)), 1)
+ licenses = self.plugin.get_value(self.world.ns.doap.license)
+ features = self.plugin.get_value(self.world.ns.lv2.optionalFeature)
+ self.assertEqual(len(licenses), 1)
+ self.assertTrue(licenses[0] in licenses)
+ with self.assertRaises(IndexError):
+ self.assertIsNone(licenses[len(licenses)])
+ self.assertEqual(len(licenses) + len(features),
+ len(licenses.merge(features)))
+ self.assertEqual(licenses.get(licenses.begin()), self.world.new_uri('http://opensource.org/licenses/isc'))
+ self.assertEqual(licenses[0], licenses.get(licenses.begin()))
+ self.assertTrue(self.plugin.has_feature(self.world.ns.lv2.hardRTCapable))
+ self.assertEqual(len(self.plugin.get_supported_features()), 1)
+ self.assertEqual(len(self.plugin.get_optional_features()), 1)
+ self.assertEqual(len(self.plugin.get_required_features()), 0)
+ self.assertFalse(self.plugin.has_extension_data(self.world.new_uri('http://example.org/nope')))
+ self.assertEqual(len(self.plugin.get_extension_data()), 0)
+ self.assertEqual(len(self.plugin.get_extension_data()), 0)
+ self.assertFalse(self.plugin.has_latency())
+ self.assertIsNone(self.plugin.get_latency_port_index())
+ def testPorts(self):
+ self.assertEqual(self.plugin.get_num_ports(), 4)
+ self.assertIsNotNone(self.plugin.get_port(0))
+ self.assertIsNotNone(self.plugin.get_port(1))
+ self.assertIsNotNone(self.plugin.get_port(2))
+ self.assertIsNotNone(self.plugin.get_port(3))
+ self.assertIsNone(self.plugin.get_port_by_index(4))
+ self.assertIsNotNone(self.plugin.get_port("input"))
+ self.assertIsNotNone(self.plugin.get_port("output"))
+ self.assertIsNotNone(self.plugin.get_port("audio_input"))
+ self.assertIsNotNone(self.plugin.get_port("audio_output"))
+ self.assertIsNone(self.plugin.get_port_by_symbol("nonexistent"))
+ self.assertIsNone(self.plugin.get_port_by_designation(self.world.ns.lv2.InputPort, self.world.ns.lv2.control))
+ self.assertIsNone(self.plugin.get_project())
+ self.assertIsNone(self.plugin.get_author_name())
+ self.assertIsNone(self.plugin.get_author_email())
+ self.assertIsNone(self.plugin.get_author_homepage())
+ self.assertFalse(self.plugin.is_replaced())
+ self.assertEqual(0, len(self.plugin.get_related(self.world.new_uri("http://example.org/Type"))))
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_InputPort, self.lv2_AudioPort))
+ port = self.plugin.get_port("input")
+ self.assertTrue(port.get_node().is_blank())
+ self.assertEqual(0, port.get(self.world.ns.lv2.index))
+ self.assertEqual(1, len(port.get_value(self.world.ns.lv2.symbol)))
+ self.assertEqual(port.get_value(self.world.ns.lv2.symbol)[0], "input")
+ self.assertFalse(port.has_property(self.world.ns.lv2.latency))
+ self.assertFalse(port.supports_event(self.world.ns.midi.MidiEvent))
+ self.assertEqual(0, port.get_index())
+ self.assertEqual("input", port.get_symbol())
+ self.assertEqual("Input", port.get_name())
+ self.assertEqual([self.world.ns.lv2.ControlPort, self.world.ns.lv2.InputPort],
+ list(port.get_classes()))
+ self.assertTrue(port.is_a(self.world.ns.lv2.ControlPort))
+ self.assertFalse(port.is_a(self.world.ns.lv2.AudioPort))
+ self.assertEquals((0.5, 0.0, 1.0), port.get_range())
+ self.assertEquals(0, len(port.get_properties()))
+ def testScalePoints(self):
+ port = self.plugin.get_port("input")
+ points = port.get_scale_points()
+ self.assertEqual(points[0].get_label(), "off")
+ self.assertEqual(points[0].get_value(), 0.0)
+ self.assertEqual(points[1].get_label(), "on")
+ self.assertEqual(points[1].get_value(), 1.0)
+ def testPortCount(self):
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_OutputPort, self.lv2_AudioPort))
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_OutputPort, self.lv2_ControlPort))
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_InputPort, self.lv2_AudioPort))
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_InputPort, self.lv2_ControlPort))
+
+class QueryTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.world.load_all()
+ self.bundle_uri = self.world.new_uri(location)
+ self.world.load_bundle(self.bundle_uri)
+ self.plugins = self.world.get_all_plugins()
+ self.plugin = self.plugins.get(self.plugins.begin())
+ def testNamespaces(self):
+ self.assertEqual(self.world.ns.lv2, "http://lv2plug.in/ns/lv2core#")
+ self.assertEqual(self.world.ns.lv2.Plugin, "http://lv2plug.in/ns/lv2core#Plugin")
+ def testQuery(self):
+ self.assertTrue(self.world.ask(None,
+ self.world.ns.rdf.type,
+ self.world.ns.lv2.Plugin))
+ self.assertLess(0, len(self.world.find_nodes(None,
+ self.world.ns.rdf.type,
+ self.world.ns.lv2.Plugin)))
+ self.assertEqual(self.plugin.get_uri(), self.world.get(None,
+ self.world.ns.rdf.type,
+ self.world.ns.lv2.Plugin))
+
+class InstanceTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.bundle_uri = self.world.new_uri(location)
+ self.world.load_bundle(self.bundle_uri)
+ self.plugins = self.world.get_all_plugins()
+ self.plugin = self.plugins[0]
+ self.instance = lilv.Instance(self.plugin, 48000)
+ self.assertEqual(self.plugin.get_uri(), self.instance.get_uri())
+ self.assertIsNone(self.instance.get_extension_data(self.world.new_uri("http://example.org/ext")))
+ self.assertIsNone(self.instance.get_extension_data("http://example.org/ext"))
+ def testRun(self):
+ import numpy
+ n_samples = 100
+ buf = numpy.zeros(n_samples)
+ with self.assertRaises(Exception):
+ self.instance.connect_port(0, "hello")
+ self.instance.connect_port(0, None)
+ self.instance.connect_port(0, None)
+ self.instance.connect_port(2, buf)
+ self.instance.connect_port(3, buf)
+ self.instance.activate()
+ self.instance.run(n_samples)
+ self.instance.deactivate()
+
+class UITests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.bundle_uri = self.world.new_uri(location)
+ self.world.load_bundle(self.bundle_uri)
+ self.plugins = self.world.get_all_plugins()
+ self.plugin = self.plugins[0]
+ def testUI(self):
+ uis = self.plugin.get_uis()
+ ui_uri = self.world.new_uri('http://example.org/lilv-bindings-test-plugin-ui')
+ self.assertEqual(1, len(uis))
+ self.assertEqual(str(uis[0]), str(ui_uri))
+ self.assertEqual(uis[0], str(ui_uri))
+ self.assertEqual(uis[0].get_uri(), ui_uri)
+ self.assertEqual(uis[0].get_bundle_uri(), self.bundle_uri)
+ self.assertEqual(uis[0].get_binary_uri(), str(self.bundle_uri) + "TODO")
+ self.assertEqual(uis[uis[0].get_uri()], uis[0])
+ self.assertTrue(uis[0].is_a(self.world.ns.ui.GtkUI))
+ self.assertTrue(uis[0] in uis)
+ self.assertTrue(uis[0].get_uri() in uis)
+ self.assertEqual([self.world.ns.ui.GtkUI], list(uis[0].get_classes()))
+ for ui in uis:
+ print(ui)
diff --git a/doc/layout.xml b/doc/layout.xml
new file mode 100644
index 0000000..74a109f
--- /dev/null
+++ b/doc/layout.xml
@@ -0,0 +1,187 @@
+<doxygenlayout version="1.0">
+ <!-- Navigation index tabs for HTML output -->
+ <navindex>
+ <tab type="mainpage" visible="yes" title=""/>
+ <tab type="pages" visible="yes" title="" intro=""/>
+ <tab type="modules" visible="yes" title="" intro=""/>
+ <tab type="namespaces" visible="yes" title="">
+ <tab type="namespacelist" visible="yes" title="" intro=""/>
+ <tab type="namespacemembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="classes" visible="yes" title="">
+ <tab type="classlist" visible="yes" title="" intro=""/>
+ <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
+ <tab type="hierarchy" visible="yes" title="" intro=""/>
+ <tab type="classmembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="files" visible="yes" title="">
+ <tab type="filelist" visible="yes" title="" intro=""/>
+ <tab type="globals" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="examples" visible="yes" title="" intro=""/>
+ </navindex>
+
+ <!-- Layout definition for a class page -->
+ <class>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <inheritancegraph visible="$CLASS_GRAPH"/>
+ <collaborationgraph visible="$COLLABORATION_GRAPH"/>
+ <allmemberslink visible="yes"/>
+ <memberdecl>
+ <nestedclasses visible="yes" title=""/>
+ <publictypes title=""/>
+ <publicslots title=""/>
+ <signals title=""/>
+ <publicmethods title=""/>
+ <publicstaticmethods title=""/>
+ <publicattributes title=""/>
+ <publicstaticattributes title=""/>
+ <protectedtypes title=""/>
+ <protectedslots title=""/>
+ <protectedmethods title=""/>
+ <protectedstaticmethods title=""/>
+ <protectedattributes title=""/>
+ <protectedstaticattributes title=""/>
+ <packagetypes title=""/>
+ <packagemethods title=""/>
+ <packagestaticmethods title=""/>
+ <packageattributes title=""/>
+ <packagestaticattributes title=""/>
+ <properties title=""/>
+ <events title=""/>
+ <privatetypes title=""/>
+ <privateslots title=""/>
+ <privatemethods title=""/>
+ <privatestaticmethods title=""/>
+ <privateattributes title=""/>
+ <privatestaticattributes title=""/>
+ <friends title=""/>
+ <related title="" subtitle=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <constructors title=""/>
+ <functions title=""/>
+ <related title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ <events title=""/>
+ </memberdef>
+ <usedfiles visible="$SHOW_USED_FILES"/>
+ <authorsection visible="yes"/>
+ </class>
+
+ <!-- Layout definition for a namespace page -->
+ <namespace>
+ <briefdescription visible="yes"/>
+ <memberdecl>
+ <nestednamespaces visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </namespace>
+
+ <!-- Layout definition for a file page -->
+ <file>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <includegraph visible="$INCLUDE_GRAPH"/>
+ <includedbygraph visible="$INCLUDED_BY_GRAPH"/>
+ <sourcelink visible="yes"/>
+ <memberdecl>
+ <classes visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection/>
+ </file>
+
+ <!-- Layout definition for a group page -->
+ <group>
+ <briefdescription visible="no"/>
+ <groupgraph visible="$GROUP_GRAPHS"/>
+ <detaileddescription title=""/>
+ <memberdecl>
+ <nestedgroups visible="yes" title=""/>
+ <dirs visible="yes" title=""/>
+ <files visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <memberdef>
+ <pagedocs/>
+ <inlineclasses title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </group>
+
+ <!-- Layout definition for a directory page -->
+ <directory>
+ <briefdescription visible="yes"/>
+ <directorygraph visible="yes"/>
+ <memberdecl>
+ <dirs visible="yes"/>
+ <files visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ </directory>
+</doxygenlayout>
diff --git a/doc/lv2apply.1 b/doc/lv2apply.1
new file mode 100644
index 0000000..4877aba
--- /dev/null
+++ b/doc/lv2apply.1
@@ -0,0 +1,34 @@
+.TH LV2APPLY 1 "05 Sep 2016"
+
+.SH NAME
+.B lv2apply \- apply an LV2 plugin to an audio file
+.SH SYNOPSIS
+.B lv2apply [OPTION]... PLUGIN_URI
+
+.SH OPTIONS
+.TP
+\fB\-i IN_FILE\fR
+Input file
+
+.TP
+\fB\-o OUT_FILE\fR
+Output file
+
+.TP
+\fB\-c SYM VAL\fR
+Set control port SYM to VAL
+
+.TP
+\fB\-\-help\fR
+Display help and exit
+
+.TP
+\fB\-\-version\fR
+Display version information and exit
+
+.SH "SEE ALSO"
+.BR lv2ls(1)
+.BR lv2info(1)
+
+.SH AUTHOR
+lv2apply was written by David Robillard <d@drobilla.net>
diff --git a/doc/lv2info.1 b/doc/lv2info.1
new file mode 100644
index 0000000..32a3bec
--- /dev/null
+++ b/doc/lv2info.1
@@ -0,0 +1,33 @@
+.TH LV2INFO 1 "05 Sep 2016"
+
+.SH NAME
+.B lv2info \- print information about an installed LV2 plugin
+.SH SYNOPSIS
+.B lv2info PLUGIN_URI
+
+.SH OPTIONS
+.TP
+\fB\-p FILE\fR
+Write Turtle description of plugin to FILE
+
+.TP
+\fB\-m FILE\fR
+Add record of plugin to manifest FILE
+
+.TP
+\fB\-\-help\fR
+Display help and exit
+
+.TP
+\fB\-\-version\fR
+Display version information and exit
+
+.SH "SEE ALSO"
+.BR lilv(3),
+.BR lv2ls(1)
+
+.SH AUTHOR
+lv2info was written by David Robillard <d@drobilla.net>
+.PP
+This manual page was written by Jaromír Mikes <mira.mikes@seznam.cz>
+and David Robillard <d@drobilla.net>
diff --git a/doc/lv2ls.1 b/doc/lv2ls.1
new file mode 100644
index 0000000..320b71c
--- /dev/null
+++ b/doc/lv2ls.1
@@ -0,0 +1,30 @@
+.TH LV2LS 1 "26 Aug 2016"
+
+.SH NAME
+.B lv2ls \- list all installed LV2 plugins
+
+.SH SYNOPSIS
+.B lv2ls [OPTION]...
+
+.SH OPTIONS
+.TP
+\fB\-n\fR, \fB\-\-names\fR
+Show names instead of URIs
+
+.TP
+\fB\-\-help\fR
+Display help and exit
+
+.TP
+\fB\-\-version\fR
+Display version information and exit
+
+.SH "SEE ALSO"
+.BR lilv(3),
+.BR lv2info(1)
+
+.SH AUTHOR
+lv2ls was written by David Robillard <d@drobilla.net>
+.PP
+This manual page was written by Jaromír Mikes <mira.mikes@seznam.cz>
+and David Robillard <d@drobilla.net>
diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in
new file mode 100644
index 0000000..e7a45d0
--- /dev/null
+++ b/doc/reference.doxygen.in
@@ -0,0 +1,2415 @@
+# Doxyfile 1.8.12
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = Lilv
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = @LILV_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = .
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = NO
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = YES
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = YES
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = YES
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = YES
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = NO
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = NO
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = NO
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = NO
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE = @LILV_SRCDIR@/doc/layout.xml
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = YES
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = @LILV_SRCDIR@/lilv/lilv.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *.c
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = NO
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET = @LILV_SRCDIR@/doc/style.css
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 160
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = YES
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 1
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME =
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = NO
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = NO
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = NO
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = NO
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = NO
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = NO
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/doc/style.css b/doc/style.css
new file mode 100644
index 0000000..f6ff8bb
--- /dev/null
+++ b/doc/style.css
@@ -0,0 +1,691 @@
+body {
+ max-width: 80em;
+ margin: 0;
+ margin-left: auto;
+ margin-right: auto;
+ background: #FFF;
+ color: #000;
+}
+
+#titlearea {
+ display: none;
+}
+
+h1 {
+ font-size: 180%;
+ font-weight: 900;
+}
+
+h2 {
+ font-size: 140%;
+ font-weight: 700;
+}
+
+h3 {
+ font-size: 120%;
+ font-weight: 700;
+}
+
+h4 {
+ font-size: 110%;
+ font-weight: 700;
+}
+
+h5 {
+ font-size: 100%;
+ font-weight: 700;
+}
+
+h6 {
+ font-size: 100%;
+ font-weight: 600;
+}
+
+p {
+ margin: 0 0 1em 0;
+}
+
+dt {
+ font-weight: 700;
+}
+
+p.startli,p.startdd,p.starttd {
+ margin-top: 2px;
+}
+
+p.endli {
+ margin-bottom: 0;
+}
+
+p.enddd {
+ margin-bottom: 4px;
+}
+
+p.endtd {
+ margin-bottom: 2px;
+}
+
+caption {
+ font-weight: 700;
+}
+
+span.legend {
+ font-size: small;
+ text-align: center;
+}
+
+h3.version {
+ font-size: small;
+ text-align: center;
+}
+
+div.qindex,div.navtab {
+ background-color: #EBEFF6;
+ border: 1px solid #A3B4D7;
+ text-align: center;
+ margin: 2px;
+ padding: 2px;
+}
+
+div.navtab {
+ margin-right: 15px;
+}
+
+/* @group Link Styling */
+a {
+ color: #546E00;
+ text-decoration: none;
+}
+
+.contents a:visited {
+ color: #344E00;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+a.qindexHL {
+ background-color: #9CAFD4;
+ color: #FFF;
+ border: 1px double #869DCA;
+}
+
+code {
+ color: #444;
+}
+
+a.code {
+ color: #4665A2;
+}
+
+a.codeRef {
+ color: #4665A2;
+}
+
+/* @end */
+dl.el {
+ margin-left: -1cm;
+}
+
+.fragment {
+ font-family: monospace, fixed;
+}
+
+pre.fragment {
+ border: 1px solid #C4C4C4;
+ background-color: #F9F9F9;
+ padding: 4px 6px;
+ margin: 4px 8px 4px 2px;
+ overflow: auto;
+ line-height: 125%;
+}
+
+div.ah {
+ background-color: #000;
+ font-weight: 700;
+ color: #FFF;
+ margin-bottom: 3px;
+ margin-top: 3px;
+ padding: .2em;
+ border: thin solid #333;
+}
+
+div.groupHeader {
+ margin-left: 16px;
+ margin-top: 12px;
+ margin-bottom: 6px;
+ font-weight: 700;
+}
+
+a + h2.groupheader {
+ display: none;
+}
+
+div.groupText {
+ margin-left: 16px;
+ font-style: italic;
+}
+
+div.contents {
+ margin-top: 10px;
+ margin-left: 10px;
+ margin-right: 10px;
+}
+
+td.indexkey {
+ background-color: #EBEFF6;
+ font-weight: 700;
+ border: 1px solid #C4CFE5;
+ margin: 2px 0;
+ padding: 2px 10px;
+}
+
+td.indexvalue {
+ background-color: #EBEFF6;
+ border: 1px solid #C4CFE5;
+ padding: 2px 10px;
+ margin: 2px 0;
+}
+
+tr.memlist {
+ background-color: #EEF1F7;
+}
+
+p.formulaDsp {
+ text-align: center;
+}
+
+img.formulaInl {
+ vertical-align: middle;
+}
+
+div.center {
+ text-align: center;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding: 0;
+}
+
+div.center img {
+ border: 0;
+}
+
+address.footer {
+ text-align: right;
+ padding-right: 12px;
+}
+
+img.footer {
+ border: 0;
+ vertical-align: middle;
+}
+
+/* @group Code Colorization */
+span.keyword {
+ color: green;
+}
+
+span.keywordtype {
+ color: #3E873E;
+}
+
+span.keywordflow {
+ color: #e08000;
+}
+
+span.comment {
+ color: maroon;
+}
+
+span.preprocessor {
+ color: #806020;
+}
+
+span.stringliteral {
+ color: #002080;
+}
+
+span.charliteral {
+ color: teal;
+}
+
+span.vhdldigit {
+ color: #F0F;
+}
+
+span.vhdlkeyword {
+ color: #700070;
+}
+
+span.vhdllogic {
+ color: red;
+}
+
+/* @end */
+td.tiny {
+ font-size: x-small;
+}
+
+.dirtab {
+ padding: 4px;
+ border-collapse: collapse;
+ border: 1px solid #A3B4D7;
+}
+
+th.dirtab {
+ background: #EBEFF6;
+ font-weight: 700;
+}
+
+hr {
+ height: 0;
+ border: none;
+ border-top: 1px solid #DDD;
+ margin: 2em 0 1em;
+}
+
+hr.footer {
+ height: 1px;
+}
+
+/* @group Member Descriptions */
+table.memberdecls {
+ border-spacing: 0.125em;
+}
+
+h2.groupheader {
+ margin: 0.5em 0 0.25em 0;
+}
+
+.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams {
+ margin: 0;
+ padding: 0;
+}
+
+.mdescLeft,.mdescRight {
+ color: #555;
+}
+
+.memItemLeft,.memItemRight,.memTemplParams {
+ border: 0;
+ font-family: monospace, fixed;
+}
+
+.memItemLeft,.memTemplItemLeft {
+ white-space: nowrap;
+ padding-left: 2em;
+ padding-right: 1em;
+}
+
+.memItemLeft a.el {
+ font-weight: bold;
+}
+
+.memTemplParams {
+ color: #464646;
+ white-space: nowrap;
+}
+
+td.memSeparator {
+ display: none;
+}
+
+td.mlabels-right {
+ vertical-align: top;
+ padding-top: 4px;
+ color: #AA6;
+}
+
+.memtitle {
+ display: none;
+}
+
+/* @end */
+/* @group Member Details */
+/* Styles for detailed member documentation */
+.memtemplate {
+ color: #4665A2;
+ font-weight: bold;
+}
+
+.memnav {
+ background-color: #EBEFF6;
+ border: 1px solid #A3B4D7;
+ text-align: center;
+ margin: 2px;
+ margin-right: 15px;
+ padding: 2px;
+}
+
+.memitem {
+ padding: 0;
+ margin: 1em 0 1em 0;
+}
+
+.memproto {
+ padding: 0;
+ font-weight: bold;
+ color: #000;
+}
+
+.memproto .paramname {
+ color: #444;
+ font-style: normal;
+}
+
+.memdoc {
+ padding: 0 0 0.5em 2em;
+}
+
+.paramkey {
+ text-align: right;
+}
+
+.paramtype {
+ color: #3E873E;
+ white-space: nowrap;
+}
+
+.paramname {
+ color: #444;
+ white-space: nowrap;
+ font-weight: bold;
+}
+
+td.paramname {
+ vertical-align: top;
+}
+
+.fieldname {
+ color: #000;
+}
+
+td.fieldname {
+ padding-right: 1em;
+ vertical-align: top;
+}
+
+td.fieldtype {
+ vertical-align: top;
+ color: #444;
+ padding-right: 0.5em;
+}
+
+td.fielddoc p {
+ margin: 0;
+}
+
+/* @end */
+/* @group Directory (tree) */
+/* for the tree view */
+.ftvtree {
+ font-family: sans-serif;
+ margin: 0;
+}
+
+/* these are for tree view when used as main index */
+.directory {
+ font-size: small;
+ margin: 0.5em;
+}
+
+.directory h3 {
+ margin: 0;
+ margin-top: 1em;
+ font-size: 11pt;
+}
+
+.directory > h3 {
+ margin-top: 0;
+}
+
+.directory p {
+ margin: 0;
+ white-space: nowrap;
+}
+
+.directory div {
+ display: none;
+ margin: 0;
+}
+
+.directory img {
+ vertical-align: -30%;
+}
+
+/* these are for tree view when not used as main index */
+.directory-alt {
+ font-size: 100%;
+ font-weight: bold;
+}
+
+.directory-alt h3 {
+ margin: 0;
+ margin-top: 1em;
+ font-size: 11pt;
+}
+
+.directory-alt > h3 {
+ margin-top: 0;
+}
+
+.directory-alt p {
+ margin: 0;
+ white-space: nowrap;
+}
+
+.directory-alt div {
+ display: none;
+ margin: 0;
+}
+
+.directory-alt img {
+ vertical-align: -30%;
+}
+
+/* @end */
+div.dynheader {
+ margin-top: 8px;
+}
+
+address {
+ font-style: normal;
+ color: #2A3D61;
+}
+
+table.doxtable {
+ border-collapse: collapse;
+ margin: 0.5em;
+}
+
+table.doxtable td,table.doxtable th {
+ border: 1px solid #DDD;
+ padding: 3px 7px 2px;
+}
+
+table.doxtable th {
+ background-color: #F3F3F3;
+ color: #000;
+ padding-bottom: 4px;
+ padding-top: 5px;
+ text-align: left;
+ font-weight: bold;
+}
+
+.tabsearch {
+ top: 0;
+ left: 10px;
+ height: 36px;
+ z-index: 101;
+ overflow: hidden;
+ font-size: 13px;
+}
+
+div.navpath {
+ padding: 0.25em;
+}
+
+.navpath ul {
+ font-size: x-small;
+ color: #8AA0CC;
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
+}
+
+.navpath li {
+ list-style-type: none;
+ float: left;
+ padding-left: 10px;
+ padding-right: 15px;
+ color: #364D7C;
+}
+
+.navpath a {
+ display: block;
+ text-decoration: none;
+ outline: none;
+}
+
+.navpath a:hover {
+ color: #6884BD;
+}
+
+div.summary {
+ float: right;
+ font-size: x-small;
+ padding: 0.25em 0.5em 0 0;
+ width: 50%;
+ text-align: right;
+}
+
+div.summary a {
+ white-space: nowrap;
+}
+
+div.header {
+ background-color: #F3F3F3;
+ margin: 0;
+ border: 0;
+}
+
+div.headertitle {
+ font-size: 180%;
+ font-weight: bold;
+ color: #FFF;
+ padding: 0.125em 0.25em 0.125em 0.25em;
+ background-color: #333;
+ background: linear-gradient(to bottom, #333 0%, #111 100%);
+ border: solid 1px #444;
+ border-top: 0;
+ border-radius: 0 0 6px 6px;
+}
+
+div.line {
+ font-family: monospace, fixed;
+ font-size: 13px;
+ min-height: 13px;
+ line-height: 1.0;
+ text-wrap: avoid;
+ white-space: pre-wrap;
+ text-indent: -53px;
+ padding-left: 53px;
+ padding-bottom: 0;
+ margin: 0;
+}
+
+.glow {
+ background-color: cyan;
+ box-shadow: 0 0 10px cyan;
+}
+
+span.lineno {
+ padding-right: 4px;
+ text-align: right;
+ border-right: 2px solid #0F0;
+ background-color: #E8E8E8;
+ white-space: pre;
+}
+span.lineno a {
+ background-color: #D8D8D8;
+}
+
+span.lineno a:hover {
+ background-color: #C8C8C8;
+}
+
+.tabs, .tabs2, .navpath {
+ background-image: none;
+ background-color: #333;
+ background: linear-gradient(to bottom, #333 0%, #111 100%);
+ border: 0;
+ border-bottom: solid 2px #000;
+ padding: 0;
+ padding-top: 2px;
+ font-size: small;
+}
+
+#navrow1 {
+ border: 0;
+}
+
+th {
+ text-align: left;
+}
+
+.mlabel {
+ padding: 0.125em;
+}
+
+/* tabs*/
+
+.tablist {
+ margin: 0;
+ padding: 0;
+ display: table;
+}
+
+.tablist li {
+ display: table-cell;
+ line-height: 2em;
+ list-style: none;
+ background-color: #333;
+ background: linear-gradient(to bottom, #444 0%, #222 100%);
+ border: 1px solid #222;
+ border-bottom: 0;
+ border-radius: 6px 6px 0 0;
+ color: #DDD;
+}
+
+.tablist a {
+ display: block;
+ padding: 0 20px;
+ font-weight: bold;
+ color: #859900;
+ text-decoration: none;
+ outline: none;
+}
+
+.header a {
+ color: #859900;
+}
+
+.tabs3 .tablist a {
+ padding: 0 10px;
+}
+
+.tablist a:hover {
+ color: #fff;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 1.0);
+ text-decoration: none;
+}
+
+.tablist li.current a {
+ color: #fff;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 1.0);
+}
+
+span.icon {
+ display: none;
+}
diff --git a/lilv.pc.in b/lilv.pc.in
new file mode 100644
index 0000000..1f3b57d
--- /dev/null
+++ b/lilv.pc.in
@@ -0,0 +1,11 @@
+prefix=@PREFIX@
+exec_prefix=@EXEC_PREFIX@
+libdir=@LIBDIR@
+includedir=@INCLUDEDIR@
+
+Name: Lilv
+Version: @LILV_VERSION@
+Description: Simple C library for hosting LV2 plugins
+Requires: lv2 @PKG_serd_0@ @PKG_sord_0@ @PKG_sratom_0@
+Libs: -L${libdir} -l@LIB_LILV@ @LILV_PKG_LIBS@
+Cflags: -I${includedir}/lilv-@LILV_MAJOR_VERSION@
diff --git a/lilv.ttl b/lilv.ttl
new file mode 100644
index 0000000..edfb0e5
--- /dev/null
+++ b/lilv.ttl
@@ -0,0 +1,29 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix : <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+
+<http://drobilla.net/software/lilv>
+ a :Project ;
+ :bug-database <http://dev.drobilla.net/query?status=new&status=assigned&status=reopened&component=LILV&order=priority> ;
+ :developer [
+ a foaf:Person ;
+ rdfs:seeAlso <http://drobilla.net/drobilla.rdf> ;
+ foaf:homepage <http://drobilla.net> ;
+ foaf:mbox_sha1sum "253b3c58086250260bac1232d744d150274ad308" ;
+ foaf:name "David Robillard"
+ ] ;
+ :download-page <http://download.drobilla.net> ;
+ :homepage <http://drobilla.net/software/lilv> ;
+ :license <http://usefulinc.com/doap/licenses/gpl> ;
+ :name "LILV" ;
+ :programming-language "C", "Turtle" ;
+ :repository [
+ :browse <http://dev.drobilla.net/browser/trunk/lilv> ;
+ :location <http://svn.drobilla.net/lad/trunk/lilv> ;
+ a :SVNRepository
+ ] ;
+ :shortdesc "Library for simple use of LV2 plugins" ;
+ :shortname "LILV" .
+
+
diff --git a/lilv/lilv.h b/lilv/lilv.h
new file mode 100644
index 0000000..f709cf1
--- /dev/null
+++ b/lilv/lilv.h
@@ -0,0 +1,1844 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file lilv.h API for Lilv, a lightweight LV2 host library.
+*/
+
+#ifndef LILV_LILV_H
+#define LILV_LILV_H
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+
+#ifdef LILV_SHARED
+# ifdef _WIN32
+# define LILV_LIB_IMPORT __declspec(dllimport)
+# define LILV_LIB_EXPORT __declspec(dllexport)
+# else
+# define LILV_LIB_IMPORT __attribute__((visibility("default")))
+# define LILV_LIB_EXPORT __attribute__((visibility("default")))
+# endif
+# ifdef LILV_INTERNAL
+# define LILV_API LILV_LIB_EXPORT
+# else
+# define LILV_API LILV_LIB_IMPORT
+# endif
+#else
+# define LILV_API
+#endif
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+# define LILV_DEPRECATED __attribute__((__deprecated__))
+#else
+# define LILV_DEPRECATED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+#define LILV_NS_DOAP "http://usefulinc.com/ns/doap#"
+#define LILV_NS_FOAF "http://xmlns.com/foaf/0.1/"
+#define LILV_NS_LILV "http://drobilla.net/ns/lilv#"
+#define LILV_NS_LV2 "http://lv2plug.in/ns/lv2core#"
+#define LILV_NS_OWL "http://www.w3.org/2002/07/owl#"
+#define LILV_NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define LILV_NS_RDFS "http://www.w3.org/2000/01/rdf-schema#"
+#define LILV_NS_XSD "http://www.w3.org/2001/XMLSchema#"
+
+#define LILV_URI_ATOM_PORT "http://lv2plug.in/ns/ext/atom#AtomPort"
+#define LILV_URI_AUDIO_PORT "http://lv2plug.in/ns/lv2core#AudioPort"
+#define LILV_URI_CONTROL_PORT "http://lv2plug.in/ns/lv2core#ControlPort"
+#define LILV_URI_CV_PORT "http://lv2plug.in/ns/lv2core#CVPort"
+#define LILV_URI_EVENT_PORT "http://lv2plug.in/ns/ext/event#EventPort"
+#define LILV_URI_INPUT_PORT "http://lv2plug.in/ns/lv2core#InputPort"
+#define LILV_URI_MIDI_EVENT "http://lv2plug.in/ns/ext/midi#MidiEvent"
+#define LILV_URI_OUTPUT_PORT "http://lv2plug.in/ns/lv2core#OutputPort"
+#define LILV_URI_PORT "http://lv2plug.in/ns/lv2core#Port"
+
+typedef struct LilvPluginImpl LilvPlugin; /**< LV2 Plugin. */
+typedef struct LilvPluginClassImpl LilvPluginClass; /**< Plugin Class. */
+typedef struct LilvPortImpl LilvPort; /**< Port. */
+typedef struct LilvScalePointImpl LilvScalePoint; /**< Scale Point. */
+typedef struct LilvUIImpl LilvUI; /**< Plugin UI. */
+typedef struct LilvNodeImpl LilvNode; /**< Typed Value. */
+typedef struct LilvWorldImpl LilvWorld; /**< Lilv World. */
+typedef struct LilvInstanceImpl LilvInstance; /**< Plugin instance. */
+typedef struct LilvStateImpl LilvState; /**< Plugin state. */
+
+typedef void LilvIter; /**< Collection iterator */
+typedef void LilvPluginClasses; /**< set<PluginClass>. */
+typedef void LilvPlugins; /**< set<Plugin>. */
+typedef void LilvScalePoints; /**< set<ScalePoint>. */
+typedef void LilvUIs; /**< set<UI>. */
+typedef void LilvNodes; /**< set<Node>. */
+
+/**
+ @defgroup lilv Lilv
+ Lilv is a simple yet powerful C API for using LV2 plugins.
+
+ For more information about LV2, see <http://lv2plug.in>.
+ For more information about Lilv, see <http://drobilla.net/software/lilv>.
+ @{
+*/
+
+/**
+ Free memory allocated by Lilv.
+
+ This function exists because some systems require memory allocated by a
+ library to be freed by code in the same library. It is otherwise equivalent
+ to the standard C free() function.
+*/
+LILV_API void
+lilv_free(void* ptr);
+
+/**
+ @name Node
+ @{
+*/
+
+/**
+ Convert a file URI string to a local path string.
+ For example, "file://foo/bar/baz.ttl" returns "/foo/bar/baz.ttl".
+ Return value is shared and must not be deleted by caller.
+ This function does not handle escaping correctly and should not be used for
+ general file URIs. Use lilv_file_uri_parse() instead.
+ @return `uri` converted to a path, or NULL on failure (URI is not local).
+*/
+LILV_API LILV_DEPRECATED const char*
+lilv_uri_to_path(const char* uri);
+
+/**
+ Convert a file URI string to a local path string.
+ For example, "file://foo/bar%20one/baz.ttl" returns "/foo/bar one/baz.ttl".
+ Return value must be freed by caller with lilv_free().
+ @param uri The file URI to parse.
+ @param hostname If non-NULL, set to the hostname in the URI, if any.
+ @return `uri` converted to a path, or NULL on failure (URI is not local).
+*/
+LILV_API char*
+lilv_file_uri_parse(const char* uri, char** hostname);
+
+/**
+ Create a new URI value.
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_uri(LilvWorld* world, const char* uri);
+
+/**
+ Create a new file URI value.
+ @param world The world.
+ @param host Host name, or NULL.
+ @param path Path on host.
+ @return A new node that must be freed by caller.
+
+ Relative paths are resolved against the current working directory. Note
+ that this may yield unexpected results if `host` is another machine.
+*/
+LILV_API LilvNode*
+lilv_new_file_uri(LilvWorld* world, const char* host, const char* path);
+
+/**
+ Create a new string value (with no language).
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_string(LilvWorld* world, const char* str);
+
+/**
+ Create a new integer value.
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_int(LilvWorld* world, int val);
+
+/**
+ Create a new floating point value.
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_float(LilvWorld* world, float val);
+
+/**
+ Create a new boolean value.
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_bool(LilvWorld* world, bool val);
+
+/**
+ Free a LilvNode.
+ It is safe to call this function on NULL.
+*/
+LILV_API void
+lilv_node_free(LilvNode* val);
+
+/**
+ Duplicate a LilvNode.
+*/
+LILV_API LilvNode*
+lilv_node_duplicate(const LilvNode* val);
+
+/**
+ Return whether two values are equivalent.
+*/
+LILV_API bool
+lilv_node_equals(const LilvNode* value, const LilvNode* other);
+
+/**
+ Return this value as a Turtle/SPARQL token.
+ Returned value must be freed by caller with lilv_free().
+ <table>
+ <caption>Example Turtle Tokens</caption>
+ <tr><th>URI</th><td>&lt;http://example.org/foo &gt;</td></tr>
+ <tr><th>QName</th><td>doap:name</td></tr>
+ <tr><th>String</th><td>"this is a string"</td></tr>
+ <tr><th>Float</th><td>1.0</td></tr>
+ <tr><th>Integer</th><td>1</td></tr>
+ <tr><th>Boolean</th><td>true</td></tr>
+ </table>
+*/
+LILV_API char*
+lilv_node_get_turtle_token(const LilvNode* value);
+
+/**
+ Return whether the value is a URI (resource).
+*/
+LILV_API bool
+lilv_node_is_uri(const LilvNode* value);
+
+/**
+ Return this value as a URI string, e.g. "http://example.org/foo".
+ Valid to call only if `lilv_node_is_uri(value)` returns true.
+ Returned value is owned by `value` and must not be freed by caller.
+*/
+LILV_API const char*
+lilv_node_as_uri(const LilvNode* value);
+
+/**
+ Return whether the value is a blank node (resource with no URI).
+*/
+LILV_API bool
+lilv_node_is_blank(const LilvNode* value);
+
+/**
+ Return this value as a blank node identifier, e.g. "genid03".
+ Valid to call only if `lilv_node_is_blank(value)` returns true.
+ Returned value is owned by `value` and must not be freed by caller.
+*/
+LILV_API const char*
+lilv_node_as_blank(const LilvNode* value);
+
+/**
+ Return whether this value is a literal (i.e. not a URI).
+ Returns true if `value` is a string or numeric value.
+*/
+LILV_API bool
+lilv_node_is_literal(const LilvNode* value);
+
+/**
+ Return whether this value is a string literal.
+ Returns true if `value` is a string value (and not numeric).
+*/
+LILV_API bool
+lilv_node_is_string(const LilvNode* value);
+
+/**
+ Return `value` as a string.
+*/
+LILV_API const char*
+lilv_node_as_string(const LilvNode* value);
+
+/**
+ Return the path of a file URI node.
+ Returns NULL if `value` is not a file URI.
+ Returned value must be freed by caller with lilv_free().
+*/
+LILV_API char*
+lilv_node_get_path(const LilvNode* value, char** hostname);
+
+/**
+ Return whether this value is a decimal literal.
+*/
+LILV_API bool
+lilv_node_is_float(const LilvNode* value);
+
+/**
+ Return `value` as a float.
+ Valid to call only if `lilv_node_is_float(value)` or
+ `lilv_node_is_int(value)` returns true.
+*/
+LILV_API float
+lilv_node_as_float(const LilvNode* value);
+
+/**
+ Return whether this value is an integer literal.
+*/
+LILV_API bool
+lilv_node_is_int(const LilvNode* value);
+
+/**
+ Return `value` as an integer.
+ Valid to call only if `lilv_node_is_int(value)` returns true.
+*/
+LILV_API int
+lilv_node_as_int(const LilvNode* value);
+
+/**
+ Return whether this value is a boolean.
+*/
+LILV_API bool
+lilv_node_is_bool(const LilvNode* value);
+
+/**
+ Return `value` as a bool.
+ Valid to call only if `lilv_node_is_bool(value)` returns true.
+*/
+LILV_API bool
+lilv_node_as_bool(const LilvNode* value);
+
+/**
+ @}
+ @name Collections
+ Lilv has several collection types for holding various types of value:
+ <ul>
+ <li>LilvPlugins (function prefix "lilv_plugins_")</li>
+ <li>LilvPluginClasses (function prefix "lilv_plugin_classes_")</li>
+ <li>LilvScalePoints (function prefix "lilv_scale_points_")</li>
+ <li>LilvNodes (function prefix "lilv_nodes_")</li>
+ <li>LilvUIs (function prefix "lilv_uis_")</li>
+ </ul>
+
+ Each collection type supports a similar basic API (except LilvPlugins which
+ is internal and thus lacks a free function):
+ <ul>
+ <li>void PREFIX_free (coll)</li>
+ <li>unsigned PREFIX_size (coll)</li>
+ <li>LilvIter* PREFIX_begin (coll)</li>
+ </ul>
+ @{
+*/
+
+/* Collections */
+
+/**
+ Iterate over each element of a collection.
+ @code
+ LILV_FOREACH(plugin_classes, i, classes) {
+ LilvPluginClass c = lilv_plugin_classes_get(classes, i);
+ // ...
+ }
+ @endcode
+*/
+#define LILV_FOREACH(colltype, iter, collection) \
+ for (LilvIter* iter = lilv_ ## colltype ## _begin(collection); \
+ !lilv_ ## colltype ## _is_end(collection, iter); \
+ (iter) = lilv_ ## colltype ## _next(collection, iter))
+
+/* LilvPluginClasses */
+
+LILV_API void
+lilv_plugin_classes_free(LilvPluginClasses* collection);
+
+LILV_API unsigned
+lilv_plugin_classes_size(const LilvPluginClasses* collection);
+
+LILV_API LilvIter*
+lilv_plugin_classes_begin(const LilvPluginClasses* collection);
+
+LILV_API const LilvPluginClass*
+lilv_plugin_classes_get(const LilvPluginClasses* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_plugin_classes_next(const LilvPluginClasses* collection, LilvIter* i);
+
+LILV_API bool
+lilv_plugin_classes_is_end(const LilvPluginClasses* collection, LilvIter* i);
+
+/**
+ Get a plugin class from `classes` by URI.
+ Return value is shared (stored in `classes`) and must not be freed or
+ modified by the caller in any way.
+ @return NULL if no plugin class with `uri` is found in `classes`.
+*/
+LILV_API const LilvPluginClass*
+lilv_plugin_classes_get_by_uri(const LilvPluginClasses* classes,
+ const LilvNode* uri);
+
+/* ScalePoints */
+
+LILV_API void
+lilv_scale_points_free(LilvScalePoints* collection);
+
+LILV_API unsigned
+lilv_scale_points_size(const LilvScalePoints* collection);
+
+LILV_API LilvIter*
+lilv_scale_points_begin(const LilvScalePoints* collection);
+
+LILV_API const LilvScalePoint*
+lilv_scale_points_get(const LilvScalePoints* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_scale_points_next(const LilvScalePoints* collection, LilvIter* i);
+
+LILV_API bool
+lilv_scale_points_is_end(const LilvScalePoints* collection, LilvIter* i);
+
+/* UIs */
+
+LILV_API void
+lilv_uis_free(LilvUIs* collection);
+
+LILV_API unsigned
+lilv_uis_size(const LilvUIs* collection);
+
+LILV_API LilvIter*
+lilv_uis_begin(const LilvUIs* collection);
+
+LILV_API const LilvUI*
+lilv_uis_get(const LilvUIs* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_uis_next(const LilvUIs* collection, LilvIter* i);
+
+LILV_API bool
+lilv_uis_is_end(const LilvUIs* collection, LilvIter* i);
+
+/**
+ Get a UI from `uis` by URI.
+ Return value is shared (stored in `uis`) and must not be freed or
+ modified by the caller in any way.
+ @return NULL if no UI with `uri` is found in `list`.
+*/
+LILV_API const LilvUI*
+lilv_uis_get_by_uri(const LilvUIs* uis,
+ const LilvNode* uri);
+
+/* Nodes */
+
+LILV_API void
+lilv_nodes_free(LilvNodes* collection);
+
+LILV_API unsigned
+lilv_nodes_size(const LilvNodes* collection);
+
+LILV_API LilvIter*
+lilv_nodes_begin(const LilvNodes* collection);
+
+LILV_API const LilvNode*
+lilv_nodes_get(const LilvNodes* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_nodes_next(const LilvNodes* collection, LilvIter* i);
+
+LILV_API bool
+lilv_nodes_is_end(const LilvNodes* collection, LilvIter* i);
+
+LILV_API LilvNode*
+lilv_nodes_get_first(const LilvNodes* collection);
+
+/**
+ Return whether `values` contains `value`.
+*/
+LILV_API bool
+lilv_nodes_contains(const LilvNodes* nodes, const LilvNode* value);
+
+/**
+ Return a new LilvNodes that contains all nodes from both `a` and `b`.
+*/
+LILV_API LilvNodes*
+lilv_nodes_merge(const LilvNodes* a, const LilvNodes* b);
+
+/* Plugins */
+
+LILV_API unsigned
+lilv_plugins_size(const LilvPlugins* collection);
+
+LILV_API LilvIter*
+lilv_plugins_begin(const LilvPlugins* collection);
+
+LILV_API const LilvPlugin*
+lilv_plugins_get(const LilvPlugins* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_plugins_next(const LilvPlugins* collection, LilvIter* i);
+
+LILV_API bool
+lilv_plugins_is_end(const LilvPlugins* collection, LilvIter* i);
+
+/**
+ Get a plugin from `plugins` by URI.
+ Return value is shared (stored in `plugins`) and must not be freed or
+ modified by the caller in any way.
+ @return NULL if no plugin with `uri` is found in `plugins`.
+*/
+LILV_API const LilvPlugin*
+lilv_plugins_get_by_uri(const LilvPlugins* plugins,
+ const LilvNode* uri);
+
+/**
+ @}
+ @name World
+ The "world" represents all Lilv state, and is used to discover/load/cache
+ LV2 data (plugins, UIs, and extensions).
+ Normal hosts which just need to load plugins by URI should simply use
+ lilv_world_load_all() to discover/load the system's LV2 resources.
+ @{
+*/
+
+/**
+ Initialize a new, empty world.
+ If initialization fails, NULL is returned.
+*/
+LILV_API LilvWorld*
+lilv_world_new(void);
+
+/**
+ Enable/disable language filtering.
+ Language filtering applies to any functions that return (a) value(s).
+ With filtering enabled, Lilv will automatically return the best value(s)
+ for the current LANG. With filtering disabled, all matching values will
+ be returned regardless of language tag. Filtering is enabled by default.
+*/
+#define LILV_OPTION_FILTER_LANG "http://drobilla.net/ns/lilv#filter-lang"
+
+/**
+ Enable/disable dynamic manifest support.
+ Dynamic manifest data will only be loaded if this option is true.
+*/
+#define LILV_OPTION_DYN_MANIFEST "http://drobilla.net/ns/lilv#dyn-manifest"
+
+/**
+ Set an option option for `world`.
+
+ Currently recognized options:
+ @ref LILV_OPTION_FILTER_LANG
+ @ref LILV_OPTION_DYN_MANIFEST
+*/
+LILV_API void
+lilv_world_set_option(LilvWorld* world,
+ const char* uri,
+ const LilvNode* value);
+
+/**
+ Destroy the world, mwahaha.
+ It is safe to call this function on NULL.
+ Note that destroying `world` will destroy all the objects it contains
+ (e.g. instances of LilvPlugin). Do not destroy the world until you are
+ finished with all objects that came from it.
+*/
+LILV_API void
+lilv_world_free(LilvWorld* world);
+
+/**
+ Load all installed LV2 bundles on the system.
+ This is the recommended way for hosts to load LV2 data. It implements the
+ established/standard best practice for discovering all LV2 data on the
+ system. The environment variable LV2_PATH may be used to control where
+ this function will look for bundles.
+
+ Hosts should use this function rather than explicitly load bundles, except
+ in special circumstances (e.g. development utilities, or hosts that ship
+ with special plugin bundles which are installed to a known location).
+*/
+LILV_API void
+lilv_world_load_all(LilvWorld* world);
+
+/**
+ Load a specific bundle.
+ `bundle_uri` must be a fully qualified URI to the bundle directory,
+ with the trailing slash, eg. file:///usr/lib/lv2/foo.lv2/
+
+ Normal hosts should not need this function (use lilv_world_load_all()).
+
+ Hosts MUST NOT attach any long-term significance to bundle paths
+ (e.g. in save files), since there are no guarantees they will remain
+ unchanged between (or even during) program invocations. Plugins (among
+ other things) MUST be identified by URIs (not paths) in save files.
+*/
+LILV_API void
+lilv_world_load_bundle(LilvWorld* world,
+ const LilvNode* bundle_uri);
+
+/**
+ Load all specifications from currently loaded bundles.
+
+ This is for hosts that explicitly load specific bundles, its use is not
+ necessary when using lilv_world_load_all(). This function parses the
+ specifications and adds them to the model.
+*/
+LILV_API void
+lilv_world_load_specifications(LilvWorld* world);
+
+/**
+ Load all plugin classes from currently loaded specifications.
+
+ Must be called after lilv_world_load_specifications(). This is for hosts
+ that explicitly load specific bundles, its use is not necessary when using
+ lilv_world_load_all().
+*/
+LILV_API void
+lilv_world_load_plugin_classes(LilvWorld* world);
+
+/**
+ Unload a specific bundle.
+
+ This unloads statements loaded by lilv_world_load_bundle(). Note that this
+ is not necessarily all information loaded from the bundle. If any resources
+ have been separately loaded with lilv_world_load_resource(), they must be
+ separately unloaded with lilv_world_unload_resource().
+*/
+LILV_API int
+lilv_world_unload_bundle(LilvWorld* world, const LilvNode* bundle_uri);
+
+/**
+ Load all the data associated with the given `resource`.
+ @param world The world.
+ @param resource Must be a subject (i.e. a URI or a blank node).
+ @return The number of files parsed, or -1 on error
+
+ All accessible data files linked to `resource` with rdfs:seeAlso will be
+ loaded into the world model.
+*/
+LILV_API int
+lilv_world_load_resource(LilvWorld* world,
+ const LilvNode* resource);
+
+/**
+ Unload all the data associated with the given `resource`.
+ @param world The world.
+ @param resource Must be a subject (i.e. a URI or a blank node).
+
+ This unloads all data loaded by a previous call to
+ lilv_world_load_resource() with the given `resource`.
+*/
+LILV_API int
+lilv_world_unload_resource(LilvWorld* world,
+ const LilvNode* resource);
+
+/**
+ Get the parent of all other plugin classes, lv2:Plugin.
+*/
+LILV_API const LilvPluginClass*
+lilv_world_get_plugin_class(const LilvWorld* world);
+
+/**
+ Return a list of all found plugin classes.
+ Returned list is owned by world and must not be freed by the caller.
+*/
+LILV_API const LilvPluginClasses*
+lilv_world_get_plugin_classes(const LilvWorld* world);
+
+/**
+ Return a list of all found plugins.
+ The returned list contains just enough references to query
+ or instantiate plugins. The data for a particular plugin will not be
+ loaded into memory until a call to an lilv_plugin_* function results in
+ a query (at which time the data is cached with the LilvPlugin so future
+ queries are very fast).
+
+ The returned list and the plugins it contains are owned by `world`
+ and must not be freed by caller.
+*/
+LILV_API const LilvPlugins*
+lilv_world_get_all_plugins(const LilvWorld* world);
+
+/**
+ Find nodes matching a triple pattern.
+ Either `subject` or `object` may be NULL (i.e. a wildcard), but not both.
+ @return All matches for the wildcard field, or NULL.
+*/
+LILV_API LilvNodes*
+lilv_world_find_nodes(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object);
+
+/**
+ Find a single node that matches a pattern.
+ Exactly one of `subject`, `predicate`, `object` must be NULL.
+ This function is equivalent to
+ lilv_nodes_get_first(lilv_world_find_nodes(...)) but simplifies the common
+ case of only wanting a single value.
+ @return the first matching node, or NULL if no matches are found.
+*/
+LILV_API LilvNode*
+lilv_world_get(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object);
+
+/**
+ Return true iff a statement matching a certain pattern exists.
+
+ This is useful for checking if particular statement exists without having to
+ bother with collections and memory management.
+
+ @param world The world.
+ @param subject Subject of statement, or NULL for anything.
+ @param predicate Predicate (key) of statement, or NULL for anything.
+ @param object Object (value) of statement, or NULL for anything.
+*/
+LILV_API bool
+lilv_world_ask(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object);
+
+/**
+ Get an LV2 symbol for some subject.
+
+ This will return the lv2:symbol property of the subject if it is given
+ explicitly, and otherwise will attempt to derive a symbol from the URI.
+ @return A string node that is a valid LV2 symbol, or NULL on error.
+*/
+LILV_API LilvNode*
+lilv_world_get_symbol(LilvWorld* world, const LilvNode* subject);
+
+/**
+ @}
+ @name Plugin
+ @{
+*/
+
+/**
+ Check if `plugin` is valid.
+ This is not a rigorous validator, but can be used to reject some malformed
+ plugins that could cause bugs (e.g. plugins with missing required fields).
+
+ Note that normal hosts do NOT need to use this - lilv does not
+ load invalid plugins into plugin lists. This is included for plugin
+ testing utilities, etc.
+ @return true iff `plugin` is valid.
+*/
+LILV_API bool
+lilv_plugin_verify(const LilvPlugin* plugin);
+
+/**
+ Get the URI of `plugin`.
+ Any serialization that refers to plugins should refer to them by this.
+ Hosts SHOULD NOT save any filesystem paths, plugin indexes, etc. in saved
+ files; save only the URI.
+
+ The URI is a globally unique identifier for one specific plugin. Two
+ plugins with the same URI are compatible in port signature, and should
+ be guaranteed to work in a compatible and consistent way. If a plugin
+ is upgraded in an incompatible way (eg if it has different ports), it
+ MUST have a different URI than it's predecessor.
+
+ @return A shared URI value which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_plugin_get_uri(const LilvPlugin* plugin);
+
+/**
+ Get the (resolvable) URI of the plugin's "main" bundle.
+ This returns the URI of the bundle where the plugin itself was found. Note
+ that the data for a plugin may be spread over many bundles, that is,
+ lilv_plugin_get_data_uris() may return URIs which are not within this
+ bundle.
+
+ Typical hosts should not need to use this function.
+ Note this always returns a fully qualified URI. If you want a local
+ filesystem path, use lilv_file_uri_parse().
+ @return a shared string which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_plugin_get_bundle_uri(const LilvPlugin* plugin);
+
+/**
+ Get the (resolvable) URIs of the RDF data files that define a plugin.
+ Typical hosts should not need to use this function.
+ Note this always returns fully qualified URIs. If you want local
+ filesystem paths, use lilv_file_uri_parse().
+ @return a list of complete URLs eg. "file:///foo/ABundle.lv2/aplug.ttl",
+ which is shared and must not be modified or freed.
+*/
+LILV_API const LilvNodes*
+lilv_plugin_get_data_uris(const LilvPlugin* plugin);
+
+/**
+ Get the (resolvable) URI of the shared library for `plugin`.
+ Note this always returns a fully qualified URI. If you want a local
+ filesystem path, use lilv_file_uri_parse().
+ @return a shared string which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_plugin_get_library_uri(const LilvPlugin* plugin);
+
+/**
+ Get the name of `plugin`.
+ This returns the name (doap:name) of the plugin. The name may be
+ translated according to the current locale, this value MUST NOT be used
+ as a plugin identifier (use the URI for that).
+ Returned value must be freed by the caller.
+*/
+LILV_API LilvNode*
+lilv_plugin_get_name(const LilvPlugin* plugin);
+
+/**
+ Get the class this plugin belongs to (e.g. Filters).
+*/
+LILV_API const LilvPluginClass*
+lilv_plugin_get_class(const LilvPlugin* plugin);
+
+/**
+ Get a value associated with the plugin in a plugin's data files.
+ `predicate` must be either a URI or a QName.
+
+ Returns the ?object of all triples found of the form:
+
+ <code>&lt;plugin-uri&gt; predicate ?object</code>
+
+ May return NULL if the property was not found, or if object(s) is not
+ sensibly represented as a LilvNodes (e.g. blank nodes).
+ Return value must be freed by caller with lilv_nodes_free().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_value(const LilvPlugin* plugin,
+ const LilvNode* predicate);
+
+/**
+ Return whether a feature is supported by a plugin.
+ This will return true if the feature is an optional or required feature
+ of the plugin.
+*/
+LILV_API bool
+lilv_plugin_has_feature(const LilvPlugin* plugin,
+ const LilvNode* feature);
+
+/**
+ Get the LV2 Features supported (required or optionally) by a plugin.
+ A feature is "supported" by a plugin if it is required OR optional.
+
+ Since required features have special rules the host must obey, this function
+ probably shouldn't be used by normal hosts. Using lilv_plugin_get_optional_features()
+ and lilv_plugin_get_required_features() separately is best in most cases.
+
+ Returned value must be freed by caller with lilv_nodes_free().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_supported_features(const LilvPlugin* plugin);
+
+/**
+ Get the LV2 Features required by a plugin.
+ If a feature is required by a plugin, hosts MUST NOT use the plugin if they do not
+ understand (or are unable to support) that feature.
+
+ All values returned here MUST be passed to the plugin's instantiate method
+ (along with data, if necessary, as defined by the feature specification)
+ or plugin instantiation will fail.
+
+ Return value must be freed by caller with lilv_nodes_free().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_required_features(const LilvPlugin* plugin);
+
+/**
+ Get the LV2 Features optionally supported by a plugin.
+ Hosts MAY ignore optional plugin features for whatever reasons. Plugins
+ MUST operate (at least somewhat) if they are instantiated without being
+ passed optional features.
+
+ Return value must be freed by caller with lilv_nodes_free().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_optional_features(const LilvPlugin* plugin);
+
+/**
+ Return whether or not a plugin provides a specific extension data.
+*/
+LILV_API bool
+lilv_plugin_has_extension_data(const LilvPlugin* plugin,
+ const LilvNode* uri);
+
+/**
+ Get a sequence of all extension data provided by a plugin.
+ This can be used to find which URIs lilv_instance_get_extension_data()
+ will return a value for without instantiating the plugin.
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_extension_data(const LilvPlugin* plugin);
+
+/**
+ Get the number of ports on this plugin.
+*/
+LILV_API uint32_t
+lilv_plugin_get_num_ports(const LilvPlugin* plugin);
+
+/**
+ Get the port ranges (minimum, maximum and default values) for all ports.
+ `min_values`, `max_values` and `def_values` must either point to an array
+ of N floats, where N is the value returned by lilv_plugin_get_num_ports()
+ for this plugin, or NULL. The elements of the array will be set to the
+ the minimum, maximum and default values of the ports on this plugin,
+ with array index corresponding to port index. If a port doesn't have a
+ minimum, maximum or default value, or the port's type is not float, the
+ corresponding array element will be set to NAN.
+
+ This is a convenience method for the common case of getting the range of
+ all float ports on a plugin, and may be significantly faster than
+ repeated calls to lilv_port_get_range().
+*/
+LILV_API void
+lilv_plugin_get_port_ranges_float(const LilvPlugin* plugin,
+ float* min_values,
+ float* max_values,
+ float* def_values);
+
+/**
+ Get the number of ports on this plugin that are members of some class(es).
+ Note that this is a varargs function so ports fitting any type 'profile'
+ desired can be found quickly. REMEMBER TO TERMINATE THE PARAMETER LIST
+ OF THIS FUNCTION WITH NULL OR VERY NASTY THINGS WILL HAPPEN.
+*/
+LILV_API uint32_t
+lilv_plugin_get_num_ports_of_class(const LilvPlugin* plugin,
+ const LilvNode* class_1, ...);
+
+#ifndef SWIG
+/**
+ Variant of lilv_plugin_get_num_ports_of_class() that takes a va_list.
+
+ This function calls va_arg() on `args` but does not call va_end().
+*/
+LILV_API uint32_t
+lilv_plugin_get_num_ports_of_class_va(const LilvPlugin* plugin,
+ const LilvNode* class_1,
+ va_list args);
+#endif
+
+/**
+ Return whether or not the plugin introduces (and reports) latency.
+ The index of the latency port can be found with
+ lilv_plugin_get_latency_port() ONLY if this function returns true.
+*/
+LILV_API bool
+lilv_plugin_has_latency(const LilvPlugin* plugin);
+
+/**
+ Return the index of the plugin's latency port.
+ It is a fatal error to call this on a plugin without checking if the port
+ exists by first calling lilv_plugin_has_latency().
+
+ Any plugin that introduces unwanted latency that should be compensated for
+ (by hosts with the ability/need) MUST provide this port, which is a control
+ rate output port that reports the latency for each cycle in frames.
+*/
+LILV_API uint32_t
+lilv_plugin_get_latency_port_index(const LilvPlugin* plugin);
+
+/**
+ Get a port on `plugin` by `index`.
+*/
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_index(const LilvPlugin* plugin,
+ uint32_t index);
+
+/**
+ Get a port on `plugin` by `symbol`.
+ Note this function is slower than lilv_plugin_get_port_by_index(),
+ especially on plugins with a very large number of ports.
+*/
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_symbol(const LilvPlugin* plugin,
+ const LilvNode* symbol);
+
+/**
+ Get a port on `plugin` by its lv2:designation.
+
+ The designation of a port describes the meaning, assignment, allocation or
+ role of the port, e.g. "left channel" or "gain". If found, the port with
+ matching `port_class` and `designation` is be returned, otherwise NULL is
+ returned. The `port_class` can be used to distinguish the input and output
+ ports for a particular designation. If `port_class` is NULL, any port with
+ the given designation will be returned.
+*/
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_designation(const LilvPlugin* plugin,
+ const LilvNode* port_class,
+ const LilvNode* designation);
+
+/**
+ Get the project the plugin is a part of.
+
+ More information about the project can be read via lilv_world_find_nodes(),
+ typically using properties from DOAP (e.g. doap:name).
+*/
+LILV_API LilvNode*
+lilv_plugin_get_project(const LilvPlugin* plugin);
+
+/**
+ Get the full name of the plugin's author.
+ Returns NULL if author name is not present.
+ Returned value must be freed by caller.
+*/
+LILV_API LilvNode*
+lilv_plugin_get_author_name(const LilvPlugin* plugin);
+
+/**
+ Get the email address of the plugin's author.
+ Returns NULL if author email address is not present.
+ Returned value must be freed by caller.
+*/
+LILV_API LilvNode*
+lilv_plugin_get_author_email(const LilvPlugin* plugin);
+
+/**
+ Get the address of the plugin author's home page.
+ Returns NULL if author homepage is not present.
+ Returned value must be freed by caller.
+*/
+LILV_API LilvNode*
+lilv_plugin_get_author_homepage(const LilvPlugin* plugin);
+
+/**
+ Return true iff `plugin` has been replaced by another plugin.
+
+ The plugin will still be usable, but hosts should hide them from their
+ user interfaces to prevent users from using deprecated plugins.
+*/
+LILV_API bool
+lilv_plugin_is_replaced(const LilvPlugin* plugin);
+
+/**
+ Write the Turtle description of `plugin` to `plugin_file`.
+
+ This function is particularly useful for porting plugins in conjunction with
+ an LV2 bridge such as NASPRO.
+*/
+LILV_API void
+lilv_plugin_write_description(LilvWorld* world,
+ const LilvPlugin* plugin,
+ const LilvNode* base_uri,
+ FILE* plugin_file);
+
+/**
+ Write a manifest entry for `plugin` to `manifest_file`.
+
+ This function is intended for use with lilv_plugin_write_description() to
+ write a complete description of a plugin to a bundle.
+*/
+LILV_API void
+lilv_plugin_write_manifest_entry(LilvWorld* world,
+ const LilvPlugin* plugin,
+ const LilvNode* base_uri,
+ FILE* manifest_file,
+ const char* plugin_file_path);
+
+/**
+ Get the resources related to `plugin` with lv2:appliesTo.
+
+ Some plugin-related resources are not linked directly to the plugin with
+ rdfs:seeAlso and thus will not be automatically loaded along with the plugin
+ data (usually for performance reasons). All such resources of the given @c
+ type related to `plugin` can be accessed with this function.
+
+ If `type` is NULL, all such resources will be returned, regardless of type.
+
+ To actually load the data for each returned resource, use
+ lilv_world_load_resource().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type);
+
+/**
+ @}
+ @name Port
+ @{
+*/
+
+/**
+ Get the RDF node of `port`.
+
+ Ports nodes may be may be URIs or blank nodes.
+
+ @return A shared node which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_port_get_node(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Port analog of lilv_plugin_get_value().
+*/
+LILV_API LilvNodes*
+lilv_port_get_value(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* predicate);
+
+/**
+ Get a single property value of a port.
+
+ This is equivalent to lilv_nodes_get_first(lilv_port_get_value(...)) but is
+ simpler to use in the common case of only caring about one value. The
+ caller is responsible for freeing the returned node.
+*/
+LILV_API LilvNode*
+lilv_port_get(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* predicate);
+
+/**
+ Return the LV2 port properties of a port.
+*/
+LILV_API LilvNodes*
+lilv_port_get_properties(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Return whether a port has a certain property.
+*/
+LILV_API bool
+lilv_port_has_property(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* property);
+
+/**
+ Return whether a port supports a certain event type.
+
+ More precisely, this returns true iff the port has an atom:supports or an
+ ev:supportsEvent property with `event_type` as the value.
+*/
+LILV_API bool
+lilv_port_supports_event(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* event_type);
+
+/**
+ Get the index of a port.
+ The index is only valid for the life of the plugin and may change between
+ versions. For a stable identifier, use the symbol.
+*/
+LILV_API uint32_t
+lilv_port_get_index(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Get the symbol of a port.
+ The 'symbol' is a short string, a valid C identifier.
+ Returned value is owned by `port` and must not be freed.
+*/
+LILV_API const LilvNode*
+lilv_port_get_symbol(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Get the name of a port.
+ This is guaranteed to return the untranslated name (the doap:name in the
+ data file without a language tag). Returned value must be freed by
+ the caller.
+*/
+LILV_API LilvNode*
+lilv_port_get_name(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Get all the classes of a port.
+ This can be used to determine if a port is an input, output, audio,
+ control, midi, etc, etc, though it's simpler to use lilv_port_is_a().
+ The returned list does not include lv2:Port, which is implied.
+ Returned value is shared and must not be destroyed by caller.
+*/
+LILV_API const LilvNodes*
+lilv_port_get_classes(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Determine if a port is of a given class (input, output, audio, etc).
+ For convenience/performance/extensibility reasons, hosts are expected to
+ create a LilvNode for each port class they "care about". Well-known type
+ URI strings are defined (e.g. LILV_URI_INPUT_PORT) for convenience, but
+ this function is designed so that Lilv is usable with any port types
+ without requiring explicit support in Lilv.
+*/
+LILV_API bool
+lilv_port_is_a(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* port_class);
+
+/**
+ Get the default, minimum, and maximum values of a port.
+
+ `def`, `min`, and `max` are outputs, pass pointers to uninitialized
+ LilvNode* variables. These will be set to point at new values (which must
+ be freed by the caller using lilv_node_free()), or NULL if the value does
+ not exist.
+*/
+LILV_API void
+lilv_port_get_range(const LilvPlugin* plugin,
+ const LilvPort* port,
+ LilvNode** def,
+ LilvNode** min,
+ LilvNode** max);
+
+/**
+ Get the scale points (enumeration values) of a port.
+ This returns a collection of 'interesting' named values of a port
+ (e.g. appropriate entries for a UI selector associated with this port).
+ Returned value may be NULL if `port` has no scale points, otherwise it
+ must be freed by caller with lilv_scale_points_free().
+*/
+LILV_API LilvScalePoints*
+lilv_port_get_scale_points(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ @}
+ @name Plugin State
+ @{
+*/
+
+/**
+ Load a state snapshot from the world RDF model.
+ This function can be used to load the default state of a plugin by passing
+ the plugin URI as the `subject` parameter.
+ @param world The world.
+ @param map URID mapper.
+ @param node The subject of the state description (e.g. a preset URI).
+ @return A new LilvState which must be freed with lilv_state_free(), or NULL.
+*/
+LILV_API LilvState*
+lilv_state_new_from_world(LilvWorld* world,
+ LV2_URID_Map* map,
+ const LilvNode* node);
+
+/**
+ Load a state snapshot from a file.
+ @param world The world.
+ @param map URID mapper.
+ @param subject The subject of the state description (e.g. a preset URI).
+ @param path The path of the file containing the state description.
+ @return A new LilvState which must be freed with lilv_state_free().
+
+ If `subject` is NULL, it is taken to be the URI of the file (i.e.
+ "<>" in Turtle).
+
+ This function parses the file separately to create the state, it does not
+ parse the file into the world model, i.e. the returned state is the only
+ new memory consumed once this function returns.
+*/
+LILV_API LilvState*
+lilv_state_new_from_file(LilvWorld* world,
+ LV2_URID_Map* map,
+ const LilvNode* subject,
+ const char* path);
+
+/**
+ Load a state snapshot from a string made by lilv_state_to_string().
+*/
+LILV_API LilvState*
+lilv_state_new_from_string(LilvWorld* world,
+ LV2_URID_Map* map,
+ const char* str);
+
+/**
+ Function to get a port value.
+ @param port_symbol The symbol of the port.
+ @param user_data The user_data passed to lilv_state_new_from_instance().
+ @param size (Output) The size of the returned value.
+ @param type (Output) The URID of the type of the returned value.
+ @return A pointer to the port value.
+
+ This function MUST set `size` and `type` appropriately.
+*/
+typedef const void* (*LilvGetPortValueFunc)(const char* port_symbol,
+ void* user_data,
+ uint32_t* size,
+ uint32_t* type);
+
+/**
+ Create a new state snapshot from a plugin instance.
+
+ @param plugin The plugin this state applies to.
+
+ @param instance An instance of `plugin`.
+
+ @param map The map to use for mapping URIs in state.
+
+ @param file_dir Directory of files created by the plugin earlier (or NULL).
+ This is for hosts that support file creation at any time with state
+ state:makePath. These files will be copied as necessary to `copy_dir` and
+ not be referred to directly in state (a temporary directory is appropriate).
+
+ @param copy_dir Directory of copies of files in `file_dir` (or NULL). This
+ directory will have the same structure as `file_dir` but with possibly
+ modified file names to distinguish different revisions. If you only care
+ about saving one state snapshot, it can be the same as `save_dir`. Plugin
+ state will refer to files in this directory.
+
+ @param save_dir Directory of files created by plugin during save (or NULL).
+ If the state will be saved, this should be the bundle directory later passed
+ to lilv_state_save().
+
+ @param get_value Function to get port values (or NULL). If NULL, the
+ returned state will not represent port values. This should only be NULL in
+ hosts that save and restore port values via some other mechanism.
+
+ @param user_data User data to pass to `get_value`.
+
+ @param link_dir Directory of links to external files (or NULL). A link will
+ be made in this directory to any external files referred to in plugin state.
+ In turn, links will be created in the save directory to these links (e.g.
+ save_dir/file => link_dir/file => /foo/bar/file). This allows many state
+ snapshots to share a single link to an external file, so archival
+ (e.g. with tar -h) will not create several copies of the file. If this is
+ not required, it can be the same as save_dir.
+
+ @param flags Bitwise OR of LV2_State_Flags values.
+
+ @param features Features to pass LV2_State_Interface.save().
+
+ @return A new LilvState which must be freed with lilv_state_free().
+
+ This function may be called simultaneously with any instance function
+ (except discovery functions) unless the threading class of that function
+ explicitly disallows this.
+
+ To support advanced file functionality, there are several directory
+ parameters. Simple hosts that only wish to save a single plugins state once
+ may simply use the same directory for all of them (or pass NULL to not
+ support files at all). The multiple parameters are necessary to support
+ saving an instances state many times while avoiding any duplication of data.
+
+ If supported (via state:makePath passed to LV2_Descriptor::instantiate()),
+ `file_dir` should be the directory where any files created by the plugin
+ (not during save time, e.g. during instantiation) are stored. These files
+ will be copied to preserve their state at this time.plugin-created files are stored.
+ Lilv will assume any files within this directory (recursively) are created
+ by the plugin and all other files are immutable. Note that this function
+ does not save the state, use lilv_state_save() for that.
+
+ See <a href="http://lv2plug.in/ns/ext/state/state.h">state.h</a> from the
+ LV2 State extension for details on the `flags` and `features` parameters.
+*/
+LILV_API LilvState*
+lilv_state_new_from_instance(const LilvPlugin* plugin,
+ LilvInstance* instance,
+ LV2_URID_Map* map,
+ const char* file_dir,
+ const char* copy_dir,
+ const char* link_dir,
+ const char* save_dir,
+ LilvGetPortValueFunc get_value,
+ void* user_data,
+ uint32_t flags,
+ const LV2_Feature *const * features);
+
+/**
+ Free `state`.
+*/
+LILV_API void
+lilv_state_free(LilvState* state);
+
+/**
+ Return true iff `a` is equivalent to `b`.
+*/
+LILV_API bool
+lilv_state_equals(const LilvState* a, const LilvState* b);
+
+/**
+ Return the number of properties in `state`.
+*/
+LILV_API unsigned
+lilv_state_get_num_properties(const LilvState* state);
+
+/**
+ Get the URI of the plugin `state` applies to.
+*/
+LILV_API const LilvNode*
+lilv_state_get_plugin_uri(const LilvState* state);
+
+/**
+ Get the URI of `state`.
+
+ This may return NULL if the state has not been saved and has no URI.
+*/
+LILV_API const LilvNode*
+lilv_state_get_uri(const LilvState* state);
+
+/**
+ Get the label of `state`.
+*/
+LILV_API const char*
+lilv_state_get_label(const LilvState* state);
+
+/**
+ Set the label of `state`.
+*/
+LILV_API void
+lilv_state_set_label(LilvState* state,
+ const char* label);
+
+/**
+ Set a metadata property on `state`.
+ @param state The state to set the metadata for.
+ @param key The key to store `value` under (URID).
+ @param value Pointer to the value to be stored.
+ @param size The size of `value` in bytes.
+ @param type The type of `value` (URID).
+ @param flags LV2_State_Flags for `value`.
+ @return 0 on success.
+
+ This is a generic version of lilv_state_set_label(), which sets metadata
+ properties visible to hosts, but not plugins. This allows storing useful
+ information such as comments or preset banks.
+*/
+LILV_API int
+lilv_state_set_metadata(LilvState* state,
+ uint32_t key,
+ const void* value,
+ size_t size,
+ uint32_t type,
+ uint32_t flags);
+
+/**
+ Function to set a port value.
+ @param port_symbol The symbol of the port.
+ @param user_data The user_data passed to lilv_state_restore().
+ @param size The size of `value`.
+ @param type The URID of the type of `value`.
+ @param value A pointer to the port value.
+*/
+typedef void (*LilvSetPortValueFunc)(const char* port_symbol,
+ void* user_data,
+ const void* value,
+ uint32_t size,
+ uint32_t type);
+
+/**
+ Enumerate the port values in a state snapshot.
+ @param state The state to retrieve port values from.
+ @param set_value A function to receive port values.
+ @param user_data User data to pass to `set_value`.
+
+ This function is a subset of lilv_state_restore() that only fires the
+ `set_value` callback and does not directly affect a plugin instance. This
+ is useful in hosts that need to retrieve the port values in a state snapshot
+ for special handling.
+*/
+LILV_API void
+lilv_state_emit_port_values(const LilvState* state,
+ LilvSetPortValueFunc set_value,
+ void* user_data);
+
+/**
+ Restore a plugin instance from a state snapshot.
+ @param state The state to restore, which must apply to the correct plugin.
+ @param instance An instance of the plugin `state` applies to, or NULL.
+ @param set_value A function to set a port value (may be NULL).
+ @param user_data User data to pass to `set_value`.
+ @param flags Bitwise OR of LV2_State_Flags values.
+ @param features Features to pass LV2_State_Interface.restore().
+
+ This will set all the properties of `instance`, if given, to the values
+ stored in `state`. If `set_value` is provided, it will be called (with the
+ given `user_data`) to restore each port value, otherwise the host must
+ restore the port values itself (using lilv_state_get_port_value()) in order
+ to completely restore `state`.
+
+ If the state has properties and `instance` is given, this function is in
+ the "instantiation" threading class, i.e. it MUST NOT be called
+ simultaneously with any function on the same plugin instance. If the state
+ has no properties, only port values are set via `set_value`.
+
+ See <a href="http://lv2plug.in/ns/ext/state/state.h">state.h</a> from the
+ LV2 State extension for details on the `flags` and `features` parameters.
+*/
+LILV_API void
+lilv_state_restore(const LilvState* state,
+ LilvInstance* instance,
+ LilvSetPortValueFunc set_value,
+ void* user_data,
+ uint32_t flags,
+ const LV2_Feature *const * features);
+
+/**
+ Save state to a file.
+ @param world The world.
+ @param map URID mapper.
+ @param unmap URID unmapper.
+ @param state State to save.
+ @param uri URI of state, may be NULL.
+ @param dir Path of the bundle directory to save into.
+ @param filename Path of the state file relative to `dir`.
+
+ The format of state on disk is compatible with that defined in the LV2
+ preset extension, i.e. this function may be used to save presets which can
+ be loaded by any host.
+
+ If `uri` is NULL, the preset URI will be a file URI, but the bundle
+ can safely be moved (i.e. the state file will use "<>" as the subject).
+*/
+LILV_API int
+lilv_state_save(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ const char* uri,
+ const char* dir,
+ const char* filename);
+
+/**
+ Save state to a string. This function does not use the filesystem.
+
+ @param world The world.
+ @param map URID mapper.
+ @param unmap URID unmapper.
+ @param state The state to serialize.
+ @param uri URI for the state description (mandatory).
+ @param base_uri Base URI for serialisation. Unless you know what you are
+ doing, pass NULL for this, otherwise the state may not be restorable via
+ lilv_state_new_from_string().
+*/
+LILV_API char*
+lilv_state_to_string(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ const char* uri,
+ const char* base_uri);
+
+/**
+ Unload a state from the world and delete all associated files.
+ @param world The world.
+ @param state State to remove from the system.
+
+ This function DELETES FILES/DIRECTORIES FROM THE FILESYSTEM! It is intended
+ for removing user-saved presets, but can delete any state the user has
+ permission to delete, including presets shipped with plugins.
+
+ The rdfs:seeAlso file for the state will be removed. The entry in the
+ bundle's manifest.ttl is removed, and if this results in an empty manifest,
+ then the manifest file is removed. If this results in an empty bundle, then
+ the bundle directory is removed as well.
+*/
+LILV_API int
+lilv_state_delete(LilvWorld* world,
+ const LilvState* state);
+
+/**
+ @}
+ @name Scale Point
+ @{
+*/
+
+/**
+ Get the label of this scale point (enumeration value)
+ Returned value is owned by `point` and must not be freed.
+*/
+LILV_API const LilvNode*
+lilv_scale_point_get_label(const LilvScalePoint* point);
+
+/**
+ Get the value of this scale point (enumeration value)
+ Returned value is owned by `point` and must not be freed.
+*/
+LILV_API const LilvNode*
+lilv_scale_point_get_value(const LilvScalePoint* point);
+
+/**
+ @}
+ @name Plugin Class
+ @{
+*/
+
+/**
+ Get the URI of this class' superclass.
+ Returned value is owned by `plugin_class` and must not be freed by caller.
+ Returned value may be NULL, if class has no parent.
+*/
+LILV_API const LilvNode*
+lilv_plugin_class_get_parent_uri(const LilvPluginClass* plugin_class);
+
+/**
+ Get the URI of this plugin class.
+ Returned value is owned by `plugin_class` and must not be freed by caller.
+*/
+LILV_API const LilvNode*
+lilv_plugin_class_get_uri(const LilvPluginClass* plugin_class);
+
+/**
+ Get the label of this plugin class, ie "Oscillators".
+ Returned value is owned by `plugin_class` and must not be freed by caller.
+*/
+LILV_API const LilvNode*
+lilv_plugin_class_get_label(const LilvPluginClass* plugin_class);
+
+/**
+ Get the subclasses of this plugin class.
+ Returned value must be freed by caller with lilv_plugin_classes_free().
+*/
+LILV_API LilvPluginClasses*
+lilv_plugin_class_get_children(const LilvPluginClass* plugin_class);
+
+/**
+ @}
+ @name Plugin Instance
+ @{
+*/
+
+/**
+ @cond LILV_DOCUMENT_INSTANCE_IMPL
+*/
+
+/* Instance of a plugin.
+ This is exposed in the ABI to allow inlining of performance critical
+ functions like lilv_instance_run() (simple wrappers of functions in lv2.h).
+ This is for performance reasons, user code should not use this definition
+ in any way (which is why it is not machine documented).
+ Truly private implementation details are hidden via `pimpl`.
+*/
+struct LilvInstanceImpl {
+ const LV2_Descriptor* lv2_descriptor;
+ LV2_Handle lv2_handle;
+ void* pimpl;
+};
+
+/**
+ @endcond
+*/
+
+/**
+ Instantiate a plugin.
+ The returned value is a lightweight handle for an LV2 plugin instance,
+ it does not refer to `plugin`, or any other Lilv state. The caller must
+ eventually free it with lilv_instance_free().
+ `features` is a NULL-terminated array of features the host supports.
+ NULL may be passed if the host supports no additional features.
+ @return NULL if instantiation failed.
+*/
+LILV_API LilvInstance*
+lilv_plugin_instantiate(const LilvPlugin* plugin,
+ double sample_rate,
+ const LV2_Feature*const* features);
+
+/**
+ Free a plugin instance.
+ It is safe to call this function on NULL.
+ `instance` is invalid after this call.
+*/
+LILV_API void
+lilv_instance_free(LilvInstance* instance);
+
+#ifndef LILV_INTERNAL
+
+/**
+ Get the URI of the plugin which `instance` is an instance of.
+ Returned string is shared and must not be modified or deleted.
+*/
+static inline const char*
+lilv_instance_get_uri(const LilvInstance* instance)
+{
+ return instance->lv2_descriptor->URI;
+}
+
+/**
+ Connect a port to a data location.
+ This may be called regardless of whether the plugin is activated,
+ activation and deactivation does not destroy port connections.
+*/
+static inline void
+lilv_instance_connect_port(LilvInstance* instance,
+ uint32_t port_index,
+ void* data_location)
+{
+ instance->lv2_descriptor->connect_port
+ (instance->lv2_handle, port_index, data_location);
+}
+
+/**
+ Activate a plugin instance.
+ This resets all state information in the plugin, except for port data
+ locations (as set by lilv_instance_connect_port()). This MUST be called
+ before calling lilv_instance_run().
+*/
+static inline void
+lilv_instance_activate(LilvInstance* instance)
+{
+ if (instance->lv2_descriptor->activate) {
+ instance->lv2_descriptor->activate(instance->lv2_handle);
+ }
+}
+
+/**
+ Run `instance` for `sample_count` frames.
+ If the hint lv2:hardRTCapable is set for this plugin, this function is
+ guaranteed not to block.
+*/
+static inline void
+lilv_instance_run(LilvInstance* instance,
+ uint32_t sample_count)
+{
+ instance->lv2_descriptor->run(instance->lv2_handle, sample_count);
+}
+
+/**
+ Deactivate a plugin instance.
+ Note that to run the plugin after this you must activate it, which will
+ reset all state information (except port connections).
+*/
+static inline void
+lilv_instance_deactivate(LilvInstance* instance)
+{
+ if (instance->lv2_descriptor->deactivate) {
+ instance->lv2_descriptor->deactivate(instance->lv2_handle);
+ }
+}
+
+/**
+ Get extension data from the plugin instance.
+ The type and semantics of the data returned is specific to the particular
+ extension, though in all cases it is shared and must not be deleted.
+*/
+static inline const void*
+lilv_instance_get_extension_data(const LilvInstance* instance,
+ const char* uri)
+{
+ if (instance->lv2_descriptor->extension_data) {
+ return instance->lv2_descriptor->extension_data(uri);
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ Get the LV2_Descriptor of the plugin instance.
+ Normally hosts should not need to access the LV2_Descriptor directly,
+ use the lilv_instance_* functions.
+
+ The returned descriptor is shared and must not be deleted.
+*/
+static inline const LV2_Descriptor*
+lilv_instance_get_descriptor(const LilvInstance* instance)
+{
+ return instance->lv2_descriptor;
+}
+
+/**
+ Get the LV2_Handle of the plugin instance.
+ Normally hosts should not need to access the LV2_Handle directly,
+ use the lilv_instance_* functions.
+
+ The returned handle is shared and must not be deleted.
+*/
+static inline LV2_Handle
+lilv_instance_get_handle(const LilvInstance* instance)
+{
+ return instance->lv2_handle;
+}
+
+#endif /* LILV_INTERNAL */
+
+/**
+ @}
+ @name Plugin UI
+ @{
+*/
+
+/**
+ Get all UIs for `plugin`.
+ Returned value must be freed by caller using lilv_uis_free().
+*/
+LILV_API LilvUIs*
+lilv_plugin_get_uis(const LilvPlugin* plugin);
+
+/**
+ Get the URI of a Plugin UI.
+ @param ui The Plugin UI
+ @return a shared value which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_ui_get_uri(const LilvUI* ui);
+
+/**
+ Get the types (URIs of RDF classes) of a Plugin UI.
+ @param ui The Plugin UI
+ @return a shared value which must not be modified or freed.
+
+ Note that in most cases lilv_ui_is_supported() should be used, which avoids
+ the need to use this function (and type specific logic).
+*/
+LILV_API const LilvNodes*
+lilv_ui_get_classes(const LilvUI* ui);
+
+/**
+ Check whether a plugin UI has a given type.
+ @param ui The Plugin UI
+ @param class_uri The URI of the LV2 UI type to check this UI against
+*/
+LILV_API bool
+lilv_ui_is_a(const LilvUI* ui, const LilvNode* class_uri);
+
+/**
+ Function to determine whether a UI type is supported.
+
+ This is provided by the user and must return non-zero iff using a UI of type
+ `ui_type_uri` in a container of type `container_type_uri` is supported.
+*/
+typedef unsigned (*LilvUISupportedFunc)(const char* container_type_uri,
+ const char* ui_type_uri);
+
+/**
+ Return true iff a Plugin UI is supported as a given widget type.
+ @param ui The Plugin UI
+ @param supported_func User provided supported predicate.
+ @param container_type The widget type to host the UI within.
+ @param ui_type (Output) If non-NULL, set to the native type of the UI
+ which is owned by `ui` and must not be freed by the caller.
+ @return The embedding quality level returned by `supported_func`.
+*/
+LILV_API unsigned
+lilv_ui_is_supported(const LilvUI* ui,
+ LilvUISupportedFunc supported_func,
+ const LilvNode* container_type,
+ const LilvNode** ui_type);
+
+/**
+ Get the URI for a Plugin UI's bundle.
+ @param ui The Plugin UI
+ @return a shared value which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_ui_get_bundle_uri(const LilvUI* ui);
+
+/**
+ Get the URI for a Plugin UI's shared library.
+ @param ui The Plugin UI
+ @return a shared value which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_ui_get_binary_uri(const LilvUI* ui);
+
+/**
+ @}
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LILV_LILV_H */
diff --git a/lilv/lilvmm.hpp b/lilv/lilvmm.hpp
new file mode 100644
index 0000000..30ec274
--- /dev/null
+++ b/lilv/lilvmm.hpp
@@ -0,0 +1,345 @@
+/*
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef LILV_LILVMM_HPP
+#define LILV_LILVMM_HPP
+
+#include "lilv/lilv.h"
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+# define LILV_DEPRECATED __attribute__((__deprecated__))
+#else
+# define LILV_DEPRECATED
+#endif
+
+namespace Lilv {
+
+LILV_DEPRECATED
+static inline const char*
+uri_to_path(const char* uri) {
+ return lilv_uri_to_path(uri);
+}
+
+#define LILV_WRAP0(RT, prefix, name) \
+ inline RT name() { return lilv_ ## prefix ## _ ## name (me); }
+
+#define LILV_WRAP0_VOID(prefix, name) \
+ inline void name() { lilv_ ## prefix ## _ ## name(me); }
+
+#define LILV_WRAP1(RT, prefix, name, T1, a1) \
+ inline RT name(T1 a1) { return lilv_ ## prefix ## _ ## name (me, a1); }
+
+#define LILV_WRAP1_VOID(prefix, name, T1, a1) \
+ inline void name(T1 a1) { lilv_ ## prefix ## _ ## name(me, a1); }
+
+#define LILV_WRAP2(RT, prefix, name, T1, a1, T2, a2) \
+ inline RT name(T1 a1, T2 a2) { \
+ return lilv_ ## prefix ## _ ## name(me, a1, a2); \
+ }
+
+#define LILV_WRAP3(RT, prefix, name, T1, a1, T2, a2, T3, a3) \
+ inline RT name(T1 a1, T2 a2, T3 a3) { \
+ return lilv_ ## prefix ## _ ## name(me, a1, a2, a3); \
+ }
+
+#define LILV_WRAP2_VOID(prefix, name, T1, a1, T2, a2) \
+ inline void name(T1 a1, T2 a2) { lilv_ ## prefix ## _ ## name(me, a1, a2); }
+
+#ifndef SWIG
+#define LILV_WRAP_CONVERSION(CT) \
+ inline operator CT*() const { return me; }
+#else
+#define LILV_WRAP_CONVERSION(CT)
+#endif
+
+struct Node {
+ inline Node(const LilvNode* node) : me(lilv_node_duplicate(node)) {}
+ inline Node(const Node& copy) : me(lilv_node_duplicate(copy.me)) {}
+
+ inline ~Node() { lilv_node_free(me); }
+
+ inline bool equals(const Node& other) const {
+ return lilv_node_equals(me, other.me);
+ }
+
+ inline bool operator==(const Node& other) const { return equals(other); }
+
+ LILV_WRAP_CONVERSION(LilvNode);
+
+ LILV_WRAP0(char*, node, get_turtle_token);
+ LILV_WRAP0(bool, node, is_uri);
+ LILV_WRAP0(const char*, node, as_uri);
+ LILV_WRAP0(bool, node, is_blank);
+ LILV_WRAP0(const char*, node, as_blank);
+ LILV_WRAP0(bool, node, is_literal);
+ LILV_WRAP0(bool, node, is_string);
+ LILV_WRAP0(const char*, node, as_string);
+ LILV_WRAP0(bool, node, is_float);
+ LILV_WRAP0(float, node, as_float);
+ LILV_WRAP0(bool, node, is_int);
+ LILV_WRAP0(int, node, as_int);
+ LILV_WRAP0(bool, node, is_bool);
+ LILV_WRAP0(bool, node, as_bool);
+
+ LilvNode* me;
+};
+
+struct ScalePoint {
+ inline ScalePoint(const LilvScalePoint* c_obj) : me(c_obj) {}
+ LILV_WRAP_CONVERSION(const LilvScalePoint);
+
+ LILV_WRAP0(const LilvNode*, scale_point, get_label);
+ LILV_WRAP0(const LilvNode*, scale_point, get_value);
+
+ const LilvScalePoint* me;
+};
+
+struct PluginClass {
+ inline PluginClass(const LilvPluginClass* c_obj) : me(c_obj) {}
+ LILV_WRAP_CONVERSION(const LilvPluginClass);
+
+ LILV_WRAP0(Node, plugin_class, get_parent_uri);
+ LILV_WRAP0(Node, plugin_class, get_uri);
+ LILV_WRAP0(Node, plugin_class, get_label);
+ LILV_WRAP0(LilvPluginClasses*, plugin_class, get_children);
+
+ const LilvPluginClass* me;
+};
+
+#define LILV_WRAP_COLL(CT, ET, prefix) \
+ inline CT(const Lilv ## CT* c_obj) : me(c_obj) {} \
+ LILV_WRAP_CONVERSION(const Lilv ## CT); \
+ LILV_WRAP0(unsigned, prefix, size); \
+ LILV_WRAP1(const ET, prefix, get, LilvIter*, i); \
+ LILV_WRAP0(LilvIter*, prefix, begin); \
+ LILV_WRAP1(LilvIter*, prefix, next, LilvIter*, i); \
+ LILV_WRAP1(bool, prefix, is_end, LilvIter*, i); \
+ const Lilv ## CT* me; \
+
+struct PluginClasses {
+ LILV_WRAP_COLL(PluginClasses, PluginClass, plugin_classes);
+ LILV_WRAP1(const PluginClass, plugin_classes,
+ get_by_uri, const LilvNode*, uri);
+};
+
+struct ScalePoints {
+ LILV_WRAP_COLL(ScalePoints, ScalePoint, scale_points);
+};
+
+struct Nodes {
+ LILV_WRAP_COLL(Nodes, Node, nodes);
+ LILV_WRAP1(bool, nodes, contains, const Node, node);
+ LILV_WRAP0(Node, nodes, get_first);
+};
+
+struct UI {
+ inline UI(const LilvUI* c_obj) : me(c_obj) {}
+ LILV_WRAP_CONVERSION(const LilvUI);
+
+ LILV_WRAP0(const LilvNode*, ui, get_uri);
+ LILV_WRAP0(const LilvNode*, ui, get_bundle_uri);
+ LILV_WRAP0(const LilvNode*, ui, get_binary_uri);
+ LILV_WRAP0(const LilvNodes*, ui, get_classes);
+ /*LILV_WRAP3(bool, ui, is_supported,
+ LilvUISupportedFunc, supported_func,
+ const LilvNode*, container_type,
+ const LilvNode**, ui_type);*/
+ LILV_WRAP1(bool, ui, is_a, const LilvNode*, class_uri);
+
+ const LilvUI* me;
+};
+
+struct UIs {
+ LILV_WRAP_COLL(UIs, UI, uis);
+};
+
+struct Port {
+ inline Port(const LilvPlugin* p, const LilvPort* c_obj)
+ : parent(p), me(c_obj)
+ {}
+
+ LILV_WRAP_CONVERSION(const LilvPort);
+
+#define LILV_PORT_WRAP0(RT, name) \
+ inline RT name () { return lilv_port_ ## name (parent, me); }
+
+#define LILV_PORT_WRAP1(RT, name, T1, a1) \
+ inline RT name (T1 a1) { return lilv_port_ ## name (parent, me, a1); }
+
+ LILV_PORT_WRAP1(LilvNodes*, get_value, LilvNode*, predicate);
+ LILV_PORT_WRAP0(LilvNodes*, get_properties)
+ LILV_PORT_WRAP1(bool, has_property, LilvNode*, property_uri);
+ LILV_PORT_WRAP1(bool, supports_event, LilvNode*, event_uri);
+ LILV_PORT_WRAP0(const LilvNode*, get_symbol);
+ LILV_PORT_WRAP0(LilvNode*, get_name);
+ LILV_PORT_WRAP0(const LilvNodes*, get_classes);
+ LILV_PORT_WRAP1(bool, is_a, LilvNode*, port_class);
+ LILV_PORT_WRAP0(LilvScalePoints*, get_scale_points);
+
+ // TODO: get_range (output parameters)
+
+ const LilvPlugin* parent;
+ const LilvPort* me;
+};
+
+struct Plugin {
+ inline Plugin(const LilvPlugin* c_obj) : me(c_obj) {}
+ LILV_WRAP_CONVERSION(const LilvPlugin);
+
+ LILV_WRAP0(bool, plugin, verify);
+ LILV_WRAP0(Node, plugin, get_uri);
+ LILV_WRAP0(Node, plugin, get_bundle_uri);
+ LILV_WRAP0(Nodes, plugin, get_data_uris);
+ LILV_WRAP0(Node, plugin, get_library_uri);
+ LILV_WRAP0(Node, plugin, get_name);
+ LILV_WRAP0(PluginClass, plugin, get_class);
+ LILV_WRAP1(Nodes, plugin, get_value, Node, pred);
+ LILV_WRAP1(bool, plugin, has_feature, Node, feature_uri);
+ LILV_WRAP0(Nodes, plugin, get_supported_features);
+ LILV_WRAP0(Nodes, plugin, get_required_features);
+ LILV_WRAP0(Nodes, plugin, get_optional_features);
+ LILV_WRAP0(unsigned, plugin, get_num_ports);
+ LILV_WRAP0(bool, plugin, has_latency);
+ LILV_WRAP0(unsigned, plugin, get_latency_port_index);
+ LILV_WRAP0(Node, plugin, get_author_name);
+ LILV_WRAP0(Node, plugin, get_author_email);
+ LILV_WRAP0(Node, plugin, get_author_homepage);
+ LILV_WRAP0(bool, plugin, is_replaced);
+ LILV_WRAP0(Nodes, plugin, get_extension_data);
+ LILV_WRAP0(UIs, plugin, get_uis);
+ LILV_WRAP1(Nodes, plugin, get_related, Node, type);
+
+ inline Port get_port_by_index(unsigned index) {
+ return Port(me, lilv_plugin_get_port_by_index(me, index));
+ }
+
+ inline Port get_port_by_symbol(LilvNode* symbol) {
+ return Port(me, lilv_plugin_get_port_by_symbol(me, symbol));
+ }
+
+ inline void get_port_ranges_float(float* min_values,
+ float* max_values,
+ float* def_values) {
+ return lilv_plugin_get_port_ranges_float(
+ me, min_values, max_values, def_values);
+ }
+
+ inline unsigned get_num_ports_of_class(LilvNode* class_1, ...) {
+ va_list args;
+ va_start(args, class_1);
+
+ const uint32_t count = lilv_plugin_get_num_ports_of_class_va(
+ me, class_1, args);
+
+ va_end(args);
+ return count;
+ }
+
+ const LilvPlugin* me;
+};
+
+struct Plugins {
+ LILV_WRAP_COLL(Plugins, Plugin, plugins);
+ LILV_WRAP1(const Plugin, plugins, get_by_uri, const LilvNode*, uri);
+};
+
+struct Instance {
+ inline Instance(LilvInstance* instance) : me(instance) {}
+
+ LILV_DEPRECATED
+ inline Instance(Plugin plugin, double sample_rate) {
+ me = lilv_plugin_instantiate(plugin, sample_rate, NULL);
+ }
+
+ LILV_DEPRECATED inline Instance(Plugin plugin,
+ double sample_rate,
+ LV2_Feature* const* features) {
+ me = lilv_plugin_instantiate(plugin, sample_rate, features);
+ }
+
+ static inline Instance* create(Plugin plugin,
+ double sample_rate,
+ LV2_Feature* const* features) {
+ LilvInstance* me = lilv_plugin_instantiate(
+ plugin, sample_rate, features);
+
+ return me ? new Instance(me) : NULL;
+ }
+
+ LILV_WRAP_CONVERSION(LilvInstance);
+
+ LILV_WRAP2_VOID(instance, connect_port,
+ unsigned, port_index,
+ void*, data_location);
+
+ LILV_WRAP0_VOID(instance, activate);
+ LILV_WRAP1_VOID(instance, run, unsigned, sample_count);
+ LILV_WRAP0_VOID(instance, deactivate);
+
+ inline const void* get_extension_data(const char* uri) {
+ return lilv_instance_get_extension_data(me, uri);
+ }
+
+ inline const LV2_Descriptor* get_descriptor() {
+ return lilv_instance_get_descriptor(me);
+ }
+
+ inline LV2_Handle get_handle() {
+ return lilv_instance_get_handle(me);
+ }
+
+ LilvInstance* me;
+};
+
+struct World {
+ inline World() : me(lilv_world_new()) {}
+ inline ~World() { lilv_world_free(me); }
+
+ inline LilvNode* new_uri(const char* uri) {
+ return lilv_new_uri(me, uri);
+ }
+ inline LilvNode* new_string(const char* str) {
+ return lilv_new_string(me, str);
+ }
+ inline LilvNode* new_int(int val) {
+ return lilv_new_int(me, val);
+ }
+ inline LilvNode* new_float(float val) {
+ return lilv_new_float(me, val);
+ }
+ inline LilvNode* new_bool(bool val) {
+ return lilv_new_bool(me, val);
+ }
+ inline Nodes find_nodes(const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object) {
+ return lilv_world_find_nodes(me, subject, predicate, object);
+ }
+
+ LILV_WRAP2_VOID(world, set_option, const char*, uri, LilvNode*, value);
+ LILV_WRAP0_VOID(world, load_all);
+ LILV_WRAP1_VOID(world, load_bundle, LilvNode*, bundle_uri);
+ LILV_WRAP0(const LilvPluginClass*, world, get_plugin_class);
+ LILV_WRAP0(const LilvPluginClasses*, world, get_plugin_classes);
+ LILV_WRAP0(const Plugins, world, get_all_plugins);
+ LILV_WRAP1(int, world, load_resource, const LilvNode*, resource);
+
+ LilvWorld* me;
+};
+
+} /* namespace Lilv */
+
+#endif /* LILV_LILVMM_HPP */
diff --git a/src/collections.c b/src/collections.c
new file mode 100644
index 0000000..9ded845
--- /dev/null
+++ b/src/collections.c
@@ -0,0 +1,224 @@
+/*
+ Copyright 2008-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "lilv_internal.h"
+
+int
+lilv_ptr_cmp(const void* a, const void* b, void* user_data)
+{
+ return (intptr_t)a - (intptr_t)b;
+}
+
+int
+lilv_resource_node_cmp(const void* a, const void* b, void* user_data)
+{
+ const SordNode* an = ((const LilvNode*)a)->node;
+ const SordNode* bn = ((const LilvNode*)b)->node;
+ return (intptr_t)an - (intptr_t)bn;
+}
+
+/* Generic collection functions */
+
+static inline LilvCollection*
+lilv_collection_new(ZixComparator cmp, ZixDestroyFunc destructor)
+{
+ return zix_tree_new(false, cmp, NULL, destructor);
+}
+
+void
+lilv_collection_free(LilvCollection* collection)
+{
+ if (collection) {
+ zix_tree_free((ZixTree*)collection);
+ }
+}
+
+unsigned
+lilv_collection_size(const LilvCollection* collection)
+{
+ return (collection ? zix_tree_size((const ZixTree*)collection) : 0);
+}
+
+LilvIter*
+lilv_collection_begin(const LilvCollection* collection)
+{
+ return collection ? (LilvIter*)zix_tree_begin((ZixTree*)collection) : NULL;
+}
+
+void*
+lilv_collection_get(const LilvCollection* collection,
+ const LilvIter* i)
+{
+ return zix_tree_get((const ZixTreeIter*)i);
+}
+
+/* Constructors */
+
+LilvScalePoints*
+lilv_scale_points_new(void)
+{
+ return lilv_collection_new(lilv_ptr_cmp,
+ (ZixDestroyFunc)lilv_scale_point_free);
+}
+
+LilvNodes*
+lilv_nodes_new(void)
+{
+ return lilv_collection_new(lilv_ptr_cmp,
+ (ZixDestroyFunc)lilv_node_free);
+}
+
+LilvUIs*
+lilv_uis_new(void)
+{
+ return lilv_collection_new(lilv_header_compare_by_uri,
+ (ZixDestroyFunc)lilv_ui_free);
+}
+
+LilvPluginClasses*
+lilv_plugin_classes_new(void)
+{
+ return lilv_collection_new(lilv_header_compare_by_uri,
+ (ZixDestroyFunc)lilv_plugin_class_free);
+}
+
+/* URI based accessors (for collections of things with URIs) */
+
+LILV_API const LilvPluginClass*
+lilv_plugin_classes_get_by_uri(const LilvPluginClasses* classes,
+ const LilvNode* uri)
+{
+ return (LilvPluginClass*)lilv_collection_get_by_uri(
+ (const ZixTree*)classes, uri);
+}
+
+LILV_API const LilvUI*
+lilv_uis_get_by_uri(const LilvUIs* uis, const LilvNode* uri)
+{
+ return (LilvUI*)lilv_collection_get_by_uri((const ZixTree*)uis, uri);
+}
+
+/* Plugins */
+
+LilvPlugins*
+lilv_plugins_new(void)
+{
+ return lilv_collection_new(lilv_header_compare_by_uri, NULL);
+}
+
+LILV_API const LilvPlugin*
+lilv_plugins_get_by_uri(const LilvPlugins* plugins, const LilvNode* uri)
+{
+ return (LilvPlugin*)lilv_collection_get_by_uri(
+ (const ZixTree*)plugins, uri);
+}
+
+/* Nodes */
+
+LILV_API bool
+lilv_nodes_contains(const LilvNodes* nodes, const LilvNode* value)
+{
+ LILV_FOREACH(nodes, i, nodes) {
+ if (lilv_node_equals(lilv_nodes_get(nodes, i), value)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+LILV_API LilvNodes*
+lilv_nodes_merge(const LilvNodes* a, const LilvNodes* b)
+{
+ LilvNodes* result = lilv_nodes_new();
+
+ LILV_FOREACH(nodes, i, a)
+ zix_tree_insert((ZixTree*)result,
+ lilv_node_duplicate(lilv_nodes_get(a, i)),
+ NULL);
+
+ LILV_FOREACH(nodes, i, b)
+ zix_tree_insert((ZixTree*)result,
+ lilv_node_duplicate(lilv_nodes_get(b, i)),
+ NULL);
+
+ return result;
+}
+
+/* Iterator */
+
+#define LILV_COLLECTION_IMPL(prefix, CT, ET) \
+LILV_API \
+unsigned \
+prefix##_size(const CT* collection) { \
+ return lilv_collection_size(collection); \
+} \
+\
+LILV_API \
+LilvIter* \
+prefix##_begin(const CT* collection) { \
+ return lilv_collection_begin(collection); \
+} \
+\
+LILV_API \
+const ET* \
+prefix##_get(const CT* collection, LilvIter* i) { \
+ return (ET*)lilv_collection_get(collection, i); \
+} \
+\
+LILV_API \
+LilvIter* \
+prefix##_next(const CT* collection, LilvIter* i) { \
+ return zix_tree_iter_next((ZixTreeIter*)i); \
+} \
+\
+LILV_API \
+bool \
+prefix##_is_end(const CT* collection, LilvIter* i) { \
+ return zix_tree_iter_is_end((ZixTreeIter*)i); \
+}
+
+LILV_COLLECTION_IMPL(lilv_plugin_classes, LilvPluginClasses, LilvPluginClass)
+LILV_COLLECTION_IMPL(lilv_scale_points, LilvScalePoints, LilvScalePoint)
+LILV_COLLECTION_IMPL(lilv_uis, LilvUIs, LilvUI)
+LILV_COLLECTION_IMPL(lilv_nodes, LilvNodes, LilvNode)
+LILV_COLLECTION_IMPL(lilv_plugins, LilvPlugins, LilvPlugin)
+
+LILV_API void
+lilv_plugin_classes_free(LilvPluginClasses* collection) {
+ lilv_collection_free(collection);
+}
+
+LILV_API void
+lilv_scale_points_free(LilvScalePoints* collection) {
+ lilv_collection_free(collection);
+}
+
+LILV_API void
+lilv_uis_free(LilvUIs* collection) {
+ lilv_collection_free(collection);
+}
+
+LILV_API void
+lilv_nodes_free(LilvNodes* collection) {
+ lilv_collection_free(collection);
+}
+
+LILV_API LilvNode*
+lilv_nodes_get_first(const LilvNodes* collection) {
+ return (LilvNode*)lilv_collection_get(collection,
+ lilv_collection_begin(collection));
+}
diff --git a/src/instance.c b/src/instance.c
new file mode 100644
index 0000000..030c6ff
--- /dev/null
+++ b/src/instance.c
@@ -0,0 +1,110 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv_internal.h"
+
+LILV_API LilvInstance*
+lilv_plugin_instantiate(const LilvPlugin* plugin,
+ double sample_rate,
+ const LV2_Feature*const* features)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ if (plugin->parse_errors) {
+ return NULL;
+ }
+
+ LilvInstance* result = NULL;
+ const LilvNode* const lib_uri = lilv_plugin_get_library_uri(plugin);
+ const LilvNode* const bundle_uri = lilv_plugin_get_bundle_uri(plugin);
+ if (!lib_uri || !bundle_uri) {
+ return NULL;
+ }
+
+ char* const bundle_path = lilv_file_uri_parse(
+ lilv_node_as_uri(bundle_uri), NULL);
+
+ LilvLib* lib = lilv_lib_open(plugin->world, lib_uri, bundle_path, features);
+ if (!lib) {
+ lilv_free(bundle_path);
+ return NULL;
+ }
+
+ const LV2_Feature** local_features = NULL;
+ if (features == NULL) {
+ local_features = (const LV2_Feature**)malloc(sizeof(LV2_Feature*));
+ local_features[0] = NULL;
+ }
+
+ // Search for plugin by URI
+ for (uint32_t i = 0; true; ++i) {
+ const LV2_Descriptor* ld = lilv_lib_get_plugin(lib, i);
+ if (!ld) {
+ LILV_ERRORF("No plugin <%s> in <%s>\n",
+ lilv_node_as_uri(lilv_plugin_get_uri(plugin)),
+ lilv_node_as_uri(lib_uri));
+ lilv_lib_close(lib);
+ break; // return NULL
+ }
+
+ if (!strcmp(ld->URI, lilv_node_as_uri(lilv_plugin_get_uri(plugin)))) {
+ // Create LilvInstance to return
+ result = (LilvInstance*)malloc(sizeof(LilvInstance));
+ result->lv2_descriptor = ld;
+ result->lv2_handle = ld->instantiate(
+ ld, sample_rate, bundle_path,
+ (features) ? features : local_features);
+ result->pimpl = lib;
+ break;
+ }
+ }
+
+ free(local_features);
+ lilv_free(bundle_path);
+
+ if (result) {
+ if (result->lv2_handle == NULL) {
+ // Failed to instantiate
+ free(result);
+ lilv_lib_close(lib);
+ return NULL;
+ }
+
+ // "Connect" all ports to NULL (catches bugs)
+ for (uint32_t i = 0; i < lilv_plugin_get_num_ports(plugin); ++i) {
+ result->lv2_descriptor->connect_port(result->lv2_handle, i, NULL);
+ }
+ }
+
+ return result;
+}
+
+LILV_API void
+lilv_instance_free(LilvInstance* instance)
+{
+ if (!instance) {
+ return;
+ }
+
+ instance->lv2_descriptor->cleanup(instance->lv2_handle);
+ instance->lv2_descriptor = NULL;
+ lilv_lib_close((LilvLib*)instance->pimpl);
+ instance->pimpl = NULL;
+ free(instance);
+}
diff --git a/src/lib.c b/src/lib.c
new file mode 100644
index 0000000..73e57bb
--- /dev/null
+++ b/src/lib.c
@@ -0,0 +1,112 @@
+/*
+ Copyright 2012-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "lilv_internal.h"
+
+LilvLib*
+lilv_lib_open(LilvWorld* world,
+ const LilvNode* uri,
+ const char* bundle_path,
+ const LV2_Feature*const* features)
+{
+ ZixTreeIter* i = NULL;
+ const LilvLib key = {
+ world, (LilvNode*)uri, (char*)bundle_path, NULL, NULL, NULL, 0
+ };
+ if (!zix_tree_find(world->libs, &key, &i)) {
+ LilvLib* llib = (LilvLib*)zix_tree_get(i);
+ ++llib->refs;
+ return llib;
+ }
+
+ const char* const lib_uri = lilv_node_as_uri(uri);
+ char* const lib_path = lilv_file_uri_parse(lib_uri, NULL);
+ if (!lib_path) {
+ return NULL;
+ }
+
+ dlerror();
+ void* lib = dlopen(lib_path, RTLD_NOW);
+ if (!lib) {
+ LILV_ERRORF("Failed to open library %s (%s)\n", lib_path, dlerror());
+ lilv_free(lib_path);
+ return NULL;
+ }
+
+ LV2_Descriptor_Function df = (LV2_Descriptor_Function)
+ lilv_dlfunc(lib, "lv2_descriptor");
+
+ LV2_Lib_Descriptor_Function ldf = (LV2_Lib_Descriptor_Function)
+ lilv_dlfunc(lib, "lv2_lib_descriptor");
+
+ const LV2_Lib_Descriptor* desc = NULL;
+ if (ldf) {
+ desc = ldf(bundle_path, features);
+ if (!desc) {
+ LILV_ERRORF("Call to %s:lv2_lib_descriptor failed\n", lib_path);
+ dlclose(lib);
+ lilv_free(lib_path);
+ return NULL;
+ }
+ } else if (!df) {
+ LILV_ERRORF("No `lv2_descriptor' or `lv2_lib_descriptor' in %s\n",
+ lib_path);
+ dlclose(lib);
+ lilv_free(lib_path);
+ return NULL;
+ }
+ lilv_free(lib_path);
+
+ LilvLib* llib = (LilvLib*)malloc(sizeof(LilvLib));
+ llib->world = world;
+ llib->uri = lilv_node_duplicate(uri);
+ llib->bundle_path = lilv_strdup(bundle_path);
+ llib->lib = lib;
+ llib->lv2_descriptor = df;
+ llib->desc = desc;
+ llib->refs = 1;
+
+ zix_tree_insert(world->libs, llib, NULL);
+ return llib;
+}
+
+const LV2_Descriptor*
+lilv_lib_get_plugin(LilvLib* lib, uint32_t index)
+{
+ if (lib->lv2_descriptor) {
+ return lib->lv2_descriptor(index);
+ } else if (lib->desc) {
+ return lib->desc->get_plugin(lib->desc->handle, index);
+ }
+ return NULL;
+}
+
+void
+lilv_lib_close(LilvLib* lib)
+{
+ if (--lib->refs == 0) {
+ dlclose(lib->lib);
+
+ ZixTreeIter* i = NULL;
+ if (lib->world->libs && !zix_tree_find(lib->world->libs, lib, &i)) {
+ zix_tree_remove(lib->world->libs, i);
+ }
+
+ lilv_node_free(lib->uri);
+ free(lib->bundle_path);
+ free(lib);
+ }
+}
diff --git a/src/lilv_internal.h b/src/lilv_internal.h
new file mode 100644
index 0000000..426eaaf
--- /dev/null
+++ b/src/lilv_internal.h
@@ -0,0 +1,445 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef LILV_INTERNAL_H
+#define LILV_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <float.h>
+
+#ifdef _WIN32
+# include <windows.h>
+# include <direct.h>
+# include <stdio.h>
+# define dlopen(path, flags) LoadLibrary(path)
+# define dlclose(lib) FreeLibrary((HMODULE)lib)
+# define unlink(path) _unlink(path)
+# define rmdir(path) _rmdir(path)
+# ifdef _MSC_VER
+# define __func__ __FUNCTION__
+# ifndef snprintf
+# define snprintf _snprintf
+# endif
+# endif
+#ifndef INFINITY
+# define INFINITY DBL_MAX + DBL_MAX
+#endif
+#ifndef NAN
+# define NAN INFINITY - INFINITY
+#endif
+static inline const char* dlerror(void) { return "Unknown error"; }
+#else
+# include <dlfcn.h>
+# include <unistd.h>
+#endif
+
+#include "serd/serd.h"
+#include "sord/sord.h"
+
+#include "zix/tree.h"
+
+#include "lilv_config.h"
+#include "lilv/lilv.h"
+
+#ifdef LILV_DYN_MANIFEST
+# include "lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h"
+#endif
+
+/*
+ *
+ * Types
+ *
+ */
+
+typedef struct LilvSpecImpl LilvSpec;
+
+typedef void LilvCollection;
+
+struct LilvPortImpl {
+ LilvNode* node; ///< RDF node
+ uint32_t index; ///< lv2:index
+ LilvNode* symbol; ///< lv2:symbol
+ LilvNodes* classes; ///< rdf:type
+};
+
+struct LilvSpecImpl {
+ SordNode* spec;
+ SordNode* bundle;
+ LilvNodes* data_uris;
+ struct LilvSpecImpl* next;
+};
+
+/**
+ Header of an LilvPlugin, LilvPluginClass, or LilvUI.
+ Any of these structs may be safely casted to LilvHeader, which is used to
+ implement collections using the same comparator.
+*/
+struct LilvHeader {
+ LilvWorld* world;
+ LilvNode* uri;
+};
+
+#ifdef LILV_DYN_MANIFEST
+typedef struct {
+ LilvNode* bundle;
+ void* lib;
+ LV2_Dyn_Manifest_Handle handle;
+ uint32_t refs;
+} LilvDynManifest;
+#endif
+
+typedef struct {
+ LilvWorld* world;
+ LilvNode* uri;
+ char* bundle_path;
+ void* lib;
+ LV2_Descriptor_Function lv2_descriptor;
+ const LV2_Lib_Descriptor* desc;
+ uint32_t refs;
+} LilvLib;
+
+struct LilvPluginImpl {
+ LilvWorld* world;
+ LilvNode* plugin_uri;
+ LilvNode* bundle_uri; ///< Bundle plugin was loaded from
+ LilvNode* binary_uri; ///< lv2:binary
+#ifdef LILV_DYN_MANIFEST
+ LilvDynManifest* dynmanifest;
+#endif
+ const LilvPluginClass* plugin_class;
+ LilvNodes* data_uris; ///< rdfs::seeAlso
+ LilvPort** ports;
+ uint32_t num_ports;
+ bool loaded;
+ bool parse_errors;
+ bool replaced;
+};
+
+struct LilvPluginClassImpl {
+ LilvWorld* world;
+ LilvNode* uri;
+ LilvNode* parent_uri;
+ LilvNode* label;
+};
+
+struct LilvInstancePimpl {
+ LilvWorld* world;
+ LilvLib* lib;
+};
+
+typedef struct {
+ bool dyn_manifest;
+ bool filter_language;
+} LilvOptions;
+
+struct LilvWorldImpl {
+ SordWorld* world;
+ SordModel* model;
+ SerdReader* reader;
+ unsigned n_read_files;
+ LilvPluginClass* lv2_plugin_class;
+ LilvPluginClasses* plugin_classes;
+ LilvSpec* specs;
+ LilvPlugins* plugins;
+ LilvPlugins* zombies;
+ LilvNodes* loaded_files;
+ ZixTree* libs;
+ struct {
+ SordNode* dc_replaces;
+ SordNode* dman_DynManifest;
+ SordNode* doap_name;
+ SordNode* lv2_Plugin;
+ SordNode* lv2_Specification;
+ SordNode* lv2_appliesTo;
+ SordNode* lv2_binary;
+ SordNode* lv2_default;
+ SordNode* lv2_designation;
+ SordNode* lv2_extensionData;
+ SordNode* lv2_index;
+ SordNode* lv2_latency;
+ SordNode* lv2_maximum;
+ SordNode* lv2_microVersion;
+ SordNode* lv2_minimum;
+ SordNode* lv2_minorVersion;
+ SordNode* lv2_name;
+ SordNode* lv2_optionalFeature;
+ SordNode* lv2_port;
+ SordNode* lv2_portProperty;
+ SordNode* lv2_reportsLatency;
+ SordNode* lv2_requiredFeature;
+ SordNode* lv2_symbol;
+ SordNode* lv2_prototype;
+ SordNode* owl_Ontology;
+ SordNode* pset_value;
+ SordNode* rdf_a;
+ SordNode* rdf_value;
+ SordNode* rdfs_Class;
+ SordNode* rdfs_label;
+ SordNode* rdfs_seeAlso;
+ SordNode* rdfs_subClassOf;
+ SordNode* xsd_base64Binary;
+ SordNode* xsd_boolean;
+ SordNode* xsd_decimal;
+ SordNode* xsd_double;
+ SordNode* xsd_integer;
+ SordNode* null_uri;
+ } uris;
+ LilvOptions opt;
+};
+
+typedef enum {
+ LILV_VALUE_URI,
+ LILV_VALUE_STRING,
+ LILV_VALUE_INT,
+ LILV_VALUE_FLOAT,
+ LILV_VALUE_BOOL,
+ LILV_VALUE_BLANK,
+ LILV_VALUE_BLOB
+} LilvNodeType;
+
+struct LilvNodeImpl {
+ LilvWorld* world;
+ SordNode* node;
+ LilvNodeType type;
+ union {
+ int int_val;
+ float float_val;
+ bool bool_val;
+ } val;
+};
+
+struct LilvScalePointImpl {
+ LilvNode* value;
+ LilvNode* label;
+};
+
+struct LilvUIImpl {
+ LilvWorld* world;
+ LilvNode* uri;
+ LilvNode* bundle_uri;
+ LilvNode* binary_uri;
+ LilvNodes* classes;
+};
+
+typedef struct LilvVersion {
+ int minor;
+ int micro;
+} LilvVersion;
+
+/*
+ *
+ * Functions
+ *
+ */
+
+LilvPort* lilv_port_new(LilvWorld* world,
+ const SordNode* node,
+ uint32_t index,
+ const char* symbol);
+void lilv_port_free(const LilvPlugin* plugin, LilvPort* port);
+
+LilvPlugin* lilv_plugin_new(LilvWorld* world,
+ LilvNode* uri,
+ LilvNode* bundle_uri);
+void lilv_plugin_clear(LilvPlugin* plugin, LilvNode* bundle_uri);
+void lilv_plugin_load_if_necessary(const LilvPlugin* plugin);
+void lilv_plugin_free(LilvPlugin* plugin);
+LilvNode* lilv_plugin_get_unique(const LilvPlugin* plugin,
+ const SordNode* subject,
+ const SordNode* predicate);
+
+void lilv_collection_free(LilvCollection* collection);
+unsigned lilv_collection_size(const LilvCollection* collection);
+LilvIter* lilv_collection_begin(const LilvCollection* collection);
+void* lilv_collection_get(const LilvCollection* collection,
+ const LilvIter* i);
+
+LilvPluginClass* lilv_plugin_class_new(LilvWorld* world,
+ const SordNode* parent_node,
+ const SordNode* uri,
+ const char* label);
+
+void lilv_plugin_class_free(LilvPluginClass* plugin_class);
+
+LilvLib*
+lilv_lib_open(LilvWorld* world,
+ const LilvNode* uri,
+ const char* bundle_path,
+ const LV2_Feature*const* features);
+
+const LV2_Descriptor* lilv_lib_get_plugin(LilvLib* lib, uint32_t index);
+void lilv_lib_close(LilvLib* lib);
+
+LilvNodes* lilv_nodes_new(void);
+LilvPlugins* lilv_plugins_new(void);
+LilvScalePoints* lilv_scale_points_new(void);
+LilvPluginClasses* lilv_plugin_classes_new(void);
+LilvUIs* lilv_uis_new(void);
+
+LilvNode* lilv_world_get_manifest_uri(LilvWorld* world,
+ const LilvNode* bundle_uri);
+
+const uint8_t* lilv_world_blank_node_prefix(LilvWorld* world);
+
+SerdStatus lilv_world_load_file(LilvWorld* world,
+ SerdReader* reader,
+ const LilvNode* uri);
+
+SerdStatus
+lilv_world_load_graph(LilvWorld* world,
+ SordNode* graph,
+ const LilvNode* uri);
+
+LilvUI* lilv_ui_new(LilvWorld* world,
+ LilvNode* uri,
+ LilvNode* type_uri,
+ LilvNode* binary_uri);
+
+void lilv_ui_free(LilvUI* ui);
+
+LilvNode* lilv_node_new(LilvWorld* world, LilvNodeType type, const char* str);
+LilvNode* lilv_node_new_from_node(LilvWorld* world, const SordNode* node);
+
+int lilv_header_compare_by_uri(const void* a, const void* b, void* user_data);
+int lilv_lib_compare(const void* a, const void* b, void* user_data);
+
+int lilv_ptr_cmp(const void* a, const void* b, void* user_data);
+int lilv_resource_node_cmp(const void* a, const void* b, void* user_data);
+
+static inline int
+lilv_version_cmp(const LilvVersion* a, const LilvVersion* b)
+{
+ if (a->minor == b->minor && a->micro == b->micro) {
+ return 0;
+ } else if ((a->minor < b->minor)
+ || (a->minor == b->minor && a->micro < b->micro)) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+struct LilvHeader*
+lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri);
+
+LilvScalePoint* lilv_scale_point_new(LilvNode* value, LilvNode* label);
+void lilv_scale_point_free(LilvScalePoint* point);
+
+SordIter*
+lilv_world_query_internal(LilvWorld* world,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object);
+
+bool
+lilv_world_ask_internal(LilvWorld* world,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object);
+
+LilvNodes*
+lilv_world_find_nodes_internal(LilvWorld* world,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object);
+
+SordModel*
+lilv_world_filter_model(LilvWorld* world,
+ SordModel* model,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object,
+ const SordNode* graph);
+
+#define FOREACH_MATCH(iter) \
+ for (; !sord_iter_end(iter); sord_iter_next(iter))
+
+LilvNodes* lilv_nodes_from_stream_objects(LilvWorld* world,
+ SordIter* stream,
+ SordQuadIndex field);
+
+char* lilv_strjoin(const char* first, ...);
+char* lilv_strdup(const char* str);
+char* lilv_get_lang(void);
+char* lilv_expand(const char* path);
+char* lilv_dirname(const char* path);
+int lilv_copy_file(const char* src, const char* dst);
+bool lilv_path_exists(const char* path, const void* ignored);
+char* lilv_path_absolute(const char* path);
+bool lilv_path_is_absolute(const char* path);
+char* lilv_get_latest_copy(const char* path, const char* copy_path);
+char* lilv_path_relative_to(const char* path, const char* base);
+bool lilv_path_is_child(const char* path, const char* dir);
+int lilv_flock(FILE* file, bool lock);
+char* lilv_realpath(const char* path);
+int lilv_symlink(const char* oldpath, const char* newpath);
+int lilv_mkdir_p(const char* dir_path);
+char* lilv_path_join(const char* a, const char* b);
+bool lilv_file_equals(const char* a_path, const char* b_path);
+
+char*
+lilv_find_free_path(const char* in_path,
+ bool (*exists)(const char*, const void*),
+ const void* user_data);
+
+void
+lilv_dir_for_each(const char* path,
+ void* data,
+ void (*f)(const char* path, const char* name, void* data));
+
+typedef void (*LilvVoidFunc)(void);
+
+/** dlsym wrapper to return a function pointer (without annoying warning) */
+static inline LilvVoidFunc
+lilv_dlfunc(void* handle, const char* symbol)
+{
+#ifdef _WIN32
+ return (LilvVoidFunc)GetProcAddress((HMODULE)handle, symbol);
+#else
+ typedef LilvVoidFunc (*VoidFuncGetter)(void*, const char*);
+ VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym;
+ return dlfunc(handle, symbol);
+#endif
+}
+
+#ifdef LILV_DYN_MANIFEST
+static const LV2_Feature* const dman_features = { NULL };
+#endif
+
+#define LILV_ERROR(str) fprintf(stderr, "%s(): error: " str, \
+ __func__)
+#define LILV_ERRORF(fmt, ...) fprintf(stderr, "%s(): error: " fmt, \
+ __func__, __VA_ARGS__)
+#define LILV_WARN(str) fprintf(stderr, "%s(): warning: " str, \
+ __func__)
+#define LILV_WARNF(fmt, ...) fprintf(stderr, "%s(): warning: " fmt, \
+ __func__, __VA_ARGS__)
+#define LILV_NOTE(str) fprintf(stderr, "%s(): note: " str, \
+ __func__)
+#define LILV_NOTEF(fmt, ...) fprintf(stderr, "%s(): note: " fmt, \
+ __func__, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LILV_INTERNAL_H */
diff --git a/src/node.c b/src/node.c
new file mode 100644
index 0000000..5605177
--- /dev/null
+++ b/src/node.c
@@ -0,0 +1,397 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv_internal.h"
+
+static void
+lilv_node_set_numerics_from_string(LilvNode* val)
+{
+ const char* str = (const char*)sord_node_get_string(val->node);
+
+ switch (val->type) {
+ case LILV_VALUE_URI:
+ case LILV_VALUE_BLANK:
+ case LILV_VALUE_STRING:
+ case LILV_VALUE_BLOB:
+ break;
+ case LILV_VALUE_INT:
+ val->val.int_val = strtol(str, NULL, 10);
+ break;
+ case LILV_VALUE_FLOAT:
+ val->val.float_val = serd_strtod(str, NULL);
+ break;
+ case LILV_VALUE_BOOL:
+ val->val.bool_val = !strcmp(str, "true");
+ break;
+ }
+}
+
+/** Note that if `type` is numeric or boolean, the returned value is corrupt
+ * until lilv_node_set_numerics_from_string is called. It is not
+ * automatically called from here to avoid overhead and imprecision when the
+ * exact string value is known.
+ */
+LilvNode*
+lilv_node_new(LilvWorld* world, LilvNodeType type, const char* str)
+{
+ LilvNode* val = (LilvNode*)malloc(sizeof(LilvNode));
+ val->world = world;
+ val->type = type;
+
+ const uint8_t* ustr = (const uint8_t*)str;
+ switch (type) {
+ case LILV_VALUE_URI:
+ val->node = sord_new_uri(world->world, ustr);
+ break;
+ case LILV_VALUE_BLANK:
+ val->node = sord_new_blank(world->world, ustr);
+ break;
+ case LILV_VALUE_STRING:
+ val->node = sord_new_literal(world->world, NULL, ustr, NULL);
+ break;
+ case LILV_VALUE_INT:
+ val->node = sord_new_literal(
+ world->world, world->uris.xsd_integer, ustr, NULL);
+ break;
+ case LILV_VALUE_FLOAT:
+ val->node = sord_new_literal(
+ world->world, world->uris.xsd_decimal, ustr, NULL);
+ break;
+ case LILV_VALUE_BOOL:
+ val->node = sord_new_literal(
+ world->world, world->uris.xsd_boolean, ustr, NULL);
+ break;
+ case LILV_VALUE_BLOB:
+ val->node = sord_new_literal(
+ world->world, world->uris.xsd_base64Binary, ustr, NULL);
+ break;
+ }
+
+ if (!val->node) {
+ free(val);
+ return NULL;
+ }
+
+ return val;
+}
+
+/** Create a new LilvNode from `node`, or return NULL if impossible */
+LilvNode*
+lilv_node_new_from_node(LilvWorld* world, const SordNode* node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ LilvNode* result = NULL;
+ SordNode* datatype_uri = NULL;
+ LilvNodeType type = LILV_VALUE_STRING;
+
+ switch (sord_node_get_type(node)) {
+ case SORD_URI:
+ result = (LilvNode*)malloc(sizeof(LilvNode));
+ result->world = world;
+ result->type = LILV_VALUE_URI;
+ result->node = sord_node_copy(node);
+ break;
+ case SORD_BLANK:
+ result = (LilvNode*)malloc(sizeof(LilvNode));
+ result->world = world;
+ result->type = LILV_VALUE_BLANK;
+ result->node = sord_node_copy(node);
+ break;
+ case SORD_LITERAL:
+ datatype_uri = sord_node_get_datatype(node);
+ if (datatype_uri) {
+ if (sord_node_equals(datatype_uri, world->uris.xsd_boolean)) {
+ type = LILV_VALUE_BOOL;
+ } else if (sord_node_equals(datatype_uri, world->uris.xsd_decimal) ||
+ sord_node_equals(datatype_uri, world->uris.xsd_double)) {
+ type = LILV_VALUE_FLOAT;
+ } else if (sord_node_equals(datatype_uri, world->uris.xsd_integer)) {
+ type = LILV_VALUE_INT;
+ } else if (sord_node_equals(datatype_uri,
+ world->uris.xsd_base64Binary)) {
+ type = LILV_VALUE_BLOB;
+ } else {
+ LILV_ERRORF("Unknown datatype `%s'\n",
+ sord_node_get_string(datatype_uri));
+ }
+ }
+ result = lilv_node_new(
+ world, type, (const char*)sord_node_get_string(node));
+ lilv_node_set_numerics_from_string(result);
+ break;
+ }
+
+ return result;
+}
+
+LILV_API LilvNode*
+lilv_new_uri(LilvWorld* world, const char* uri)
+{
+ return lilv_node_new(world, LILV_VALUE_URI, uri);
+}
+
+LILV_API LilvNode*
+lilv_new_file_uri(LilvWorld* world, const char* host, const char* path)
+{
+ char* abs_path = lilv_path_absolute(path);
+ SerdNode s = serd_node_new_file_uri(
+ (const uint8_t*)abs_path, (const uint8_t*)host, NULL, true);
+
+ LilvNode* ret = lilv_node_new(world, LILV_VALUE_URI, (const char*)s.buf);
+ serd_node_free(&s);
+ free(abs_path);
+ return ret;
+}
+
+LILV_API LilvNode*
+lilv_new_string(LilvWorld* world, const char* str)
+{
+ return lilv_node_new(world, LILV_VALUE_STRING, str);
+}
+
+LILV_API LilvNode*
+lilv_new_int(LilvWorld* world, int val)
+{
+ char str[32];
+ snprintf(str, sizeof(str), "%d", val);
+ LilvNode* ret = lilv_node_new(world, LILV_VALUE_INT, str);
+ ret->val.int_val = val;
+ return ret;
+}
+
+LILV_API LilvNode*
+lilv_new_float(LilvWorld* world, float val)
+{
+ char str[32];
+ snprintf(str, sizeof(str), "%f", val);
+ LilvNode* ret = lilv_node_new(world, LILV_VALUE_FLOAT, str);
+ ret->val.float_val = val;
+ return ret;
+}
+
+LILV_API LilvNode*
+lilv_new_bool(LilvWorld* world, bool val)
+{
+ LilvNode* ret = lilv_node_new(world, LILV_VALUE_BOOL,
+ val ? "true" : "false");
+ ret->val.bool_val = val;
+ return ret;
+}
+
+LILV_API LilvNode*
+lilv_node_duplicate(const LilvNode* val)
+{
+ if (!val) {
+ return NULL;
+ }
+
+ LilvNode* result = (LilvNode*)malloc(sizeof(LilvNode));
+ result->world = val->world;
+ result->node = sord_node_copy(val->node);
+ result->val = val->val;
+ result->type = val->type;
+ return result;
+}
+
+LILV_API void
+lilv_node_free(LilvNode* val)
+{
+ if (val) {
+ sord_node_free(val->world->world, val->node);
+ free(val);
+ }
+}
+
+LILV_API bool
+lilv_node_equals(const LilvNode* value, const LilvNode* other)
+{
+ if (value == NULL && other == NULL) {
+ return true;
+ } else if (value == NULL || other == NULL) {
+ return false;
+ } else if (value->type != other->type) {
+ return false;
+ }
+
+ switch (value->type) {
+ case LILV_VALUE_URI:
+ case LILV_VALUE_BLANK:
+ case LILV_VALUE_STRING:
+ case LILV_VALUE_BLOB:
+ return sord_node_equals(value->node, other->node);
+ case LILV_VALUE_INT:
+ return (value->val.int_val == other->val.int_val);
+ case LILV_VALUE_FLOAT:
+ return (value->val.float_val == other->val.float_val);
+ case LILV_VALUE_BOOL:
+ return (value->val.bool_val == other->val.bool_val);
+ }
+
+ return false; /* shouldn't get here */
+}
+
+LILV_API char*
+lilv_node_get_turtle_token(const LilvNode* value)
+{
+ const char* str = (const char*)sord_node_get_string(value->node);
+ size_t len = 0;
+ char* result = NULL;
+ SerdNode node;
+
+ switch (value->type) {
+ case LILV_VALUE_URI:
+ len = strlen(str) + 3;
+ result = (char*)calloc(len, 1);
+ snprintf(result, len, "<%s>", str);
+ break;
+ case LILV_VALUE_BLANK:
+ len = strlen(str) + 3;
+ result = (char*)calloc(len, 1);
+ snprintf(result, len, "_:%s", str);
+ break;
+ case LILV_VALUE_STRING:
+ case LILV_VALUE_BOOL:
+ case LILV_VALUE_BLOB:
+ result = lilv_strdup(str);
+ break;
+ case LILV_VALUE_INT:
+ node = serd_node_new_integer(value->val.int_val);
+ result = (char*)node.buf;
+ break;
+ case LILV_VALUE_FLOAT:
+ node = serd_node_new_decimal(value->val.float_val, 8);
+ result = (char*)node.buf;
+ break;
+ }
+
+ return result;
+}
+
+LILV_API bool
+lilv_node_is_uri(const LilvNode* value)
+{
+ return (value && value->type == LILV_VALUE_URI);
+}
+
+LILV_API const char*
+lilv_node_as_uri(const LilvNode* value)
+{
+ return (lilv_node_is_uri(value)
+ ? (const char*)sord_node_get_string(value->node)
+ : NULL);
+}
+
+LILV_API bool
+lilv_node_is_blank(const LilvNode* value)
+{
+ return (value && value->type == LILV_VALUE_BLANK);
+}
+
+LILV_API const char*
+lilv_node_as_blank(const LilvNode* value)
+{
+ return (lilv_node_is_blank(value)
+ ? (const char*)sord_node_get_string(value->node)
+ : NULL);
+}
+
+LILV_API bool
+lilv_node_is_literal(const LilvNode* value)
+{
+ if (!value) {
+ return false;
+ }
+
+ switch (value->type) {
+ case LILV_VALUE_STRING:
+ case LILV_VALUE_INT:
+ case LILV_VALUE_FLOAT:
+ case LILV_VALUE_BLOB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+LILV_API bool
+lilv_node_is_string(const LilvNode* value)
+{
+ return (value && value->type == LILV_VALUE_STRING);
+}
+
+LILV_API const char*
+lilv_node_as_string(const LilvNode* value)
+{
+ return value ? (const char*)sord_node_get_string(value->node) : NULL;
+}
+
+LILV_API bool
+lilv_node_is_int(const LilvNode* value)
+{
+ return (value && value->type == LILV_VALUE_INT);
+}
+
+LILV_API int
+lilv_node_as_int(const LilvNode* value)
+{
+ return lilv_node_is_int(value) ? value->val.int_val : 0;
+}
+
+LILV_API bool
+lilv_node_is_float(const LilvNode* value)
+{
+ return (value && value->type == LILV_VALUE_FLOAT);
+}
+
+LILV_API float
+lilv_node_as_float(const LilvNode* value)
+{
+ if (lilv_node_is_float(value)) {
+ return value->val.float_val;
+ } else if (lilv_node_is_int(value)) {
+ return (float)value->val.int_val;
+ }
+ return NAN;
+}
+
+LILV_API bool
+lilv_node_is_bool(const LilvNode* value)
+{
+ return (value && value->type == LILV_VALUE_BOOL);
+}
+
+LILV_API bool
+lilv_node_as_bool(const LilvNode* value)
+{
+ return lilv_node_is_bool(value) ? value->val.bool_val : false;
+}
+
+LILV_API char*
+lilv_node_get_path(const LilvNode* value, char** hostname)
+{
+ if (lilv_node_is_uri(value)) {
+ return lilv_file_uri_parse(lilv_node_as_uri(value), hostname);
+ }
+ return NULL;
+}
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644
index 0000000..0575b32
--- /dev/null
+++ b/src/plugin.c
@@ -0,0 +1,1138 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define __STDC_LIMIT_MACROS
+
+#include <assert.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv_config.h"
+#include "lilv_internal.h"
+
+#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
+
+#define NS_DOAP (const uint8_t*)"http://usefulinc.com/ns/doap#"
+#define NS_FOAF (const uint8_t*)"http://xmlns.com/foaf/0.1/"
+
+static void
+lilv_plugin_init(LilvPlugin* plugin, LilvNode* bundle_uri)
+{
+ plugin->bundle_uri = bundle_uri;
+ plugin->binary_uri = NULL;
+#ifdef LILV_DYN_MANIFEST
+ plugin->dynmanifest = NULL;
+#endif
+ plugin->plugin_class = NULL;
+ plugin->data_uris = lilv_nodes_new();
+ plugin->ports = NULL;
+ plugin->num_ports = 0;
+ plugin->loaded = false;
+ plugin->parse_errors = false;
+ plugin->replaced = false;
+}
+
+/** Ownership of `uri` and `bundle` is taken */
+LilvPlugin*
+lilv_plugin_new(LilvWorld* world, LilvNode* uri, LilvNode* bundle_uri)
+{
+ LilvPlugin* plugin = (LilvPlugin*)malloc(sizeof(LilvPlugin));
+
+ plugin->world = world;
+ plugin->plugin_uri = uri;
+
+ lilv_plugin_init(plugin, bundle_uri);
+ return plugin;
+}
+
+void
+lilv_plugin_clear(LilvPlugin* plugin, LilvNode* bundle_uri)
+{
+ lilv_node_free(plugin->bundle_uri);
+ lilv_node_free(plugin->binary_uri);
+ lilv_nodes_free(plugin->data_uris);
+ lilv_plugin_init(plugin, bundle_uri);
+}
+
+static void
+lilv_plugin_free_ports(LilvPlugin* plugin)
+{
+ if (plugin->ports) {
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ lilv_port_free(plugin, plugin->ports[i]);
+ }
+ free(plugin->ports);
+ plugin->num_ports = 0;
+ plugin->ports = NULL;
+ }
+}
+
+void
+lilv_plugin_free(LilvPlugin* plugin)
+{
+#ifdef LILV_DYN_MANIFEST
+ if (plugin->dynmanifest && --plugin->dynmanifest->refs == 0) {
+ typedef int (*CloseFunc)(LV2_Dyn_Manifest_Handle);
+ CloseFunc close_func = (CloseFunc)lilv_dlfunc(plugin->dynmanifest->lib,
+ "lv2_dyn_manifest_close");
+ if (close_func) {
+ close_func(plugin->dynmanifest->handle);
+ }
+
+ dlclose(plugin->dynmanifest->lib);
+ lilv_node_free(plugin->dynmanifest->bundle);
+ free(plugin->dynmanifest);
+ }
+#endif
+
+ lilv_node_free(plugin->plugin_uri);
+ plugin->plugin_uri = NULL;
+
+ lilv_node_free(plugin->bundle_uri);
+ plugin->bundle_uri = NULL;
+
+ lilv_node_free(plugin->binary_uri);
+ plugin->binary_uri = NULL;
+
+ lilv_plugin_free_ports(plugin);
+
+ lilv_nodes_free(plugin->data_uris);
+ plugin->data_uris = NULL;
+
+ free(plugin);
+}
+
+static LilvNode*
+lilv_plugin_get_one(const LilvPlugin* plugin,
+ const SordNode* subject,
+ const SordNode* predicate)
+{
+ LilvNode* ret = NULL;
+ SordIter* stream = lilv_world_query_internal(
+ plugin->world, subject, predicate, NULL);
+ if (!sord_iter_end(stream)) {
+ ret = lilv_node_new_from_node(plugin->world,
+ sord_iter_get_node(stream, SORD_OBJECT));
+ }
+ sord_iter_free(stream);
+ return ret;
+}
+
+LilvNode*
+lilv_plugin_get_unique(const LilvPlugin* plugin,
+ const SordNode* subject,
+ const SordNode* predicate)
+{
+ LilvNode* ret = lilv_plugin_get_one(plugin, subject, predicate);
+ if (!ret) {
+ LILV_ERRORF("No value found for (%s %s ...) property\n",
+ sord_node_get_string(subject),
+ sord_node_get_string(predicate));
+ }
+ return ret;
+}
+
+static void
+lilv_plugin_load(LilvPlugin* plugin)
+{
+ SordNode* bundle_uri_node = plugin->bundle_uri->node;
+ const SerdNode* bundle_uri_snode = sord_node_to_serd_node(bundle_uri_node);
+
+ SerdEnv* env = serd_env_new(bundle_uri_snode);
+ SerdReader* reader = sord_new_reader(plugin->world->model, env, SERD_TURTLE,
+ bundle_uri_node);
+
+ SordModel* prots = lilv_world_filter_model(
+ plugin->world,
+ plugin->world->model,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_prototype,
+ NULL, NULL);
+ SordModel* skel = sord_new(plugin->world->world, SORD_SPO, false);
+ SordIter* iter = sord_begin(prots);
+ for (; !sord_iter_end(iter); sord_iter_next(iter)) {
+ const SordNode* t = sord_iter_get_node(iter, SORD_OBJECT);
+ LilvNode* prototype = lilv_node_new_from_node(plugin->world, t);
+
+ lilv_world_load_resource(plugin->world, prototype);
+
+ SordIter* statements = sord_search(
+ plugin->world->model, prototype->node, NULL, NULL, NULL);
+ FOREACH_MATCH(statements) {
+ SordQuad quad;
+ sord_iter_get(statements, quad);
+ quad[0] = plugin->plugin_uri->node;
+ sord_add(skel, quad);
+ }
+
+ sord_iter_free(statements);
+ lilv_node_free(prototype);
+ }
+ sord_iter_free(iter);
+
+ for (iter = sord_begin(skel); !sord_iter_end(iter); sord_iter_next(iter)) {
+ SordQuad quad;
+ sord_iter_get(iter, quad);
+ sord_add(plugin->world->model, quad);
+ }
+ sord_iter_free(iter);
+ sord_free(skel);
+ sord_free(prots);
+
+ // Parse all the plugin's data files into RDF model
+ SerdStatus st = SERD_SUCCESS;
+ LILV_FOREACH(nodes, i, plugin->data_uris) {
+ const LilvNode* data_uri = lilv_nodes_get(plugin->data_uris, i);
+
+ serd_env_set_base_uri(env, sord_node_to_serd_node(data_uri->node));
+ st = lilv_world_load_file(plugin->world, reader, data_uri);
+ if (st > SERD_FAILURE) {
+ break;
+ }
+ }
+
+ if (st > SERD_FAILURE) {
+ plugin->loaded = true;
+ plugin->parse_errors = true;
+ serd_reader_free(reader);
+ serd_env_free(env);
+ return;
+ }
+
+#ifdef LILV_DYN_MANIFEST
+ // Load and parse dynamic manifest data, if this is a library
+ if (plugin->dynmanifest) {
+ typedef int (*GetDataFunc)(LV2_Dyn_Manifest_Handle handle,
+ FILE* fp,
+ const char* uri);
+ GetDataFunc get_data_func = (GetDataFunc)lilv_dlfunc(
+ plugin->dynmanifest->lib, "lv2_dyn_manifest_get_data");
+ if (get_data_func) {
+ const SordNode* bundle = plugin->dynmanifest->bundle->node;
+ serd_env_set_base_uri(env, sord_node_to_serd_node(bundle));
+ FILE* fd = tmpfile();
+ get_data_func(plugin->dynmanifest->handle, fd,
+ lilv_node_as_string(plugin->plugin_uri));
+ rewind(fd);
+ serd_reader_add_blank_prefix(
+ reader, lilv_world_blank_node_prefix(plugin->world));
+ serd_reader_read_file_handle(
+ reader, fd, (const uint8_t*)"(dyn-manifest)");
+ fclose(fd);
+ }
+ }
+#endif
+ serd_reader_free(reader);
+ serd_env_free(env);
+
+ plugin->loaded = true;
+}
+
+static bool
+is_symbol(const char* str)
+{
+ for (const char* s = str; *s; ++s) {
+ if (!((*s >= 'a' && *s <= 'z') ||
+ (*s >= 'A' && *s <= 'Z') ||
+ (s > str && *s >= '0' && *s <= '9') ||
+ *s == '_')) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void
+lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_plugin)
+{
+ LilvPlugin* plugin = (LilvPlugin*)const_plugin;
+
+ lilv_plugin_load_if_necessary(plugin);
+
+ if (!plugin->ports) {
+ plugin->ports = (LilvPort**)malloc(sizeof(LilvPort*));
+ plugin->ports[0] = NULL;
+
+ SordIter* ports = lilv_world_query_internal(
+ plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_port,
+ NULL);
+
+ FOREACH_MATCH(ports) {
+ const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT);
+ LilvNode* index = lilv_plugin_get_unique(
+ plugin, port, plugin->world->uris.lv2_index);
+ LilvNode* symbol = lilv_plugin_get_unique(
+ plugin, port, plugin->world->uris.lv2_symbol);
+
+ if (!lilv_node_is_string(symbol) ||
+ !is_symbol((const char*)sord_node_get_string(symbol->node))) {
+ LILV_ERRORF("Plugin <%s> port symbol `%s' is invalid\n",
+ lilv_node_as_uri(plugin->plugin_uri),
+ lilv_node_as_string(symbol));
+ lilv_node_free(symbol);
+ lilv_node_free(index);
+ lilv_plugin_free_ports(plugin);
+ break;
+ }
+
+ if (!lilv_node_is_int(index)) {
+ LILV_ERRORF("Plugin <%s> port index is not an integer\n",
+ lilv_node_as_uri(plugin->plugin_uri));
+ lilv_node_free(symbol);
+ lilv_node_free(index);
+ lilv_plugin_free_ports(plugin);
+ break;
+ }
+
+ uint32_t this_index = lilv_node_as_int(index);
+ LilvPort* this_port = NULL;
+ if (plugin->num_ports > this_index) {
+ this_port = plugin->ports[this_index];
+ } else {
+ plugin->ports = (LilvPort**)realloc(
+ plugin->ports, (this_index + 1) * sizeof(LilvPort*));
+ memset(plugin->ports + plugin->num_ports, '\0',
+ (this_index - plugin->num_ports) * sizeof(LilvPort*));
+ plugin->num_ports = this_index + 1;
+ }
+
+ // Havn't seen this port yet, add it to array
+ if (!this_port) {
+ this_port = lilv_port_new(plugin->world,
+ port,
+ this_index,
+ lilv_node_as_string(symbol));
+ plugin->ports[this_index] = this_port;
+ }
+
+ SordIter* types = lilv_world_query_internal(
+ plugin->world, port, plugin->world->uris.rdf_a, NULL);
+ FOREACH_MATCH(types) {
+ const SordNode* type = sord_iter_get_node(types, SORD_OBJECT);
+ if (sord_node_get_type(type) == SORD_URI) {
+ zix_tree_insert(
+ (ZixTree*)this_port->classes,
+ lilv_node_new_from_node(plugin->world, type), NULL);
+ } else {
+ LILV_WARNF("Plugin <%s> port type is not a URI\n",
+ lilv_node_as_uri(plugin->plugin_uri));
+ }
+ }
+ sord_iter_free(types);
+
+ lilv_node_free(symbol);
+ lilv_node_free(index);
+ }
+ sord_iter_free(ports);
+
+ // Check sanity
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ if (!plugin->ports[i]) {
+ LILV_ERRORF("Plugin <%s> is missing port %d/%d\n",
+ lilv_node_as_uri(plugin->plugin_uri), i, plugin->num_ports);
+ lilv_plugin_free_ports(plugin);
+ break;
+ }
+ }
+ }
+}
+
+void
+lilv_plugin_load_if_necessary(const LilvPlugin* plugin)
+{
+ if (!plugin->loaded) {
+ lilv_plugin_load((LilvPlugin*)plugin);
+ }
+}
+
+LILV_API const LilvNode*
+lilv_plugin_get_uri(const LilvPlugin* plugin)
+{
+ return plugin->plugin_uri;
+}
+
+LILV_API const LilvNode*
+lilv_plugin_get_bundle_uri(const LilvPlugin* plugin)
+{
+ return plugin->bundle_uri;
+}
+
+LILV_API const LilvNode*
+lilv_plugin_get_library_uri(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary((LilvPlugin*)plugin);
+ if (!plugin->binary_uri) {
+ // <plugin> lv2:binary ?binary
+ SordIter* i = lilv_world_query_internal(plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_binary,
+ NULL);
+ FOREACH_MATCH(i) {
+ const SordNode* binary_node = sord_iter_get_node(i, SORD_OBJECT);
+ if (sord_node_get_type(binary_node) == SORD_URI) {
+ ((LilvPlugin*)plugin)->binary_uri =
+ lilv_node_new_from_node(plugin->world, binary_node);
+ break;
+ }
+ }
+ sord_iter_free(i);
+ }
+ if (!plugin->binary_uri) {
+ LILV_WARNF("Plugin <%s> has no lv2:binary\n",
+ lilv_node_as_uri(lilv_plugin_get_uri(plugin)));
+ }
+ return plugin->binary_uri;
+}
+
+LILV_API const LilvNodes*
+lilv_plugin_get_data_uris(const LilvPlugin* plugin)
+{
+ return plugin->data_uris;
+}
+
+LILV_API const LilvPluginClass*
+lilv_plugin_get_class(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary((LilvPlugin*)plugin);
+ if (!plugin->plugin_class) {
+ // <plugin> a ?class
+ SordIter* c = lilv_world_query_internal(plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.rdf_a,
+ NULL);
+ FOREACH_MATCH(c) {
+ const SordNode* class_node = sord_iter_get_node(c, SORD_OBJECT);
+ if (sord_node_get_type(class_node) != SORD_URI) {
+ continue;
+ }
+
+ LilvNode* klass = lilv_node_new_from_node(plugin->world, class_node);
+ if (!lilv_node_equals(klass, plugin->world->lv2_plugin_class->uri)) {
+ const LilvPluginClass* pclass = lilv_plugin_classes_get_by_uri(
+ plugin->world->plugin_classes, klass);
+
+ if (pclass) {
+ ((LilvPlugin*)plugin)->plugin_class = pclass;
+ lilv_node_free(klass);
+ break;
+ }
+ }
+
+ lilv_node_free(klass);
+ }
+ sord_iter_free(c);
+
+ if (plugin->plugin_class == NULL) {
+ ((LilvPlugin*)plugin)->plugin_class =
+ plugin->world->lv2_plugin_class;
+ }
+ }
+ return plugin->plugin_class;
+}
+
+static LilvNodes*
+lilv_plugin_get_value_internal(const LilvPlugin* plugin,
+ const SordNode* predicate)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_find_nodes_internal(
+ plugin->world, plugin->plugin_uri->node, predicate, NULL);
+}
+
+LILV_API bool
+lilv_plugin_verify(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ if (plugin->parse_errors) {
+ return false;
+ }
+
+ LilvNode* rdf_type = lilv_new_uri(plugin->world, LILV_NS_RDF "type");
+ LilvNodes* results = lilv_plugin_get_value(plugin, rdf_type);
+ lilv_node_free(rdf_type);
+ if (!results) {
+ return false;
+ }
+
+ lilv_nodes_free(results);
+ results = lilv_plugin_get_value_internal(plugin,
+ plugin->world->uris.doap_name);
+ if (!results) {
+ return false;
+ }
+
+ lilv_nodes_free(results);
+ LilvNode* lv2_port = lilv_new_uri(plugin->world, LV2_CORE__port);
+ results = lilv_plugin_get_value(plugin, lv2_port);
+ lilv_node_free(lv2_port);
+ if (!results) {
+ return false;
+ }
+
+ lilv_nodes_free(results);
+ return true;
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_name(const LilvPlugin* plugin)
+{
+ LilvNodes* results = lilv_plugin_get_value_internal(
+ plugin, plugin->world->uris.doap_name);
+
+ LilvNode* ret = NULL;
+ if (results) {
+ LilvNode* val = lilv_nodes_get_first(results);
+ if (lilv_node_is_string(val)) {
+ ret = lilv_node_duplicate(val);
+ }
+ lilv_nodes_free(results);
+ }
+
+ if (!ret) {
+ LILV_WARNF("Plugin <%s> has no (mandatory) doap:name\n",
+ lilv_node_as_string(lilv_plugin_get_uri(plugin)));
+ }
+
+ return ret;
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_value(const LilvPlugin* plugin,
+ const LilvNode* predicate)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_find_nodes(plugin->world, plugin->plugin_uri, predicate, NULL);
+}
+
+LILV_API uint32_t
+lilv_plugin_get_num_ports(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ return plugin->num_ports;
+}
+
+LILV_API void
+lilv_plugin_get_port_ranges_float(const LilvPlugin* plugin,
+ float* min_values,
+ float* max_values,
+ float* def_values)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ LilvNode* min = NULL;
+ LilvNode* max = NULL;
+ LilvNode* def = NULL;
+ LilvNode** minptr = min_values ? &min : NULL;
+ LilvNode** maxptr = max_values ? &max : NULL;
+ LilvNode** defptr = def_values ? &def : NULL;
+
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ lilv_port_get_range(plugin, plugin->ports[i], defptr, minptr, maxptr);
+
+ if (min_values) {
+ if (lilv_node_is_float(min) || lilv_node_is_int(min)) {
+ min_values[i] = lilv_node_as_float(min);
+ } else {
+ min_values[i] = NAN;
+ }
+ }
+
+ if (max_values) {
+ if (lilv_node_is_float(max) || lilv_node_is_int(max)) {
+ max_values[i] = lilv_node_as_float(max);
+ } else {
+ max_values[i] = NAN;
+ }
+ }
+
+ if (def_values) {
+ if (lilv_node_is_float(def) || lilv_node_is_int(def)) {
+ def_values[i] = lilv_node_as_float(def);
+ } else {
+ def_values[i] = NAN;
+ }
+ }
+
+ lilv_node_free(def);
+ lilv_node_free(min);
+ lilv_node_free(max);
+ }
+}
+
+LILV_API uint32_t
+lilv_plugin_get_num_ports_of_class_va(const LilvPlugin* plugin,
+ const LilvNode* class_1,
+ va_list args)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+
+ uint32_t count = 0;
+
+ // Build array of classes from args so we can walk it several times
+ size_t n_classes = 0;
+ const LilvNode** classes = NULL;
+ for (LilvNode* c = NULL; (c = va_arg(args, LilvNode*)); ) {
+ classes = (const LilvNode**)realloc(
+ classes, ++n_classes * sizeof(LilvNode*));
+ classes[n_classes - 1] = c;
+ }
+
+ // Check each port against every type
+ for (unsigned i = 0; i < plugin->num_ports; ++i) {
+ LilvPort* port = plugin->ports[i];
+ if (port && lilv_port_is_a(plugin, port, class_1)) {
+ bool matches = true;
+ for (size_t j = 0; j < n_classes; ++j) {
+ if (!lilv_port_is_a(plugin, port, classes[j])) {
+ matches = false;
+ break;
+ }
+ }
+
+ if (matches) {
+ ++count;
+ }
+ }
+ }
+
+ free(classes);
+ return count;
+}
+
+LILV_API uint32_t
+lilv_plugin_get_num_ports_of_class(const LilvPlugin* plugin,
+ const LilvNode* class_1, ...)
+{
+ va_list args;
+ va_start(args, class_1);
+
+ uint32_t count = lilv_plugin_get_num_ports_of_class_va(plugin, class_1, args);
+
+ va_end(args);
+ return count;
+}
+
+LILV_API bool
+lilv_plugin_has_latency(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ SordIter* ports = lilv_world_query_internal(
+ plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_port,
+ NULL);
+
+ bool ret = false;
+ FOREACH_MATCH(ports) {
+ const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT);
+ SordIter* prop = lilv_world_query_internal(
+ plugin->world,
+ port,
+ plugin->world->uris.lv2_portProperty,
+ plugin->world->uris.lv2_reportsLatency);
+ SordIter* des = lilv_world_query_internal(
+ plugin->world,
+ port,
+ plugin->world->uris.lv2_designation,
+ plugin->world->uris.lv2_latency);
+ const bool latent = !sord_iter_end(prop) || !sord_iter_end(des);
+ sord_iter_free(prop);
+ sord_iter_free(des);
+ if (latent) {
+ ret = true;
+ break;
+ }
+ }
+ sord_iter_free(ports);
+
+ return ret;
+}
+
+static const LilvPort*
+lilv_plugin_get_port_by_property(const LilvPlugin* plugin,
+ const SordNode* port_property)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ LilvPort* port = plugin->ports[i];
+ SordIter* iter = lilv_world_query_internal(
+ plugin->world,
+ port->node->node,
+ plugin->world->uris.lv2_portProperty,
+ port_property);
+
+ const bool found = !sord_iter_end(iter);
+ sord_iter_free(iter);
+
+ if (found) {
+ return port;
+ }
+ }
+
+ return NULL;
+}
+
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_designation(const LilvPlugin* plugin,
+ const LilvNode* port_class,
+ const LilvNode* designation)
+{
+ LilvWorld* world = plugin->world;
+ lilv_plugin_load_ports_if_necessary(plugin);
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ LilvPort* port = plugin->ports[i];
+ SordIter* iter = lilv_world_query_internal(
+ world,
+ port->node->node,
+ world->uris.lv2_designation,
+ designation->node);
+
+ const bool found = !sord_iter_end(iter) &&
+ (!port_class || lilv_port_is_a(plugin, port, port_class));
+ sord_iter_free(iter);
+
+ if (found) {
+ return port;
+ }
+ }
+
+ return NULL;
+}
+
+LILV_API uint32_t
+lilv_plugin_get_latency_port_index(const LilvPlugin* plugin)
+{
+ const LilvPort* prop_port = lilv_plugin_get_port_by_property(
+ plugin, plugin->world->uris.lv2_reportsLatency);
+ const LilvPort* des_port = lilv_plugin_get_port_by_property(
+ plugin, plugin->world->uris.lv2_latency);
+ if (prop_port) {
+ return prop_port->index;
+ } else if (des_port) {
+ return des_port->index;
+ } else {
+ return (uint32_t)-1;
+ }
+}
+
+LILV_API bool
+lilv_plugin_has_feature(const LilvPlugin* plugin,
+ const LilvNode* feature)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ const SordNode* predicates[] = { plugin->world->uris.lv2_requiredFeature,
+ plugin->world->uris.lv2_optionalFeature,
+ NULL };
+
+ for (const SordNode** pred = predicates; *pred; ++pred) {
+ if (lilv_world_ask_internal(
+ plugin->world, plugin->plugin_uri->node, *pred, feature->node)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_supported_features(const LilvPlugin* plugin)
+{
+ LilvNodes* optional = lilv_plugin_get_optional_features(plugin);
+ LilvNodes* required = lilv_plugin_get_required_features(plugin);
+ LilvNodes* result = lilv_nodes_merge(optional, required);
+ lilv_nodes_free(optional);
+ lilv_nodes_free(required);
+ return result;
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_optional_features(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_find_nodes_internal(plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_optionalFeature,
+ NULL);
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_required_features(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_find_nodes_internal(plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_requiredFeature,
+ NULL);
+}
+
+LILV_API bool
+lilv_plugin_has_extension_data(const LilvPlugin* plugin,
+ const LilvNode* uri)
+{
+ if (!lilv_node_is_uri(uri)) {
+ LILV_ERRORF("Extension data `%s' is not a URI\n",
+ sord_node_get_string(uri->node));
+ return false;
+ }
+
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_ask_internal(
+ plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_extensionData,
+ uri->node);
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_extension_data(const LilvPlugin* plugin)
+{
+ return lilv_plugin_get_value_internal(plugin, plugin->world->uris.lv2_extensionData);
+}
+
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_index(const LilvPlugin* plugin,
+ uint32_t index)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ if (index < plugin->num_ports) {
+ return plugin->ports[index];
+ } else {
+ return NULL;
+ }
+}
+
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_symbol(const LilvPlugin* plugin,
+ const LilvNode* symbol)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ LilvPort* port = plugin->ports[i];
+ if (lilv_node_equals(port->symbol, symbol)) {
+ return port;
+ }
+ }
+
+ return NULL;
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_project(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+
+ SordNode* lv2_project = sord_new_uri(plugin->world->world,
+ (const uint8_t*)LV2_CORE__project);
+
+ SordIter* projects = lilv_world_query_internal(plugin->world,
+ plugin->plugin_uri->node,
+ lv2_project,
+ NULL);
+
+ sord_node_free(plugin->world->world, lv2_project);
+
+ if (sord_iter_end(projects)) {
+ sord_iter_free(projects);
+ return NULL;
+ }
+
+ const SordNode* project = sord_iter_get_node(projects, SORD_OBJECT);
+
+ sord_iter_free(projects);
+ return lilv_node_new_from_node(plugin->world, project);
+}
+
+static const SordNode*
+lilv_plugin_get_author(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+
+ SordNode* doap_maintainer = sord_new_uri(
+ plugin->world->world, NS_DOAP "maintainer");
+
+ SordIter* maintainers = lilv_world_query_internal(
+ plugin->world,
+ plugin->plugin_uri->node,
+ doap_maintainer,
+ NULL);
+
+ if (sord_iter_end(maintainers)) {
+ sord_iter_free(maintainers);
+
+ LilvNode* project = lilv_plugin_get_project(plugin);
+ if (!project) {
+ sord_node_free(plugin->world->world, doap_maintainer);
+ return NULL;
+ }
+
+ maintainers = lilv_world_query_internal(
+ plugin->world,
+ project->node,
+ doap_maintainer,
+ NULL);
+
+ lilv_node_free(project);
+ }
+
+ sord_node_free(plugin->world->world, doap_maintainer);
+
+ if (sord_iter_end(maintainers)) {
+ sord_iter_free(maintainers);
+ return NULL;
+ }
+
+ const SordNode* author = sord_iter_get_node(maintainers, SORD_OBJECT);
+
+ sord_iter_free(maintainers);
+ return author;
+}
+
+static LilvNode*
+lilv_plugin_get_author_property(const LilvPlugin* plugin, const uint8_t* uri)
+{
+ const SordNode* author = lilv_plugin_get_author(plugin);
+ if (author) {
+ SordWorld* sworld = plugin->world->world;
+ SordNode* pred = sord_new_uri(sworld, uri);
+ LilvNode* ret = lilv_plugin_get_one(plugin, author, pred);
+ sord_node_free(sworld, pred);
+ return ret;
+ }
+ return NULL;
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_author_name(const LilvPlugin* plugin)
+{
+ return lilv_plugin_get_author_property(plugin, NS_FOAF "name");
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_author_email(const LilvPlugin* plugin)
+{
+ return lilv_plugin_get_author_property(plugin, NS_FOAF "mbox");
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_author_homepage(const LilvPlugin* plugin)
+{
+ return lilv_plugin_get_author_property(plugin, NS_FOAF "homepage");
+}
+
+LILV_API bool
+lilv_plugin_is_replaced(const LilvPlugin* plugin)
+{
+ return plugin->replaced;
+}
+
+LILV_API LilvUIs*
+lilv_plugin_get_uis(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+
+ SordNode* ui_ui_node = sord_new_uri(plugin->world->world,
+ (const uint8_t*)LV2_UI__ui);
+ SordNode* ui_binary_node = sord_new_uri(plugin->world->world,
+ (const uint8_t*)LV2_UI__binary);
+
+ LilvUIs* result = lilv_uis_new();
+ SordIter* uis = lilv_world_query_internal(plugin->world,
+ plugin->plugin_uri->node,
+ ui_ui_node,
+ NULL);
+
+ FOREACH_MATCH(uis) {
+ const SordNode* ui = sord_iter_get_node(uis, SORD_OBJECT);
+
+ LilvNode* type = lilv_plugin_get_unique(plugin, ui, plugin->world->uris.rdf_a);
+ LilvNode* binary = lilv_plugin_get_one(plugin, ui, plugin->world->uris.lv2_binary);
+ if (!binary) {
+ binary = lilv_plugin_get_unique(plugin, ui, ui_binary_node);
+ }
+
+ if (sord_node_get_type(ui) != SORD_URI
+ || !lilv_node_is_uri(type)
+ || !lilv_node_is_uri(binary)) {
+ lilv_node_free(binary);
+ lilv_node_free(type);
+ LILV_ERRORF("Corrupt UI <%s>\n", sord_node_get_string(ui));
+ continue;
+ }
+
+ LilvUI* lilv_ui = lilv_ui_new(
+ plugin->world,
+ lilv_node_new_from_node(plugin->world, ui),
+ type,
+ binary);
+
+ zix_tree_insert((ZixTree*)result, lilv_ui, NULL);
+ }
+ sord_iter_free(uis);
+
+ sord_node_free(plugin->world->world, ui_binary_node);
+ sord_node_free(plugin->world->world, ui_ui_node);
+
+ if (lilv_uis_size(result) > 0) {
+ return result;
+ } else {
+ lilv_uis_free(result);
+ return NULL;
+ }
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type)
+{
+ lilv_plugin_load_if_necessary(plugin);
+
+ LilvWorld* const world = plugin->world;
+ LilvNodes* const related = lilv_world_find_nodes_internal(
+ world,
+ NULL,
+ world->uris.lv2_appliesTo,
+ lilv_plugin_get_uri(plugin)->node);
+
+ if (!type) {
+ return related;
+ }
+
+ LilvNodes* matches = lilv_nodes_new();
+ LILV_FOREACH(nodes, i, related) {
+ LilvNode* node = (LilvNode*)lilv_collection_get((ZixTree*)related, i);
+ if (lilv_world_ask_internal(
+ world, node->node, world->uris.rdf_a, type->node)) {
+ zix_tree_insert((ZixTree*)matches,
+ lilv_node_new_from_node(world, node->node),
+ NULL);
+ }
+ }
+
+ lilv_nodes_free(related);
+ return matches;
+}
+
+static SerdEnv*
+new_lv2_env(const SerdNode* base)
+{
+ SerdEnv* env = serd_env_new(base);
+
+#define USTR(s) ((const uint8_t*)(s))
+ serd_env_set_prefix_from_strings(env, USTR("doap"), USTR(LILV_NS_DOAP));
+ serd_env_set_prefix_from_strings(env, USTR("foaf"), USTR(LILV_NS_FOAF));
+ serd_env_set_prefix_from_strings(env, USTR("lv2"), USTR(LILV_NS_LV2));
+ serd_env_set_prefix_from_strings(env, USTR("owl"), USTR(LILV_NS_OWL));
+ serd_env_set_prefix_from_strings(env, USTR("rdf"), USTR(LILV_NS_RDF));
+ serd_env_set_prefix_from_strings(env, USTR("rdfs"), USTR(LILV_NS_RDFS));
+ serd_env_set_prefix_from_strings(env, USTR("xsd"), USTR(LILV_NS_XSD));
+
+ return env;
+}
+
+static void
+maybe_write_prefixes(SerdWriter* writer, SerdEnv* env, FILE* file)
+{
+ fseek(file, 0, SEEK_END);
+ if (ftell(file) == 0) {
+ serd_env_foreach(
+ env, (SerdPrefixSink)serd_writer_set_prefix, writer);
+ } else {
+ fprintf(file, "\n");
+ }
+}
+
+LILV_API void
+lilv_plugin_write_description(LilvWorld* world,
+ const LilvPlugin* plugin,
+ const LilvNode* base_uri,
+ FILE* plugin_file)
+{
+ const LilvNode* subject = lilv_plugin_get_uri(plugin);
+ const uint32_t num_ports = lilv_plugin_get_num_ports(plugin);
+ const SerdNode* base = sord_node_to_serd_node(base_uri->node);
+ SerdEnv* env = new_lv2_env(base);
+
+ SerdWriter* writer = serd_writer_new(
+ SERD_TURTLE,
+ (SerdStyle)(SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED),
+ env,
+ NULL,
+ serd_file_sink,
+ plugin_file);
+
+ // Write prefixes if this is a new file
+ maybe_write_prefixes(writer, env, plugin_file);
+
+ // Write plugin description
+ SordIter* plug_iter = lilv_world_query_internal(
+ world, subject->node, NULL, NULL);
+ sord_write_iter(plug_iter, writer);
+
+ // Write port descriptions
+ for (uint32_t i = 0; i < num_ports; ++i) {
+ const LilvPort* port = plugin->ports[i];
+ SordIter* port_iter = lilv_world_query_internal(
+ world, port->node->node, NULL, NULL);
+ sord_write_iter(port_iter, writer);
+ }
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+}
+
+LILV_API void
+lilv_plugin_write_manifest_entry(LilvWorld* world,
+ const LilvPlugin* plugin,
+ const LilvNode* base_uri,
+ FILE* manifest_file,
+ const char* plugin_file_path)
+{
+ const LilvNode* subject = lilv_plugin_get_uri(plugin);
+ const SerdNode* base = sord_node_to_serd_node(base_uri->node);
+ SerdEnv* env = new_lv2_env(base);
+
+ SerdWriter* writer = serd_writer_new(
+ SERD_TURTLE,
+ (SerdStyle)(SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED),
+ env,
+ NULL,
+ serd_file_sink,
+ manifest_file);
+
+ // Write prefixes if this is a new file
+ maybe_write_prefixes(writer, env, manifest_file);
+
+ // Write manifest entry
+ serd_writer_write_statement(
+ writer, 0, NULL,
+ sord_node_to_serd_node(subject->node),
+ sord_node_to_serd_node(plugin->world->uris.rdf_a),
+ sord_node_to_serd_node(plugin->world->uris.lv2_Plugin), 0, 0);
+
+ const SerdNode file_node = serd_node_from_string(
+ SERD_URI, (const uint8_t*)plugin_file_path);
+ serd_writer_write_statement(
+ writer, 0, NULL,
+ sord_node_to_serd_node(subject->node),
+ sord_node_to_serd_node(plugin->world->uris.rdfs_seeAlso),
+ &file_node, 0, 0);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+}
diff --git a/src/pluginclass.c b/src/pluginclass.c
new file mode 100644
index 0000000..622ff8f
--- /dev/null
+++ b/src/pluginclass.c
@@ -0,0 +1,88 @@
+/*
+ Copyright 2007-2015 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv_internal.h"
+
+LilvPluginClass*
+lilv_plugin_class_new(LilvWorld* world,
+ const SordNode* parent_node,
+ const SordNode* uri,
+ const char* label)
+{
+ LilvPluginClass* pc = (LilvPluginClass*)malloc(sizeof(LilvPluginClass));
+ pc->world = world;
+ pc->uri = lilv_node_new_from_node(world, uri);
+ pc->label = lilv_node_new(world, LILV_VALUE_STRING, label);
+ pc->parent_uri = (parent_node
+ ? lilv_node_new_from_node(world, parent_node)
+ : NULL);
+ return pc;
+}
+
+void
+lilv_plugin_class_free(LilvPluginClass* plugin_class)
+{
+ if (!plugin_class) {
+ return;
+ }
+
+ lilv_node_free(plugin_class->uri);
+ lilv_node_free(plugin_class->parent_uri);
+ lilv_node_free(plugin_class->label);
+ free(plugin_class);
+}
+
+LILV_API const LilvNode*
+lilv_plugin_class_get_parent_uri(const LilvPluginClass* plugin_class)
+{
+ return plugin_class->parent_uri ? plugin_class->parent_uri : NULL;
+}
+
+LILV_API const LilvNode*
+lilv_plugin_class_get_uri(const LilvPluginClass* plugin_class)
+{
+ return plugin_class->uri;
+}
+
+LILV_API const LilvNode*
+lilv_plugin_class_get_label(const LilvPluginClass* plugin_class)
+{
+ return plugin_class->label;
+}
+
+LILV_API LilvPluginClasses*
+lilv_plugin_class_get_children(const LilvPluginClass* plugin_class)
+{
+ // Returned list doesn't own categories
+ LilvPluginClasses* all = plugin_class->world->plugin_classes;
+ LilvPluginClasses* result = zix_tree_new(false, lilv_ptr_cmp, NULL, NULL);
+
+ for (ZixTreeIter* i = zix_tree_begin((ZixTree*)all);
+ i != zix_tree_end((ZixTree*)all);
+ i = zix_tree_iter_next(i)) {
+ const LilvPluginClass* c = (LilvPluginClass*)zix_tree_get(i);
+ const LilvNode* parent = lilv_plugin_class_get_parent_uri(c);
+ if (parent && lilv_node_equals(lilv_plugin_class_get_uri(plugin_class),
+ parent)) {
+ zix_tree_insert((ZixTree*)result, (LilvPluginClass*)c, NULL);
+ }
+ }
+
+ return result;
+}
diff --git a/src/port.c b/src/port.c
new file mode 100644
index 0000000..fcc32fc
--- /dev/null
+++ b/src/port.c
@@ -0,0 +1,268 @@
+/*
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/event/event.h"
+
+#include "lilv_internal.h"
+
+LilvPort*
+lilv_port_new(LilvWorld* world,
+ const SordNode* node,
+ uint32_t index,
+ const char* symbol)
+{
+ LilvPort* port = (LilvPort*)malloc(sizeof(LilvPort));
+ port->node = lilv_node_new_from_node(world, node);
+ port->index = index;
+ port->symbol = lilv_node_new(world, LILV_VALUE_STRING, symbol);
+ port->classes = lilv_nodes_new();
+ return port;
+}
+
+void
+lilv_port_free(const LilvPlugin* plugin, LilvPort* port)
+{
+ if (port) {
+ lilv_node_free(port->node);
+ lilv_nodes_free(port->classes);
+ lilv_node_free(port->symbol);
+ free(port);
+ }
+}
+
+LILV_API bool
+lilv_port_is_a(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* port_class)
+{
+ LILV_FOREACH(nodes, i, port->classes) {
+ if (lilv_node_equals(lilv_nodes_get(port->classes, i), port_class)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+LILV_API bool
+lilv_port_has_property(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* property)
+{
+ return lilv_world_ask_internal(plugin->world,
+ port->node->node,
+ plugin->world->uris.lv2_portProperty,
+ property->node);
+}
+
+LILV_API bool
+lilv_port_supports_event(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* event_type)
+{
+ const uint8_t* predicates[] = { (const uint8_t*)LV2_EVENT__supportsEvent,
+ (const uint8_t*)LV2_ATOM__supports,
+ NULL };
+
+ for (const uint8_t** pred = predicates; *pred; ++pred) {
+ if (lilv_world_ask_internal(plugin->world,
+ port->node->node,
+ sord_new_uri(plugin->world->world, *pred),
+ event_type->node)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static LilvNodes*
+lilv_port_get_value_by_node(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const SordNode* predicate)
+{
+ return lilv_world_find_nodes_internal(plugin->world,
+ port->node->node,
+ predicate,
+ NULL);
+}
+
+LILV_API const LilvNode*
+lilv_port_get_node(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ return port->node;
+}
+
+LILV_API LilvNodes*
+lilv_port_get_value(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* predicate)
+{
+ if (!lilv_node_is_uri(predicate)) {
+ LILV_ERRORF("Predicate `%s' is not a URI\n",
+ sord_node_get_string(predicate->node));
+ return NULL;
+ }
+
+ return lilv_port_get_value_by_node(plugin, port, predicate->node);
+}
+
+LILV_API LilvNode*
+lilv_port_get(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* predicate)
+{
+ LilvNodes* values = lilv_port_get_value(plugin, port, predicate);
+
+ LilvNode* value = lilv_node_duplicate(
+ values ? lilv_nodes_get_first(values) : NULL);
+
+ lilv_nodes_free(values);
+ return value;
+}
+
+LILV_API uint32_t
+lilv_port_get_index(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ return port->index;
+}
+
+LILV_API const LilvNode*
+lilv_port_get_symbol(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ return port->symbol;
+}
+
+LILV_API LilvNode*
+lilv_port_get_name(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ LilvNodes* results = lilv_port_get_value_by_node(
+ plugin, port, plugin->world->uris.lv2_name);
+
+ LilvNode* ret = NULL;
+ if (results) {
+ LilvNode* val = lilv_nodes_get_first(results);
+ if (lilv_node_is_string(val)) {
+ ret = lilv_node_duplicate(val);
+ }
+ lilv_nodes_free(results);
+ }
+
+ if (!ret) {
+ LILV_WARNF("Plugin <%s> port has no (mandatory) doap:name\n",
+ lilv_node_as_string(lilv_plugin_get_uri(plugin)));
+ }
+
+ return ret;
+}
+
+LILV_API const LilvNodes*
+lilv_port_get_classes(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ return port->classes;
+}
+
+LILV_API void
+lilv_port_get_range(const LilvPlugin* plugin,
+ const LilvPort* port,
+ LilvNode** def,
+ LilvNode** min,
+ LilvNode** max)
+{
+ if (def) {
+ LilvNodes* defaults = lilv_port_get_value_by_node(
+ plugin, port, plugin->world->uris.lv2_default);
+ *def = defaults
+ ? lilv_node_duplicate(lilv_nodes_get_first(defaults))
+ : NULL;
+ lilv_nodes_free(defaults);
+ }
+ if (min) {
+ LilvNodes* minimums = lilv_port_get_value_by_node(
+ plugin, port, plugin->world->uris.lv2_minimum);
+ *min = minimums
+ ? lilv_node_duplicate(lilv_nodes_get_first(minimums))
+ : NULL;
+ lilv_nodes_free(minimums);
+ }
+ if (max) {
+ LilvNodes* maximums = lilv_port_get_value_by_node(
+ plugin, port, plugin->world->uris.lv2_maximum);
+ *max = maximums
+ ? lilv_node_duplicate(lilv_nodes_get_first(maximums))
+ : NULL;
+ lilv_nodes_free(maximums);
+ }
+}
+
+LILV_API LilvScalePoints*
+lilv_port_get_scale_points(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ SordIter* points = lilv_world_query_internal(
+ plugin->world,
+ port->node->node,
+ sord_new_uri(plugin->world->world, (const uint8_t*)LV2_CORE__scalePoint),
+ NULL);
+
+ LilvScalePoints* ret = NULL;
+ if (!sord_iter_end(points)) {
+ ret = lilv_scale_points_new();
+ }
+
+ FOREACH_MATCH(points) {
+ const SordNode* point = sord_iter_get_node(points, SORD_OBJECT);
+
+ LilvNode* value = lilv_plugin_get_unique(plugin,
+ point,
+ plugin->world->uris.rdf_value);
+
+ LilvNode* label = lilv_plugin_get_unique(plugin,
+ point,
+ plugin->world->uris.rdfs_label);
+
+ if (value && label) {
+ zix_tree_insert(
+ (ZixTree*)ret, lilv_scale_point_new(value, label), NULL);
+ }
+ }
+ sord_iter_free(points);
+
+ assert(!ret || lilv_nodes_size(ret) > 0);
+ return ret;
+}
+
+LILV_API LilvNodes*
+lilv_port_get_properties(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ LilvNode* pred = lilv_node_new_from_node(
+ plugin->world, plugin->world->uris.lv2_portProperty);
+ LilvNodes* ret = lilv_port_get_value(plugin, port, pred);
+ lilv_node_free(pred);
+ return ret;
+}
diff --git a/src/query.c b/src/query.c
new file mode 100644
index 0000000..fe2988f
--- /dev/null
+++ b/src/query.c
@@ -0,0 +1,139 @@
+/*
+ Copyright 2007-2015 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv_internal.h"
+
+typedef enum {
+ LILV_LANG_MATCH_NONE, ///< Language does not match at all
+ LILV_LANG_MATCH_PARTIAL, ///< Partial (language, but not country) match
+ LILV_LANG_MATCH_EXACT ///< Exact (language and country) match
+} LilvLangMatch;
+
+static LilvLangMatch
+lilv_lang_matches(const char* a, const char* b)
+{
+ if (!strcmp(a, b)) {
+ return LILV_LANG_MATCH_EXACT;
+ }
+
+ const char* a_dash = strchr(a, '-');
+ const size_t a_lang_len = a_dash ? (size_t)(a_dash - a) : strlen(a);
+ const char* b_dash = strchr(b, '-');
+ const size_t b_lang_len = b_dash ? (size_t)(b_dash - b) : strlen(b);
+
+ if (a_lang_len == b_lang_len && !strncmp(a, b, a_lang_len)) {
+ return LILV_LANG_MATCH_PARTIAL;
+ }
+
+ return LILV_LANG_MATCH_NONE;
+}
+
+static LilvNodes*
+lilv_nodes_from_stream_objects_i18n(LilvWorld* world,
+ SordIter* stream,
+ SordQuadIndex field)
+{
+ LilvNodes* values = lilv_nodes_new();
+ const SordNode* nolang = NULL; // Untranslated value
+ const SordNode* partial = NULL; // Partial language match
+ char* syslang = lilv_get_lang();
+ FOREACH_MATCH(stream) {
+ const SordNode* value = sord_iter_get_node(stream, field);
+ if (sord_node_get_type(value) == SORD_LITERAL) {
+ const char* lang = sord_node_get_language(value);
+ LilvLangMatch lm = LILV_LANG_MATCH_NONE;
+ if (lang) {
+ lm = (syslang)
+ ? lilv_lang_matches(lang, syslang)
+ : LILV_LANG_MATCH_PARTIAL;
+ } else {
+ nolang = value;
+ if (!syslang) {
+ lm = LILV_LANG_MATCH_EXACT;
+ }
+ }
+
+ if (lm == LILV_LANG_MATCH_EXACT) {
+ // Exact language match, add to results
+ zix_tree_insert((ZixTree*)values,
+ lilv_node_new_from_node(world, value),
+ NULL);
+ } else if (lm == LILV_LANG_MATCH_PARTIAL) {
+ // Partial language match, save in case we find no exact
+ partial = value;
+ }
+ } else {
+ zix_tree_insert((ZixTree*)values,
+ lilv_node_new_from_node(world, value),
+ NULL);
+ }
+ }
+ sord_iter_free(stream);
+ free(syslang);
+
+ if (lilv_nodes_size(values) > 0) {
+ return values;
+ }
+
+ const SordNode* best = nolang;
+ if (syslang && partial) {
+ // Partial language match for system language
+ best = partial;
+ } else if (!best) {
+ // No languages matches at all, and no untranslated value
+ // Use any value, if possible
+ best = partial;
+ }
+
+ if (best) {
+ zix_tree_insert(
+ (ZixTree*)values, lilv_node_new_from_node(world, best), NULL);
+ } else {
+ // No matches whatsoever
+ lilv_nodes_free(values);
+ values = NULL;
+ }
+
+ return values;
+}
+
+LilvNodes*
+lilv_nodes_from_stream_objects(LilvWorld* world,
+ SordIter* stream,
+ SordQuadIndex field)
+{
+ if (sord_iter_end(stream)) {
+ sord_iter_free(stream);
+ return NULL;
+ } else if (world->opt.filter_language) {
+ return lilv_nodes_from_stream_objects_i18n(world, stream, field);
+ } else {
+ LilvNodes* values = lilv_nodes_new();
+ FOREACH_MATCH(stream) {
+ const SordNode* value = sord_iter_get_node(stream, field);
+ LilvNode* node = lilv_node_new_from_node(world, value);
+ if (node) {
+ zix_tree_insert((ZixTree*)values, node, NULL);
+ }
+ }
+ sord_iter_free(stream);
+ return values;
+ }
+}
diff --git a/src/scalepoint.c b/src/scalepoint.c
new file mode 100644
index 0000000..349b2b3
--- /dev/null
+++ b/src/scalepoint.c
@@ -0,0 +1,49 @@
+/*
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "lilv_internal.h"
+
+/** Ownership of value and label is taken */
+LilvScalePoint*
+lilv_scale_point_new(LilvNode* value, LilvNode* label)
+{
+ LilvScalePoint* point = (LilvScalePoint*)malloc(sizeof(LilvScalePoint));
+ point->value = value;
+ point->label = label;
+ return point;
+}
+
+void
+lilv_scale_point_free(LilvScalePoint* point)
+{
+ if (point) {
+ lilv_node_free(point->value);
+ lilv_node_free(point->label);
+ free(point);
+ }
+}
+
+LILV_API const LilvNode*
+lilv_scale_point_get_value(const LilvScalePoint* point)
+{
+ return point->value;
+}
+
+LILV_API const LilvNode*
+lilv_scale_point_get_label(const LilvScalePoint* point)
+{
+ return point->label;
+}
diff --git a/src/state.c b/src/state.c
new file mode 100644
index 0000000..0b14f25
--- /dev/null
+++ b/src/state.c
@@ -0,0 +1,1352 @@
+/*
+ Copyright 2007-2017 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+
+#include "lilv_config.h"
+#include "lilv_internal.h"
+#include "sratom/sratom.h"
+
+#define USTR(s) ((const uint8_t*)(s))
+
+typedef struct {
+ void* value; ///< Value/Object
+ size_t size; ///< Size of value
+ uint32_t key; ///< Key/Predicate (URID)
+ uint32_t type; ///< Type of value (URID)
+ uint32_t flags; ///< State flags (POD, etc)
+} Property;
+
+typedef struct {
+ char* symbol; ///< Symbol of port
+ void* value; ///< Value of port
+ uint32_t size; ///< Size of value
+ uint32_t type; ///< Type of value (URID)
+} PortValue;
+
+typedef struct {
+ char* abs; ///< Absolute path of actual file
+ char* rel; ///< Abstract path (relative path in state dir)
+} PathMap;
+
+typedef struct {
+ size_t n;
+ Property* props;
+} PropertyArray;
+
+struct LilvStateImpl {
+ LilvNode* plugin_uri; ///< Plugin URI
+ LilvNode* uri; ///< State/preset URI
+ char* dir; ///< Save directory (if saved)
+ char* file_dir; ///< Directory for files created by plugin
+ char* copy_dir; ///< Directory for snapshots of external files
+ char* link_dir; ///< Directory for links to external files
+ char* label; ///< State/Preset label
+ ZixTree* abs2rel; ///< PathMap sorted by abs
+ ZixTree* rel2abs; ///< PathMap sorted by rel
+ PropertyArray props; ///< State properties
+ PropertyArray metadata; ///< State metadata
+ PortValue* values; ///< Port values
+ uint32_t atom_Path; ///< atom:Path URID
+ uint32_t n_values; ///< Number of port values
+};
+
+static int
+abs_cmp(const void* a, const void* b, void* user_data)
+{
+ return strcmp(((const PathMap*)a)->abs, ((const PathMap*)b)->abs);
+}
+
+static int
+rel_cmp(const void* a, const void* b, void* user_data)
+{
+ return strcmp(((const PathMap*)a)->rel, ((const PathMap*)b)->rel);
+}
+
+static int
+property_cmp(const void* a, const void* b)
+{
+ return ((const Property*)a)->key - ((const Property*)b)->key;
+}
+
+static int
+value_cmp(const void* a, const void* b)
+{
+ return strcmp(((const PortValue*)a)->symbol,
+ ((const PortValue*)b)->symbol);
+}
+
+static void
+path_rel_free(void* ptr)
+{
+ free(((PathMap*)ptr)->abs);
+ free(((PathMap*)ptr)->rel);
+ free(ptr);
+}
+
+static PortValue*
+append_port_value(LilvState* state,
+ const char* port_symbol,
+ const void* value,
+ uint32_t size,
+ uint32_t type)
+{
+ PortValue* pv = NULL;
+ if (value) {
+ state->values = (PortValue*)realloc(
+ state->values, (++state->n_values) * sizeof(PortValue));
+ pv = &state->values[state->n_values - 1];
+ pv->symbol = lilv_strdup(port_symbol);
+ pv->value = malloc(size);
+ pv->size = size;
+ pv->type = type;
+ memcpy(pv->value, value, size);
+ }
+ return pv;
+}
+
+static const char*
+lilv_state_rel2abs(const LilvState* state, const char* path)
+{
+ ZixTreeIter* iter = NULL;
+ const PathMap key = { NULL, (char*)path };
+ if (state->rel2abs && !zix_tree_find(state->rel2abs, &key, &iter)) {
+ return ((const PathMap*)zix_tree_get(iter))->abs;
+ }
+ return path;
+}
+
+static void
+append_property(LilvState* state,
+ PropertyArray* array,
+ uint32_t key,
+ const void* value,
+ size_t size,
+ uint32_t type,
+ uint32_t flags)
+{
+ array->props = (Property*)realloc(
+ array->props, (++array->n) * sizeof(Property));
+
+ Property* const prop = &array->props[array->n - 1];
+ if ((flags & LV2_STATE_IS_POD) || type == state->atom_Path) {
+ prop->value = malloc(size);
+ memcpy(prop->value, value, size);
+ } else {
+ prop->value = (void*)value;
+ }
+
+ prop->size = size;
+ prop->key = key;
+ prop->type = type;
+ prop->flags = flags;
+}
+
+static LV2_State_Status
+store_callback(LV2_State_Handle handle,
+ uint32_t key,
+ const void* value,
+ size_t size,
+ uint32_t type,
+ uint32_t flags)
+{
+ LilvState* const state = (LilvState*)handle;
+ append_property(state, &state->props, key, value, size, type, flags);
+ return LV2_STATE_SUCCESS;
+}
+
+static const void*
+retrieve_callback(LV2_State_Handle handle,
+ uint32_t key,
+ size_t* size,
+ uint32_t* type,
+ uint32_t* flags)
+{
+ const LilvState* const state = (LilvState*)handle;
+ const Property search_key = { NULL, 0, key, 0, 0 };
+ const Property* const prop = (Property*)bsearch(
+ &search_key, state->props.props, state->props.n,
+ sizeof(Property), property_cmp);
+
+ if (prop) {
+ *size = prop->size;
+ *type = prop->type;
+ *flags = prop->flags;
+ return prop->value;
+ }
+ return NULL;
+}
+
+static bool
+lilv_state_has_path(const char* path, const void* state)
+{
+ return lilv_state_rel2abs((const LilvState*)state, path) != path;
+}
+
+static char*
+make_path(LV2_State_Make_Path_Handle handle, const char* path)
+{
+ LilvState* state = (LilvState*)handle;
+ lilv_mkdir_p(state->dir);
+
+ return lilv_path_join(state->dir, path);
+}
+
+static char*
+abstract_path(LV2_State_Map_Path_Handle handle,
+ const char* abs_path)
+{
+ LilvState* state = (LilvState*)handle;
+ char* path = NULL;
+ char* real_path = lilv_realpath(abs_path);
+ const PathMap key = { real_path, NULL };
+ ZixTreeIter* iter = NULL;
+
+ if (abs_path[0] == '\0') {
+ return lilv_strdup(abs_path);
+ } else if (!zix_tree_find(state->abs2rel, &key, &iter)) {
+ // Already mapped path in a previous call
+ PathMap* pm = (PathMap*)zix_tree_get(iter);
+ free(real_path);
+ return lilv_strdup(pm->rel);
+ } else if (lilv_path_is_child(real_path, state->dir)) {
+ // File in state directory (loaded, or created by plugin during save)
+ path = lilv_path_relative_to(real_path, state->dir);
+ } else if (lilv_path_is_child(real_path, state->file_dir)) {
+ // File created by plugin earlier
+ path = lilv_path_relative_to(real_path, state->file_dir);
+ if (state->copy_dir) {
+ int st = lilv_mkdir_p(state->copy_dir);
+ if (st) {
+ LILV_ERRORF("Error creating directory %s (%s)\n",
+ state->copy_dir, strerror(st));
+ }
+
+ char* cpath = lilv_path_join(state->copy_dir, path);
+ char* copy = lilv_get_latest_copy(real_path, cpath);
+ if (!copy || !lilv_file_equals(real_path, copy)) {
+ // No recent enough copy, make a new one
+ free(copy);
+ copy = lilv_find_free_path(cpath, lilv_path_exists, NULL);
+ if ((st = lilv_copy_file(real_path, copy))) {
+ LILV_ERRORF("Error copying state file %s (%s)\n",
+ copy, strerror(st));
+ }
+ }
+ free(real_path);
+ free(cpath);
+
+ // Refer to the latest copy in plugin state
+ real_path = copy;
+ }
+ } else if (state->link_dir) {
+ // New path outside state directory, make a link
+ const char* slash = strrchr(real_path, '/');
+ const char* name = slash ? (slash + 1) : real_path;
+
+ // Find a free name in the (virtual) state directory
+ path = lilv_find_free_path(name, lilv_state_has_path, state);
+ } else {
+ // No link directory, preserve absolute path
+ path = lilv_strdup(abs_path);
+ }
+
+ // Add record to path mapping
+ PathMap* pm = (PathMap*)malloc(sizeof(PathMap));
+ pm->abs = real_path;
+ pm->rel = lilv_strdup(path);
+ zix_tree_insert(state->abs2rel, pm, NULL);
+ zix_tree_insert(state->rel2abs, pm, NULL);
+
+ return path;
+}
+
+static char*
+absolute_path(LV2_State_Map_Path_Handle handle,
+ const char* state_path)
+{
+ LilvState* state = (LilvState*)handle;
+ char* path = NULL;
+ if (lilv_path_is_absolute(state_path)) {
+ // Absolute path, return identical path
+ path = lilv_strdup(state_path);
+ } else if (state->dir) {
+ // Relative path inside state directory
+ path = lilv_path_join(state->dir, state_path);
+ } else {
+ // State has not been saved, unmap
+ path = lilv_strdup(lilv_state_rel2abs(state, state_path));
+ }
+
+ return path;
+}
+
+/** Return a new features array which is `feature` added to `features`. */
+static const LV2_Feature**
+add_features(const LV2_Feature *const * features,
+ const LV2_Feature* map, const LV2_Feature* make)
+{
+ size_t n_features = 0;
+ for (; features && features[n_features]; ++n_features) {}
+
+ const LV2_Feature** ret = (const LV2_Feature**)calloc(
+ n_features + 3, sizeof(LV2_Feature*));
+
+ if (features) {
+ memcpy(ret, features, n_features * sizeof(LV2_Feature*));
+ }
+
+ ret[n_features] = map;
+ ret[n_features + 1] = make;
+ return ret;
+}
+
+static char*
+absolute_dir(const char* path)
+{
+ char* abs_path = lilv_path_absolute(path);
+ char* base = lilv_path_join(abs_path, NULL);
+ free(abs_path);
+ return base;
+}
+
+static const char*
+state_strerror(LV2_State_Status st)
+{
+ switch (st) {
+ case LV2_STATE_SUCCESS: return "Completed successfully";
+ case LV2_STATE_ERR_BAD_TYPE: return "Unsupported type";
+ case LV2_STATE_ERR_BAD_FLAGS: return "Unsupported flags";
+ case LV2_STATE_ERR_NO_FEATURE: return "Missing features";
+ case LV2_STATE_ERR_NO_PROPERTY: return "Missing property";
+ default: return "Unknown error";
+ }
+}
+
+LILV_API LilvState*
+lilv_state_new_from_instance(const LilvPlugin* plugin,
+ LilvInstance* instance,
+ LV2_URID_Map* map,
+ const char* file_dir,
+ const char* copy_dir,
+ const char* link_dir,
+ const char* save_dir,
+ LilvGetPortValueFunc get_value,
+ void* user_data,
+ uint32_t flags,
+ const LV2_Feature *const * features)
+{
+ const LV2_Feature** sfeatures = NULL;
+ LilvWorld* const world = plugin->world;
+ LilvState* const state = (LilvState*)calloc(1, sizeof(LilvState));
+ state->plugin_uri = lilv_node_duplicate(lilv_plugin_get_uri(plugin));
+ state->abs2rel = zix_tree_new(false, abs_cmp, NULL, path_rel_free);
+ state->rel2abs = zix_tree_new(false, rel_cmp, NULL, NULL);
+ state->file_dir = file_dir ? absolute_dir(file_dir) : NULL;
+ state->copy_dir = copy_dir ? absolute_dir(copy_dir) : NULL;
+ state->link_dir = link_dir ? absolute_dir(link_dir) : NULL;
+ state->dir = save_dir ? absolute_dir(save_dir) : NULL;
+ state->atom_Path = map->map(map->handle, LV2_ATOM__Path);
+
+ LV2_State_Map_Path pmap = { state, abstract_path, absolute_path };
+ LV2_Feature pmap_feature = { LV2_STATE__mapPath, &pmap };
+ LV2_State_Make_Path pmake = { state, make_path };
+ LV2_Feature pmake_feature = { LV2_STATE__makePath, &pmake };
+ features = sfeatures = add_features(features, &pmap_feature,
+ save_dir ? &pmake_feature : NULL);
+
+ // Store port values
+ if (get_value) {
+ LilvNode* lv2_ControlPort = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
+ LilvNode* lv2_InputPort = lilv_new_uri(world, LILV_URI_INPUT_PORT);
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ const LilvPort* const port = plugin->ports[i];
+ if (lilv_port_is_a(plugin, port, lv2_ControlPort)
+ && lilv_port_is_a(plugin, port, lv2_InputPort)) {
+ uint32_t size, type;
+ const char* sym = lilv_node_as_string(port->symbol);
+ const void* value = get_value(sym, user_data, &size, &type);
+ append_port_value(state, sym, value, size, type);
+ }
+ }
+ lilv_node_free(lv2_ControlPort);
+ lilv_node_free(lv2_InputPort);
+ }
+
+ // Store properties
+ const LV2_Descriptor* desc = instance->lv2_descriptor;
+ const LV2_State_Interface* iface = (desc->extension_data)
+ ? (const LV2_State_Interface*)desc->extension_data(LV2_STATE__interface)
+ : NULL;
+
+ if (iface) {
+ LV2_State_Status st = iface->save(
+ instance->lv2_handle, store_callback, state, flags, features);
+ if (st) {
+ LILV_ERRORF("Error saving plugin state: %s\n", state_strerror(st));
+ free(state->props.props);
+ state->props.props = NULL;
+ state->props.n = 0;
+ } else {
+ qsort(state->props.props, state->props.n, sizeof(Property), property_cmp);
+ }
+ }
+
+ qsort(state->values, state->n_values, sizeof(PortValue), value_cmp);
+
+ free(sfeatures);
+ return state;
+}
+
+LILV_API void
+lilv_state_emit_port_values(const LilvState* state,
+ LilvSetPortValueFunc set_value,
+ void* user_data)
+{
+ for (uint32_t i = 0; i < state->n_values; ++i) {
+ const PortValue* val = &state->values[i];
+ set_value(val->symbol, user_data, val->value, val->size, val->type);
+ }
+}
+
+LILV_API void
+lilv_state_restore(const LilvState* state,
+ LilvInstance* instance,
+ LilvSetPortValueFunc set_value,
+ void* user_data,
+ uint32_t flags,
+ const LV2_Feature *const * features)
+{
+ if (!state) {
+ LILV_ERROR("lilv_state_restore() called on NULL state\n");
+ return;
+ }
+
+ LV2_State_Map_Path map_path = {
+ (LilvState*)state, abstract_path, absolute_path };
+ LV2_Feature map_feature = { LV2_STATE__mapPath, &map_path };
+
+ if (instance) {
+ const LV2_Descriptor* desc = instance->lv2_descriptor;
+ if (desc->extension_data) {
+ const LV2_State_Interface* iface = (const LV2_State_Interface*)
+ desc->extension_data(LV2_STATE__interface);
+
+ if (iface && iface->restore) {
+ const LV2_Feature** sfeatures = add_features(
+ features, &map_feature, NULL);
+
+ iface->restore(instance->lv2_handle, retrieve_callback,
+ (LV2_State_Handle)state, flags, sfeatures);
+
+ free(sfeatures);
+ }
+ }
+ }
+
+
+ if (set_value) {
+ lilv_state_emit_port_values(state, set_value, user_data);
+ }
+}
+
+static void
+set_state_dir_from_model(LilvState* state, const SordNode* graph)
+{
+ if (!state->dir && graph) {
+ const char* uri = (const char*)sord_node_get_string(graph);
+ state->dir = lilv_file_uri_parse(uri, NULL);
+ }
+ assert(!state->dir || lilv_path_is_absolute(state->dir));
+}
+
+static LilvState*
+new_state_from_model(LilvWorld* world,
+ LV2_URID_Map* map,
+ SordModel* model,
+ const SordNode* node,
+ const char* dir)
+{
+ // Check that we know at least something about this state subject
+ if (!sord_ask(model, node, 0, 0, 0)) {
+ return NULL;
+ }
+
+ // Allocate state
+ LilvState* const state = (LilvState*)calloc(1, sizeof(LilvState));
+ state->dir = lilv_strdup(dir);
+ state->atom_Path = map->map(map->handle, LV2_ATOM__Path);
+ state->uri = lilv_node_new_from_node(world, node);
+
+ // Get the plugin URI this state applies to
+ SordIter* i = sord_search(model, node, world->uris.lv2_appliesTo, 0, 0);
+ if (i) {
+ const SordNode* object = sord_iter_get_node(i, SORD_OBJECT);
+ const SordNode* graph = sord_iter_get_node(i, SORD_GRAPH);
+ state->plugin_uri = lilv_node_new_from_node(world, object);
+ set_state_dir_from_model(state, graph);
+ sord_iter_free(i);
+ } else if (sord_ask(model,
+ node,
+ world->uris.rdf_a,
+ world->uris.lv2_Plugin, 0)) {
+ // Loading plugin description as state (default state)
+ state->plugin_uri = lilv_node_new_from_node(world, node);
+ } else {
+ LILV_ERRORF("State %s missing lv2:appliesTo property\n",
+ sord_node_get_string(node));
+ }
+
+ // Get the state label
+ i = sord_search(model, node, world->uris.rdfs_label, NULL, NULL);
+ if (i) {
+ const SordNode* object = sord_iter_get_node(i, SORD_OBJECT);
+ const SordNode* graph = sord_iter_get_node(i, SORD_GRAPH);
+ state->label = lilv_strdup((const char*)sord_node_get_string(object));
+ set_state_dir_from_model(state, graph);
+ sord_iter_free(i);
+ }
+
+ Sratom* sratom = sratom_new(map);
+ SerdChunk chunk = { NULL, 0 };
+ LV2_Atom_Forge forge;
+ lv2_atom_forge_init(&forge, map);
+ lv2_atom_forge_set_sink(
+ &forge, sratom_forge_sink, sratom_forge_deref, &chunk);
+
+ // Get port values
+ SordIter* ports = sord_search(model, node, world->uris.lv2_port, 0, 0);
+ FOREACH_MATCH(ports) {
+ const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT);
+
+ SordNode* label = sord_get(model, port, world->uris.rdfs_label, 0, 0);
+ SordNode* symbol = sord_get(model, port, world->uris.lv2_symbol, 0, 0);
+ SordNode* value = sord_get(model, port, world->uris.pset_value, 0, 0);
+ if (!value) {
+ value = sord_get(model, port, world->uris.lv2_default, 0, 0);
+ }
+ if (!symbol) {
+ LILV_ERRORF("State `%s' port missing symbol.\n",
+ sord_node_get_string(node));
+ } else if (value) {
+ chunk.len = 0;
+ sratom_read(sratom, &forge, world->world, model, value);
+ const LV2_Atom* atom = (const LV2_Atom*)chunk.buf;
+
+ append_port_value(state,
+ (const char*)sord_node_get_string(symbol),
+ LV2_ATOM_BODY_CONST(atom),
+ atom->size, atom->type);
+
+ if (label) {
+ lilv_state_set_label(state,
+ (const char*)sord_node_get_string(label));
+ }
+ }
+ sord_node_free(world->world, value);
+ sord_node_free(world->world, symbol);
+ sord_node_free(world->world, label);
+ }
+ sord_iter_free(ports);
+
+ // Get properties
+ SordNode* statep = sord_new_uri(world->world, USTR(LV2_STATE__state));
+ SordNode* state_node = sord_get(model, node, statep, NULL, NULL);
+ if (state_node) {
+ SordIter* props = sord_search(model, state_node, 0, 0, 0);
+ FOREACH_MATCH(props) {
+ const SordNode* p = sord_iter_get_node(props, SORD_PREDICATE);
+ const SordNode* o = sord_iter_get_node(props, SORD_OBJECT);
+ const char* key = (const char*)sord_node_get_string(p);
+
+ chunk.len = 0;
+ lv2_atom_forge_set_sink(
+ &forge, sratom_forge_sink, sratom_forge_deref, &chunk);
+
+ sratom_read(sratom, &forge, world->world, model, o);
+ const LV2_Atom* atom = (const LV2_Atom*)chunk.buf;
+ uint32_t flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE;
+ Property prop = { NULL, 0, 0, 0, flags };
+
+ prop.key = map->map(map->handle, key);
+ prop.type = atom->type;
+ prop.size = atom->size;
+ prop.value = malloc(atom->size);
+ memcpy(prop.value, LV2_ATOM_BODY_CONST(atom), atom->size);
+ if (atom->type == forge.Path) {
+ prop.flags = LV2_STATE_IS_POD;
+ }
+
+ if (prop.value) {
+ state->props.props = (Property*)realloc(
+ state->props.props, (++state->props.n) * sizeof(Property));
+ state->props.props[state->props.n - 1] = prop;
+ }
+ }
+ sord_iter_free(props);
+ }
+ sord_node_free(world->world, state_node);
+ sord_node_free(world->world, statep);
+
+ free((void*)chunk.buf);
+ sratom_free(sratom);
+
+ if (state->props.props) {
+ qsort(state->props.props, state->props.n, sizeof(Property), property_cmp);
+ }
+ if (state->values) {
+ qsort(state->values, state->n_values, sizeof(PortValue), value_cmp);
+ }
+
+ return state;
+}
+
+LILV_API LilvState*
+lilv_state_new_from_world(LilvWorld* world,
+ LV2_URID_Map* map,
+ const LilvNode* node)
+{
+ if (!lilv_node_is_uri(node) && !lilv_node_is_blank(node)) {
+ LILV_ERRORF("Subject `%s' is not a URI or blank node.\n",
+ lilv_node_as_string(node));
+ return NULL;
+ }
+
+ return new_state_from_model(world, map, world->model, node->node, NULL);
+}
+
+LILV_API LilvState*
+lilv_state_new_from_file(LilvWorld* world,
+ LV2_URID_Map* map,
+ const LilvNode* subject,
+ const char* path)
+{
+ if (subject && !lilv_node_is_uri(subject)
+ && !lilv_node_is_blank(subject)) {
+ LILV_ERRORF("Subject `%s' is not a URI or blank node.\n",
+ lilv_node_as_string(subject));
+ return NULL;
+ }
+
+ uint8_t* abs_path = (uint8_t*)lilv_path_absolute(path);
+ SerdNode node = serd_node_new_file_uri(abs_path, NULL, NULL, true);
+ SerdEnv* env = serd_env_new(&node);
+ SordModel* model = sord_new(world->world, SORD_SPO, false);
+ SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
+
+ serd_reader_read_file(reader, node.buf);
+
+ SordNode* subject_node = (subject)
+ ? subject->node
+ : sord_node_from_serd_node(world->world, env, &node, NULL, NULL);
+
+ char* dirname = lilv_dirname(path);
+ char* real_path = lilv_realpath(dirname);
+ LilvState* state = new_state_from_model(
+ world, map, model, subject_node, real_path);
+ free(dirname);
+ free(real_path);
+
+ serd_node_free(&node);
+ free(abs_path);
+ serd_reader_free(reader);
+ sord_free(model);
+ serd_env_free(env);
+ return state;
+}
+
+static void
+set_prefixes(SerdEnv* env)
+{
+#define SET_PSET(e, p, u) serd_env_set_prefix_from_strings(e, p, u)
+ SET_PSET(env, USTR("atom"), USTR(LV2_ATOM_PREFIX));
+ SET_PSET(env, USTR("lv2"), USTR(LV2_CORE_PREFIX));
+ SET_PSET(env, USTR("pset"), USTR(LV2_PRESETS_PREFIX));
+ SET_PSET(env, USTR("rdf"), USTR(LILV_NS_RDF));
+ SET_PSET(env, USTR("rdfs"), USTR(LILV_NS_RDFS));
+ SET_PSET(env, USTR("state"), USTR(LV2_STATE_PREFIX));
+ SET_PSET(env, USTR("xsd"), USTR(LILV_NS_XSD));
+}
+
+LILV_API LilvState*
+lilv_state_new_from_string(LilvWorld* world,
+ LV2_URID_Map* map,
+ const char* str)
+{
+ if (!str) {
+ return NULL;
+ }
+
+ SerdNode base = SERD_NODE_NULL;
+ SerdEnv* env = serd_env_new(&base);
+ SordModel* model = sord_new(world->world, SORD_SPO|SORD_OPS, false);
+ SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
+
+ set_prefixes(env);
+ serd_reader_read_string(reader, USTR(str));
+
+ SordNode* o = sord_new_uri(world->world, USTR(LV2_PRESETS__Preset));
+ SordNode* s = sord_get(model, NULL, world->uris.rdf_a, o, NULL);
+
+ LilvState* state = new_state_from_model(world, map, model, s, NULL);
+
+ sord_node_free(world->world, s);
+ sord_node_free(world->world, o);
+ serd_reader_free(reader);
+ sord_free(model);
+ serd_env_free(env);
+
+ return state;
+}
+
+static SerdWriter*
+ttl_writer(SerdSink sink, void* stream, const SerdNode* base, SerdEnv** new_env)
+{
+ SerdURI base_uri = SERD_URI_NULL;
+ if (base && base->buf) {
+ serd_uri_parse(base->buf, &base_uri);
+ }
+
+ SerdEnv* env = *new_env ? *new_env : serd_env_new(base);
+ set_prefixes(env);
+
+ SerdWriter* writer = serd_writer_new(
+ SERD_TURTLE,
+ (SerdStyle)(SERD_STYLE_RESOLVED |
+ SERD_STYLE_ABBREVIATED|
+ SERD_STYLE_CURIED),
+ env,
+ &base_uri,
+ sink,
+ stream);
+
+ if (!*new_env) {
+ *new_env = env;
+ }
+
+ return writer;
+}
+
+static SerdWriter*
+ttl_file_writer(FILE* fd, const SerdNode* node, SerdEnv** env)
+{
+ SerdWriter* writer = ttl_writer(serd_file_sink, fd, node, env);
+
+ fseek(fd, 0, SEEK_END);
+ if (ftell(fd) == 0) {
+ serd_env_foreach(*env, (SerdPrefixSink)serd_writer_set_prefix, writer);
+ } else {
+ fprintf(fd, "\n");
+ }
+
+ return writer;
+}
+
+static void
+add_to_model(SordWorld* world,
+ SerdEnv* env,
+ SordModel* model,
+ const SerdNode s,
+ const SerdNode p,
+ const SerdNode o)
+{
+ SordNode* ss = sord_node_from_serd_node(world, env, &s, NULL, NULL);
+ SordNode* sp = sord_node_from_serd_node(world, env, &p, NULL, NULL);
+ SordNode* so = sord_node_from_serd_node(world, env, &o, NULL, NULL);
+
+ SordQuad quad = { ss, sp, so, NULL };
+ sord_add(model, quad);
+
+ sord_node_free(world, ss);
+ sord_node_free(world, sp);
+ sord_node_free(world, so);
+}
+
+static void
+remove_manifest_entry(SordWorld* world, SordModel* model, const char* subject)
+{
+ SordNode* s = sord_new_uri(world, USTR(subject));
+ SordIter* i = sord_search(model, s, NULL, NULL, NULL);
+ while (!sord_iter_end(i)) {
+ sord_erase(model, i);
+ }
+ sord_iter_free(i);
+ sord_node_free(world, s);
+}
+
+static int
+add_state_to_manifest(LilvWorld* lworld,
+ const LilvNode* plugin_uri,
+ const char* manifest_path,
+ const char* state_uri,
+ const char* state_path)
+{
+ SordWorld* world = lworld->world;
+ SerdNode manifest = serd_node_new_file_uri(USTR(manifest_path), 0, 0, 1);
+ SerdNode file = serd_node_new_file_uri(USTR(state_path), 0, 0, 1);
+ SerdEnv* env = serd_env_new(&manifest);
+ SordModel* model = sord_new(world, SORD_SPO, false);
+
+ FILE* rfd = fopen(manifest_path, "r");
+ if (rfd) {
+ // Read manifest into model
+ SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
+ lilv_flock(rfd, true);
+ serd_reader_read_file_handle(reader, rfd, manifest.buf);
+ serd_reader_free(reader);
+ }
+
+ // Choose state URI (use file URI if not given)
+ if (!state_uri) {
+ state_uri = (const char*)file.buf;
+ }
+
+ // Remove any existing manifest entries for this state
+ remove_manifest_entry(world, model, state_uri);
+
+ // Add manifest entry for this state to model
+ SerdNode s = serd_node_from_string(SERD_URI, USTR(state_uri));
+
+ // <state> a pset:Preset
+ add_to_model(world, env, model,
+ s,
+ serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")),
+ serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset)));
+
+ // <state> a pset:Preset
+ add_to_model(world, env, model,
+ s,
+ serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")),
+ serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset)));
+
+ // <state> rdfs:seeAlso <file>
+ add_to_model(world, env, model,
+ s,
+ serd_node_from_string(SERD_URI, USTR(LILV_NS_RDFS "seeAlso")),
+ file);
+
+ // <state> lv2:appliesTo <plugin>
+ add_to_model(world, env, model,
+ s,
+ serd_node_from_string(SERD_URI, USTR(LV2_CORE__appliesTo)),
+ serd_node_from_string(SERD_URI,
+ USTR(lilv_node_as_string(plugin_uri))));
+
+ // Write manifest model to file
+ FILE* wfd = fopen(manifest_path, "w");
+ if (wfd) {
+ SerdWriter* writer = ttl_file_writer(wfd, &manifest, &env);
+ sord_write(model, writer, NULL);
+ serd_writer_free(writer);
+ fclose(wfd);
+ } else {
+ LILV_ERRORF("Failed to open %s for writing (%s)\n",
+ manifest_path, strerror(errno));
+ }
+
+ sord_free(model);
+ serd_node_free(&file);
+ serd_node_free(&manifest);
+ serd_env_free(env);
+
+ if (rfd) {
+ lilv_flock(rfd, false);
+ fclose(rfd);
+ }
+
+ return 0;
+}
+
+static bool
+link_exists(const char* path, const void* data)
+{
+ const char* target = (const char*)data;
+ if (!lilv_path_exists(path, NULL)) {
+ return false;
+ }
+ char* real_path = lilv_realpath(path);
+ bool matches = !strcmp(real_path, target);
+ free(real_path);
+ return !matches;
+}
+
+static int
+maybe_symlink(const char* oldpath, const char* newpath)
+{
+ return link_exists(newpath, oldpath) ? 0 : lilv_symlink(oldpath, newpath);
+}
+
+static void
+write_property_array(const LilvState* state,
+ const PropertyArray* array,
+ Sratom* sratom,
+ uint32_t flags,
+ const SerdNode* subject,
+ LV2_URID_Unmap* unmap,
+ const char* dir)
+{
+ for (uint32_t i = 0; i < array->n; ++i) {
+ Property* prop = &array->props[i];
+ const char* key = unmap->unmap(unmap->handle, prop->key);
+
+ const SerdNode p = serd_node_from_string(SERD_URI, USTR(key));
+ if (prop->type == state->atom_Path && !dir) {
+ const char* path = (const char*)prop->value;
+ const char* abs_path = lilv_state_rel2abs(state, path);
+ LILV_WARNF("Writing absolute path %s\n", abs_path);
+ sratom_write(sratom, unmap, flags,
+ subject, &p, prop->type,
+ strlen(abs_path) + 1, abs_path);
+ } else if (prop->flags & LV2_STATE_IS_POD ||
+ prop->type == state->atom_Path) {
+ sratom_write(sratom, unmap, flags,
+ subject, &p, prop->type, prop->size, prop->value);
+ } else {
+ LILV_WARNF("Lost non-POD property <%s> on save\n", key);
+ }
+ }
+}
+
+static int
+lilv_state_write(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ SerdWriter* writer,
+ const char* uri,
+ const char* dir)
+{
+ SerdNode lv2_appliesTo = serd_node_from_string(
+ SERD_CURIE, USTR("lv2:appliesTo"));
+
+ const SerdNode* plugin_uri = sord_node_to_serd_node(
+ state->plugin_uri->node);
+
+ SerdNode subject = serd_node_from_string(SERD_URI, USTR(uri ? uri : ""));
+
+ // <subject> a pset:Preset
+ SerdNode p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type"));
+ SerdNode o = serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset));
+ serd_writer_write_statement(writer, 0, NULL,
+ &subject, &p, &o, NULL, NULL);
+
+ // <subject> lv2:appliesTo <http://example.org/plugin>
+ serd_writer_write_statement(writer, 0, NULL,
+ &subject,
+ &lv2_appliesTo,
+ plugin_uri, NULL, NULL);
+
+ // <subject> rdfs:label label
+ if (state->label) {
+ p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDFS "label"));
+ o = serd_node_from_string(SERD_LITERAL, USTR(state->label));
+ serd_writer_write_statement(writer, 0,
+ NULL, &subject, &p, &o, NULL, NULL);
+ }
+
+ SerdEnv* env = serd_writer_get_env(writer);
+ const SerdNode* base = serd_env_get_base_uri(env, NULL);
+
+ Sratom* sratom = sratom_new(map);
+ sratom_set_sink(sratom, (const char*)base->buf,
+ (SerdStatementSink)serd_writer_write_statement,
+ (SerdEndSink)serd_writer_end_anon,
+ writer);
+
+ // Write metadata
+ sratom_set_pretty_numbers(sratom, false); // Use precise types
+ write_property_array(state, &state->metadata, sratom, 0,
+ &subject, unmap, dir);
+
+ // Write port values
+ sratom_set_pretty_numbers(sratom, true); // Use pretty numbers
+ for (uint32_t i = 0; i < state->n_values; ++i) {
+ PortValue* const value = &state->values[i];
+
+ const SerdNode port = serd_node_from_string(
+ SERD_BLANK, USTR(value->symbol));
+
+ // <> lv2:port _:symbol
+ p = serd_node_from_string(SERD_URI, USTR(LV2_CORE__port));
+ serd_writer_write_statement(writer, SERD_ANON_O_BEGIN,
+ NULL, &subject, &p, &port, NULL, NULL);
+
+ // _:symbol lv2:symbol "symbol"
+ p = serd_node_from_string(SERD_URI, USTR(LV2_CORE__symbol));
+ o = serd_node_from_string(SERD_LITERAL, USTR(value->symbol));
+ serd_writer_write_statement(writer, SERD_ANON_CONT,
+ NULL, &port, &p, &o, NULL, NULL);
+
+ // _:symbol pset:value value
+ p = serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__value));
+ sratom_write(sratom, unmap, SERD_ANON_CONT, &port, &p,
+ value->type, value->size, value->value);
+
+ serd_writer_end_anon(writer, &port);
+ }
+
+ // Write properties
+ const SerdNode body = serd_node_from_string(SERD_BLANK, USTR("body"));
+ if (state->props.n > 0) {
+ p = serd_node_from_string(SERD_URI, USTR(LV2_STATE__state));
+ serd_writer_write_statement(writer, SERD_ANON_O_BEGIN, NULL,
+ &subject, &p, &body, NULL, NULL);
+ }
+ sratom_set_pretty_numbers(sratom, false); // Use precise types
+ write_property_array(state, &state->props, sratom, SERD_ANON_CONT,
+ &body, unmap, dir);
+
+ if (state->props.n > 0) {
+ serd_writer_end_anon(writer, &body);
+ }
+
+ sratom_free(sratom);
+ return 0;
+}
+
+static void
+lilv_state_make_links(const LilvState* state, const char* dir)
+{
+ // Create symlinks to files
+ for (ZixTreeIter* i = zix_tree_begin(state->abs2rel);
+ i != zix_tree_end(state->abs2rel);
+ i = zix_tree_iter_next(i)) {
+ const PathMap* pm = (const PathMap*)zix_tree_get(i);
+
+ char* path = lilv_path_join(dir, pm->rel);
+ if (lilv_path_is_child(pm->abs, state->copy_dir)
+ && strcmp(state->copy_dir, dir)) {
+ // Link directly to snapshot in the copy directory
+ char* target = lilv_path_relative_to(pm->abs, dir);
+ maybe_symlink(target, path);
+ free(target);
+ } else if (!lilv_path_is_child(pm->abs, dir)) {
+ const char* link_dir = state->link_dir ? state->link_dir : dir;
+ char* pat = lilv_path_join(link_dir, pm->rel);
+ if (!strcmp(dir, link_dir)) {
+ // Link directory is save directory, make link at exact path
+ remove(pat);
+ maybe_symlink(pm->abs, pat);
+ } else {
+ // Make a link in the link directory to external file
+ char* lpath = lilv_find_free_path(pat, link_exists, pm->abs);
+ if (!lilv_path_exists(lpath, NULL)) {
+ lilv_symlink(pm->abs, lpath);
+ }
+
+ // Make a link in the save directory to the external link
+ char* target = lilv_path_relative_to(lpath, dir);
+ maybe_symlink(target, path);
+ free(target);
+ free(lpath);
+ }
+ free(pat);
+ }
+ free(path);
+ }
+}
+
+LILV_API int
+lilv_state_save(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ const char* uri,
+ const char* dir,
+ const char* filename)
+{
+ if (!filename || !dir || lilv_mkdir_p(dir)) {
+ return 1;
+ }
+
+ char* abs_dir = absolute_dir(dir);
+ char* const path = lilv_path_join(abs_dir, filename);
+ FILE* fd = fopen(path, "w");
+ if (!fd) {
+ LILV_ERRORF("Failed to open %s (%s)\n", path, strerror(errno));
+ free(abs_dir);
+ free(path);
+ return 4;
+ }
+
+ // Create symlinks to files if necessary
+ lilv_state_make_links(state, abs_dir);
+
+ // Write state to Turtle file
+ SerdNode file = serd_node_new_file_uri(USTR(path), NULL, NULL, true);
+ SerdNode node = uri ? serd_node_from_string(SERD_URI, USTR(uri)) : file;
+ SerdEnv* env = NULL;
+ SerdWriter* ttl = ttl_file_writer(fd, &file, &env);
+ int ret = lilv_state_write(
+ world, map, unmap, state, ttl, (const char*)node.buf, dir);
+
+ // Set saved dir and uri (FIXME: const violation)
+ free(state->dir);
+ lilv_node_free(state->uri);
+ ((LilvState*)state)->dir = lilv_strdup(abs_dir);
+ ((LilvState*)state)->uri = lilv_new_uri(world, (const char*)node.buf);
+
+ serd_node_free(&file);
+ serd_writer_free(ttl);
+ serd_env_free(env);
+ fclose(fd);
+
+ // Add entry to manifest
+ char* const manifest = lilv_path_join(abs_dir, "manifest.ttl");
+ add_state_to_manifest(world, state->plugin_uri, manifest, uri, path);
+
+ free(manifest);
+ free(abs_dir);
+ free(path);
+ return ret;
+}
+
+LILV_API char*
+lilv_state_to_string(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ const char* uri,
+ const char* base_uri)
+{
+ if (!uri) {
+ LILV_ERROR("Attempt to serialise state with no URI\n");
+ return NULL;
+ }
+
+ SerdChunk chunk = { NULL, 0 };
+ SerdEnv* env = NULL;
+ SerdNode base = serd_node_from_string(SERD_URI, USTR(base_uri));
+ SerdWriter* writer = ttl_writer(serd_chunk_sink, &chunk, &base, &env);
+
+ lilv_state_write(world, map, unmap, state, writer, uri, NULL);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+ return (char*)serd_chunk_sink_finish(&chunk);
+}
+
+static void
+try_unlink(const char* path)
+{
+ if (unlink(path)) {
+ LILV_ERRORF("Failed to remove %s (%s)\n", path, strerror(errno));
+ }
+}
+
+LILV_API int
+lilv_state_delete(LilvWorld* world,
+ const LilvState* state)
+{
+ if (!state->dir || !state->uri) {
+ LILV_ERROR("Attempt to delete unsaved state\n");
+ return -1;
+ }
+
+ LilvNode* bundle = lilv_new_file_uri(world, NULL, state->dir);
+ LilvNode* manifest = lilv_world_get_manifest_uri(world, bundle);
+ char* manifest_path = lilv_node_get_path(manifest, NULL);
+ SordModel* model = sord_new(world->world, SORD_SPO, false);
+
+ {
+ // Read manifest into temporary local model
+ SerdEnv* env = serd_env_new(sord_node_to_serd_node(manifest->node));
+ SerdReader* ttl = sord_new_reader(model, env, SERD_TURTLE, NULL);
+ serd_reader_read_file(ttl, USTR(manifest_path));
+ serd_reader_free(ttl);
+ serd_env_free(env);
+ }
+
+ SordNode* file = sord_get(
+ model, state->uri->node, world->uris.rdfs_seeAlso, NULL, NULL);
+ if (file) {
+ // Remove state file
+ char* path = lilv_file_uri_parse(
+ (const char*)sord_node_get_string(file), NULL);
+ try_unlink(path);
+ lilv_free(path);
+ }
+
+ // Remove any existing manifest entries for this state
+ remove_manifest_entry(
+ world->world, model, lilv_node_as_string(state->uri));
+ remove_manifest_entry(
+ world->world, world->model, lilv_node_as_string(state->uri));
+
+ // Drop bundle from model
+ lilv_world_unload_bundle(world, bundle);
+
+ if (sord_num_quads(model) == 0) {
+ // Manifest is empty, attempt to remove bundle entirely
+ try_unlink(manifest_path);
+
+ // Remove all known files from state bundle
+ for (ZixTreeIter* i = zix_tree_begin(state->abs2rel);
+ i != zix_tree_end(state->abs2rel);
+ i = zix_tree_iter_next(i)) {
+ const PathMap* pm = (const PathMap*)zix_tree_get(i);
+ char* path = lilv_path_join(state->dir, pm->rel);
+ try_unlink(path);
+ free(path);
+ }
+
+ if (rmdir(state->dir)) {
+ LILV_ERRORF("Failed to remove directory %s (%s)\n",
+ state->dir, strerror(errno));
+ }
+ } else {
+ // Still something in the manifest, reload bundle
+ lilv_world_load_bundle(world, bundle);
+ }
+
+ sord_free(model);
+ lilv_free(manifest_path);
+ lilv_node_free(manifest);
+ lilv_node_free(bundle);
+
+ return 0;
+}
+
+static void
+free_property_array(LilvState* state, PropertyArray* array)
+{
+ for (uint32_t i = 0; i < array->n; ++i) {
+ Property* prop = &array->props[i];
+ if ((prop->flags & LV2_STATE_IS_POD) ||
+ prop->type == state->atom_Path) {
+ free(prop->value);
+ }
+ }
+ free(array->props);
+}
+
+LILV_API void
+lilv_state_free(LilvState* state)
+{
+ if (state) {
+ free_property_array(state, &state->props);
+ free_property_array(state, &state->metadata);
+ for (uint32_t i = 0; i < state->n_values; ++i) {
+ free(state->values[i].value);
+ free(state->values[i].symbol);
+ }
+ lilv_node_free(state->plugin_uri);
+ lilv_node_free(state->uri);
+ zix_tree_free(state->abs2rel);
+ zix_tree_free(state->rel2abs);
+ free(state->values);
+ free(state->label);
+ free(state->dir);
+ free(state->file_dir);
+ free(state->copy_dir);
+ free(state->link_dir);
+ free(state);
+ }
+}
+
+LILV_API bool
+lilv_state_equals(const LilvState* a, const LilvState* b)
+{
+ if (!lilv_node_equals(a->plugin_uri, b->plugin_uri)
+ || (a->label && !b->label)
+ || (b->label && !a->label)
+ || (a->label && b->label && strcmp(a->label, b->label))
+ || a->props.n != b->props.n
+ || a->n_values != b->n_values) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < a->n_values; ++i) {
+ PortValue* const av = &a->values[i];
+ PortValue* const bv = &b->values[i];
+ if (av->size != bv->size || av->type != bv->type
+ || strcmp(av->symbol, bv->symbol)
+ || memcmp(av->value, bv->value, av->size)) {
+ return false;
+ }
+ }
+
+ for (uint32_t i = 0; i < a->props.n; ++i) {
+ Property* const ap = &a->props.props[i];
+ Property* const bp = &b->props.props[i];
+ if (ap->key != bp->key
+ || ap->type != bp->type
+ || ap->flags != bp->flags) {
+ return false;
+ } else if (ap->type == a->atom_Path) {
+ if (!lilv_file_equals(lilv_state_rel2abs(a, (char*)ap->value),
+ lilv_state_rel2abs(b, (char*)bp->value))) {
+ return false;
+ }
+ } else if (ap->size != bp->size
+ || memcmp(ap->value, bp->value, ap->size)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+LILV_API unsigned
+lilv_state_get_num_properties(const LilvState* state)
+{
+ return state->props.n;
+}
+
+LILV_API const LilvNode*
+lilv_state_get_plugin_uri(const LilvState* state)
+{
+ return state->plugin_uri;
+}
+
+LILV_API const LilvNode*
+lilv_state_get_uri(const LilvState* state)
+{
+ return state->uri;
+}
+
+LILV_API const char*
+lilv_state_get_label(const LilvState* state)
+{
+ return state->label;
+}
+
+LILV_API void
+lilv_state_set_label(LilvState* state, const char* label)
+{
+ const size_t len = strlen(label);
+ state->label = (char*)realloc(state->label, len + 1);
+ memcpy(state->label, label, len + 1);
+}
+
+LILV_API int
+lilv_state_set_metadata(LilvState* state,
+ uint32_t key,
+ const void* value,
+ size_t size,
+ uint32_t type,
+ uint32_t flags)
+{
+ append_property(state, &state->metadata, key, value, size, type, flags);
+ return LV2_STATE_SUCCESS;
+}
diff --git a/src/ui.c b/src/ui.c
new file mode 100644
index 0000000..e3a9b2b
--- /dev/null
+++ b/src/ui.c
@@ -0,0 +1,111 @@
+/*
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv_internal.h"
+
+LilvUI*
+lilv_ui_new(LilvWorld* world,
+ LilvNode* uri,
+ LilvNode* type_uri,
+ LilvNode* binary_uri)
+{
+ assert(uri);
+ assert(type_uri);
+ assert(binary_uri);
+
+ LilvUI* ui = (LilvUI*)malloc(sizeof(LilvUI));
+ ui->world = world;
+ ui->uri = uri;
+ ui->binary_uri = binary_uri;
+
+ // FIXME: kludge
+ char* bundle = lilv_strdup(lilv_node_as_string(ui->binary_uri));
+ char* last_slash = strrchr(bundle, '/') + 1;
+ *last_slash = '\0';
+ ui->bundle_uri = lilv_new_uri(world, bundle);
+ free(bundle);
+
+ ui->classes = lilv_nodes_new();
+ zix_tree_insert((ZixTree*)ui->classes, type_uri, NULL);
+
+ return ui;
+}
+
+void
+lilv_ui_free(LilvUI* ui)
+{
+ lilv_node_free(ui->uri);
+ lilv_node_free(ui->bundle_uri);
+ lilv_node_free(ui->binary_uri);
+ lilv_nodes_free(ui->classes);
+ free(ui);
+}
+
+LILV_API const LilvNode*
+lilv_ui_get_uri(const LilvUI* ui)
+{
+ return ui->uri;
+}
+
+LILV_API unsigned
+lilv_ui_is_supported(const LilvUI* ui,
+ LilvUISupportedFunc supported_func,
+ const LilvNode* container_type,
+ const LilvNode** ui_type)
+{
+ const LilvNodes* classes = lilv_ui_get_classes(ui);
+ LILV_FOREACH(nodes, c, classes) {
+ const LilvNode* type = lilv_nodes_get(classes, c);
+ const unsigned q = supported_func(lilv_node_as_uri(container_type),
+ lilv_node_as_uri(type));
+ if (q) {
+ if (ui_type) {
+ *ui_type = type;
+ }
+ return q;
+ }
+ }
+
+ return 0;
+}
+
+LILV_API const LilvNodes*
+lilv_ui_get_classes(const LilvUI* ui)
+{
+ return ui->classes;
+}
+
+LILV_API bool
+lilv_ui_is_a(const LilvUI* ui, const LilvNode* class_uri)
+{
+ return lilv_nodes_contains(ui->classes, class_uri);
+}
+
+LILV_API const LilvNode*
+lilv_ui_get_bundle_uri(const LilvUI* ui)
+{
+ return ui->bundle_uri;
+}
+
+LILV_API const LilvNode*
+lilv_ui_get_binary_uri(const LilvUI* ui)
+{
+ return ui->binary_uri;
+}
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..e896d65
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,659 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define _POSIX_C_SOURCE 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 <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+
+#ifdef _WIN32
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0600 /* for CreateSymbolicLink */
+#endif
+# include <windows.h>
+# include <direct.h>
+# include <io.h>
+# define F_OK 0
+# define mkdir(path, flags) _mkdir(path)
+# if (defined(_MSC_VER) && _MSC_VER <= 1400) || defined(__MINGW64__) || defined(__MINGW32__)
+/** Implement 'CreateSymbolicLink()' for MSVC 8 or earlier */
+#ifdef __cplusplus
+extern "C"
+#endif
+BOOLEAN WINAPI
+CreateSymbolicLink(LPCTSTR linkpath, LPCTSTR targetpath, DWORD flags)
+{
+ typedef BOOLEAN (WINAPI* PFUNC)(LPCTSTR, LPCTSTR, DWORD);
+
+ PFUNC pfn = (PFUNC)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
+ "CreateSymbolicLinkA");
+ return pfn ? pfn(linkpath, targetpath, flags) : 0;
+}
+# endif
+#else
+# include <dirent.h>
+# include <limits.h>
+# include <unistd.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "lilv_internal.h"
+
+#if defined(HAVE_FLOCK) && defined(HAVE_FILENO)
+# include <sys/file.h>
+#endif
+
+#ifndef PAGE_SIZE
+# define PAGE_SIZE 4096
+#endif
+
+void
+lilv_free(void* ptr)
+{
+ free(ptr);
+}
+
+char*
+lilv_strjoin(const char* first, ...)
+{
+ size_t len = strlen(first);
+ char* result = (char*)malloc(len + 1);
+
+ memcpy(result, first, len);
+
+ va_list args;
+ va_start(args, first);
+ while (1) {
+ const char* const s = va_arg(args, const char *);
+ if (s == NULL) {
+ break;
+ }
+
+ const size_t this_len = strlen(s);
+ char* new_result = (char*)realloc(result, len + this_len + 1);
+ if (!new_result) {
+ free(result);
+ return NULL;
+ }
+
+ result = new_result;
+ memcpy(result + len, s, this_len);
+ len += this_len;
+ }
+ va_end(args);
+
+ result[len] = '\0';
+
+ return result;
+}
+
+char*
+lilv_strdup(const char* str)
+{
+ if (!str) {
+ return NULL;
+ }
+
+ const size_t len = strlen(str);
+ char* copy = (char*)malloc(len + 1);
+ memcpy(copy, str, len + 1);
+ return copy;
+}
+
+const char*
+lilv_uri_to_path(const char* uri)
+{
+ return (const char*)serd_uri_to_path((const uint8_t*)uri);
+}
+
+char*
+lilv_file_uri_parse(const char* uri, char** hostname)
+{
+ return (char*)serd_file_uri_parse((const uint8_t*)uri, (uint8_t**)hostname);
+}
+
+/** Return the current LANG converted to Turtle (i.e. RFC3066) style.
+ * For example, if LANG is set to "en_CA.utf-8", this returns "en-ca".
+ */
+char*
+lilv_get_lang(void)
+{
+ const char* const env_lang = getenv("LANG");
+ if (!env_lang || !strcmp(env_lang, "")
+ || !strcmp(env_lang, "C") || !strcmp(env_lang, "POSIX")) {
+ return NULL;
+ }
+
+ const size_t env_lang_len = strlen(env_lang);
+ char* const lang = (char*)malloc(env_lang_len + 1);
+ for (size_t i = 0; i < env_lang_len + 1; ++i) {
+ if (env_lang[i] == '_') {
+ lang[i] = '-'; // Convert _ to -
+ } else if (env_lang[i] >= 'A' && env_lang[i] <= 'Z') {
+ lang[i] = env_lang[i] + ('a' - 'A'); // Convert to lowercase
+ } else if (env_lang[i] >= 'a' && env_lang[i] <= 'z') {
+ lang[i] = env_lang[i]; // Lowercase letter, copy verbatim
+ } else if (env_lang[i] >= '0' && env_lang[i] <= '9') {
+ lang[i] = env_lang[i]; // Digit, copy verbatim
+ } else if (env_lang[i] == '\0' || env_lang[i] == '.') {
+ // End, or start of suffix (e.g. en_CA.utf-8), finished
+ lang[i] = '\0';
+ break;
+ } else {
+ LILV_ERRORF("Illegal LANG `%s' ignored\n", env_lang);
+ free(lang);
+ return NULL;
+ }
+ }
+
+ return lang;
+}
+
+/** Append suffix to dst, update dst_len, and return the realloc'd result. */
+static char*
+strappend(char* dst, size_t* dst_len, const char* suffix, size_t suffix_len)
+{
+ dst = (char*)realloc(dst, *dst_len + suffix_len + 1);
+ memcpy(dst + *dst_len, suffix, suffix_len);
+ dst[(*dst_len += suffix_len)] = '\0';
+ return dst;
+}
+
+/** Append the value of the environment variable var to dst. */
+static char*
+append_var(char* dst, size_t* dst_len, const char* var)
+{
+ // Get value from environment
+ const char* val = getenv(var);
+ if (val) { // Value found, append it
+ return strappend(dst, dst_len, val, strlen(val));
+ } else { // No value found, append variable reference as-is
+ return strappend(strappend(dst, dst_len, "$", 1),
+ dst_len, var, strlen(var));
+ }
+}
+
+/** Expand variables (e.g. POSIX ~ or $FOO, Windows %FOO%) in `path`. */
+char*
+lilv_expand(const char* path)
+{
+#ifdef _WIN32
+ char* out = (char*)malloc(MAX_PATH);
+ ExpandEnvironmentStrings(path, out, MAX_PATH);
+#else
+ char* out = NULL;
+ size_t len = 0;
+
+ const char* start = path; // Start of current chunk to copy
+ for (const char* s = path; *s;) {
+ if (*s == '$') {
+ // Hit $ (variable reference, e.g. $VAR_NAME)
+ for (const char* t = s + 1; ; ++t) {
+ if (!*t || (!isupper(*t) && !isdigit(*t) && *t != '_')) {
+ // Append preceding chunk
+ out = strappend(out, &len, start, s - start);
+
+ // Append variable value (or $VAR_NAME if not found)
+ char* var = (char*)calloc(t - s, 1);
+ memcpy(var, s + 1, t - s - 1);
+ out = append_var(out, &len, var);
+ free(var);
+
+ // Continue after variable reference
+ start = s = t;
+ break;
+ }
+ }
+ } else if (*s == '~' && (*(s + 1) == '/' || !*(s + 1))) {
+ // Hit ~ before slash or end of string (home directory reference)
+ out = strappend(out, &len, start, s - start);
+ out = append_var(out, &len, "HOME");
+ start = ++s;
+ } else {
+ ++s;
+ }
+ }
+
+ if (*start) {
+ out = strappend(out, &len, start, strlen(start));
+ }
+#endif
+
+ return out;
+}
+
+static bool
+lilv_is_dir_sep(const char c)
+{
+ return c == '/' || c == LILV_DIR_SEP[0];
+}
+
+char*
+lilv_dirname(const char* path)
+{
+ const char* s = path + strlen(path) - 1; // Last character
+ for (; s > path && lilv_is_dir_sep(*s); --s) {} // Last non-slash
+ for (; s > path && !lilv_is_dir_sep(*s); --s) {} // Last internal slash
+ for (; s > path && lilv_is_dir_sep(*s); --s) {} // Skip duplicates
+
+ if (s == path) { // Hit beginning
+ return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup(".");
+ } else { // Pointing to the last character of the result (inclusive)
+ char* dirname = (char*)malloc(s - path + 2);
+ memcpy(dirname, path, s - path + 1);
+ dirname[s - path + 1] = '\0';
+ return dirname;
+ }
+}
+
+bool
+lilv_path_exists(const char* path, const void* ignored)
+{
+#ifdef HAVE_LSTAT
+ struct stat st;
+ return !lstat(path, &st);
+#else
+ return !access(path, F_OK);
+#endif
+}
+
+char*
+lilv_find_free_path(const char* in_path,
+ bool (*exists)(const char*, const void*),
+ const void* user_data)
+{
+ const size_t in_path_len = strlen(in_path);
+ char* path = (char*)malloc(in_path_len + 7);
+ memcpy(path, in_path, in_path_len + 1);
+
+ for (int i = 2; i < 1000000; ++i) {
+ if (!exists(path, user_data)) {
+ return path;
+ }
+ snprintf(path, in_path_len + 7, "%s.%u", in_path, i);
+ }
+
+ return NULL;
+}
+
+int
+lilv_copy_file(const char* src, const char* dst)
+{
+ FILE* in = fopen(src, "r");
+ if (!in) {
+ return errno;
+ }
+
+ FILE* out = fopen(dst, "w");
+ if (!out) {
+ fclose(in);
+ return errno;
+ }
+
+ char* page = (char*)malloc(PAGE_SIZE);
+ size_t n_read = 0;
+ int st = 0;
+ while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) {
+ if (fwrite(page, 1, n_read, out) != n_read) {
+ st = errno;
+ break;
+ }
+ }
+
+ if (!st && (ferror(in) || ferror(out))) {
+ st = EBADF;
+ }
+
+ free(page);
+ fclose(in);
+ fclose(out);
+
+ return st;
+}
+
+static inline bool
+is_windows_path(const char* path)
+{
+ return (isalpha(path[0]) && (path[1] == ':' || path[1] == '|') &&
+ (path[2] == '/' || path[2] == '\\'));
+}
+
+bool
+lilv_path_is_absolute(const char* path)
+{
+ if (lilv_is_dir_sep(path[0])) {
+ return true;
+ }
+
+#ifdef _WIN32
+ if (is_windows_path(path)) {
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+char*
+lilv_path_absolute(const char* path)
+{
+ if (lilv_path_is_absolute(path)) {
+ return lilv_strdup(path);
+ } else {
+ char* cwd = getcwd(NULL, 0);
+ char* abs_path = lilv_path_join(cwd, path);
+ free(cwd);
+ return abs_path;
+ }
+}
+
+char*
+lilv_path_join(const char* a, const char* b)
+{
+ if (!a) {
+ return lilv_strdup(b);
+ }
+
+ const size_t a_len = strlen(a);
+ const size_t b_len = b ? strlen(b) : 0;
+ const size_t pre_len = a_len - (lilv_is_dir_sep(a[a_len - 1]) ? 1 : 0);
+ char* path = (char*)calloc(1, a_len + b_len + 2);
+ memcpy(path, a, pre_len);
+ path[pre_len] = '/';
+ if (b) {
+ memcpy(path + pre_len + 1,
+ b + (lilv_is_dir_sep(b[0]) ? 1 : 0),
+ lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len);
+ }
+ return path;
+}
+
+typedef struct {
+ char* pattern;
+ time_t time;
+ char* latest;
+} Latest;
+
+static void
+update_latest(const char* path, const char* name, void* data)
+{
+ Latest* latest = (Latest*)data;
+ char* entry_path = lilv_path_join(path, name);
+ unsigned num;
+ if (sscanf(entry_path, latest->pattern, &num) == 1) {
+ struct stat st;
+ if (!stat(entry_path, &st)) {
+ if (st.st_mtime >= latest->time) {
+ free(latest->latest);
+ latest->latest = entry_path;
+ }
+ } else {
+ LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
+ }
+ }
+ if (entry_path != latest->latest) {
+ free(entry_path);
+ }
+}
+
+/** Return the latest copy of the file at `path` that is newer. */
+char*
+lilv_get_latest_copy(const char* path, const char* copy_path)
+{
+ char* copy_dir = lilv_dirname(copy_path);
+ Latest latest = { lilv_strjoin(copy_path, ".%u", NULL), 0, NULL };
+
+ struct stat st;
+ if (!stat(path, &st)) {
+ latest.time = st.st_mtime;
+ } else {
+ LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
+ }
+
+ lilv_dir_for_each(copy_dir, &latest, update_latest);
+
+ free(latest.pattern);
+ free(copy_dir);
+ return latest.latest;
+}
+
+char*
+lilv_realpath(const char* path)
+{
+ if (!path) {
+ return NULL;
+ }
+
+#if defined(_WIN32)
+ char* out = (char*)malloc(MAX_PATH);
+ GetFullPathName(path, MAX_PATH, out, NULL);
+ return out;
+#elif _POSIX_VERSION >= 200809L
+ char* real_path = realpath(path, NULL);
+ return real_path ? real_path : lilv_strdup(path);
+#else
+ // OSX <= 10.5, if anyone cares. I sure don't.
+ char* out = (char*)malloc(PATH_MAX);
+ char* real_path = realpath(path, out);
+ if (!real_path) {
+ free(out);
+ return lilv_strdup(path);
+ } else {
+ return real_path;
+ }
+#endif
+}
+
+int
+lilv_symlink(const char* oldpath, const char* newpath)
+{
+ int ret = 0;
+ if (strcmp(oldpath, newpath)) {
+#ifdef _WIN32
+ ret = !CreateSymbolicLink(newpath, oldpath, 0);
+ if (ret) {
+ ret = !CreateHardLink(newpath, oldpath, 0);
+ }
+#else
+ ret = symlink(oldpath, newpath);
+#endif
+ }
+ if (ret) {
+ LILV_ERRORF("Failed to link %s => %s (%s)\n",
+ newpath, oldpath, strerror(errno));
+ }
+ return ret;
+}
+
+char*
+lilv_path_relative_to(const char* path, const char* base)
+{
+ const size_t path_len = strlen(path);
+ const size_t base_len = strlen(base);
+ const size_t min_len = (path_len < base_len) ? path_len : base_len;
+
+ // Find the last separator common to both paths
+ size_t last_shared_sep = 0;
+ for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) {
+ if (lilv_is_dir_sep(path[i])) {
+ last_shared_sep = i;
+ }
+ }
+
+ if (last_shared_sep == 0) {
+ // No common components, return path
+ return lilv_strdup(path);
+ }
+
+ // Find the number of up references ("..") required
+ size_t up = 0;
+ for (size_t i = last_shared_sep + 1; i < base_len; ++i) {
+ if (lilv_is_dir_sep(base[i])) {
+ ++up;
+ }
+ }
+
+ // Write up references
+ const size_t suffix_len = path_len - last_shared_sep;
+ char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1);
+ for (size_t i = 0; i < up; ++i) {
+ memcpy(rel + (i * 3), "../", 3);
+ }
+
+ // Write suffix
+ memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len);
+ return rel;
+}
+
+bool
+lilv_path_is_child(const char* path, const char* dir)
+{
+ if (path && dir) {
+ const size_t path_len = strlen(path);
+ const size_t dir_len = strlen(dir);
+ return dir && path_len >= dir_len && !strncmp(path, dir, dir_len);
+ }
+ return false;
+}
+
+int
+lilv_flock(FILE* file, bool lock)
+{
+#if defined(HAVE_FLOCK) && defined(HAVE_FILENO)
+ return flock(fileno(file), lock ? LOCK_EX : LOCK_UN);
+#else
+ return 0;
+#endif
+}
+
+void
+lilv_dir_for_each(const char* path,
+ void* data,
+ void (*f)(const char* path, const char* name, void* data))
+{
+#ifdef _WIN32
+ char* pat = lilv_path_join(path, "*");
+ WIN32_FIND_DATA fd;
+ HANDLE fh = FindFirstFile(pat, &fd);
+ if (fh != INVALID_HANDLE_VALUE) {
+ do {
+ f(path, fd.cFileName, data);
+ } while (FindNextFile(fh, &fd));
+ }
+ free(pat);
+#else
+ DIR* dir = opendir(path);
+ if (dir) {
+ for (struct dirent* entry; (entry = readdir(dir));) {
+ f(path, entry->d_name, data);
+ }
+ closedir(dir);
+ }
+#endif
+}
+
+int
+lilv_mkdir_p(const char* dir_path)
+{
+ char* path = lilv_strdup(dir_path);
+ const size_t path_len = strlen(path);
+ size_t i = 1;
+
+#ifdef _WIN32
+ if (is_windows_path(dir_path)) {
+ i = 3;
+ }
+#endif
+
+ for (; i <= path_len; ++i) {
+ if (path[i] == LILV_DIR_SEP[0] || path[i] == '\0') {
+ path[i] = '\0';
+ if (mkdir(path, 0755) && errno != EEXIST) {
+ free(path);
+ return errno;
+ }
+ path[i] = LILV_DIR_SEP[0];
+ }
+ }
+
+ free(path);
+ return 0;
+}
+
+static off_t
+lilv_file_size(const char* path)
+{
+ struct stat buf;
+ if (stat(path, &buf)) {
+ LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
+ return 0;
+ }
+ return buf.st_size;
+}
+
+bool
+lilv_file_equals(const char* a_path, const char* b_path)
+{
+ if (!strcmp(a_path, b_path)) {
+ return true; // Paths match
+ }
+
+ bool match = false;
+ FILE* a_file = NULL;
+ FILE* b_file = NULL;
+ char* const a_real = lilv_realpath(a_path);
+ char* const b_real = lilv_realpath(b_path);
+ if (!strcmp(a_real, b_real)) {
+ match = true; // Real paths match
+ } else if (lilv_file_size(a_path) != lilv_file_size(b_path)) {
+ match = false; // Sizes differ
+ } else if (!(a_file = fopen(a_real, "rb")) ||
+ !(b_file = fopen(b_real, "rb"))) {
+ match = false; // Missing file matches nothing
+ } else {
+ // TODO: Improve performance by reading chunks
+ match = true;
+ while (!feof(a_file) && !feof(b_file)) {
+ if (fgetc(a_file) != fgetc(b_file)) {
+ match = false;
+ break;
+ }
+ }
+ }
+
+ if (a_file) {
+ fclose(a_file);
+ }
+ if (b_file) {
+ fclose(b_file);
+ }
+ free(a_real);
+ free(b_real);
+ return match;
+}
diff --git a/src/world.c b/src/world.c
new file mode 100644
index 0000000..1858569
--- /dev/null
+++ b/src/world.c
@@ -0,0 +1,1212 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+
+#include "lilv_internal.h"
+
+static int
+lilv_world_drop_graph(LilvWorld* world, const SordNode* graph);
+
+LILV_API LilvWorld*
+lilv_world_new(void)
+{
+ LilvWorld* world = (LilvWorld*)malloc(sizeof(LilvWorld));
+
+ world->world = sord_world_new();
+ if (!world->world) {
+ goto fail;
+ }
+
+ world->model = sord_new(world->world, SORD_SPO|SORD_OPS, true);
+ if (!world->model) {
+ goto fail;
+ }
+
+ world->specs = NULL;
+ world->plugin_classes = lilv_plugin_classes_new();
+ world->plugins = lilv_plugins_new();
+ world->zombies = lilv_plugins_new();
+ world->loaded_files = zix_tree_new(
+ false, lilv_resource_node_cmp, NULL, (ZixDestroyFunc)lilv_node_free);
+
+ world->libs = zix_tree_new(false, lilv_lib_compare, NULL, NULL);
+
+#define NS_DCTERMS "http://purl.org/dc/terms/"
+#define NS_DYNMAN "http://lv2plug.in/ns/ext/dynmanifest#"
+#define NS_OWL "http://www.w3.org/2002/07/owl#"
+
+#define NEW_URI(uri) sord_new_uri(world->world, (const uint8_t*)(uri))
+
+ world->uris.dc_replaces = NEW_URI(NS_DCTERMS "replaces");
+ world->uris.dman_DynManifest = NEW_URI(NS_DYNMAN "DynManifest");
+ world->uris.doap_name = NEW_URI(LILV_NS_DOAP "name");
+ world->uris.lv2_Plugin = NEW_URI(LV2_CORE__Plugin);
+ world->uris.lv2_Specification = NEW_URI(LV2_CORE__Specification);
+ world->uris.lv2_appliesTo = NEW_URI(LV2_CORE__appliesTo);
+ world->uris.lv2_binary = NEW_URI(LV2_CORE__binary);
+ world->uris.lv2_default = NEW_URI(LV2_CORE__default);
+ world->uris.lv2_designation = NEW_URI(LV2_CORE__designation);
+ world->uris.lv2_extensionData = NEW_URI(LV2_CORE__extensionData);
+ world->uris.lv2_index = NEW_URI(LV2_CORE__index);
+ world->uris.lv2_latency = NEW_URI(LV2_CORE__latency);
+ world->uris.lv2_maximum = NEW_URI(LV2_CORE__maximum);
+ world->uris.lv2_microVersion = NEW_URI(LV2_CORE__microVersion);
+ world->uris.lv2_minimum = NEW_URI(LV2_CORE__minimum);
+ world->uris.lv2_minorVersion = NEW_URI(LV2_CORE__minorVersion);
+ world->uris.lv2_name = NEW_URI(LV2_CORE__name);
+ world->uris.lv2_optionalFeature = NEW_URI(LV2_CORE__optionalFeature);
+ world->uris.lv2_port = NEW_URI(LV2_CORE__port);
+ world->uris.lv2_portProperty = NEW_URI(LV2_CORE__portProperty);
+ world->uris.lv2_reportsLatency = NEW_URI(LV2_CORE__reportsLatency);
+ world->uris.lv2_requiredFeature = NEW_URI(LV2_CORE__requiredFeature);
+ world->uris.lv2_symbol = NEW_URI(LV2_CORE__symbol);
+ world->uris.lv2_prototype = NEW_URI(LV2_CORE__prototype);
+ world->uris.owl_Ontology = NEW_URI(NS_OWL "Ontology");
+ world->uris.pset_value = NEW_URI(LV2_PRESETS__value);
+ world->uris.rdf_a = NEW_URI(LILV_NS_RDF "type");
+ world->uris.rdf_value = NEW_URI(LILV_NS_RDF "value");
+ world->uris.rdfs_Class = NEW_URI(LILV_NS_RDFS "Class");
+ world->uris.rdfs_label = NEW_URI(LILV_NS_RDFS "label");
+ world->uris.rdfs_seeAlso = NEW_URI(LILV_NS_RDFS "seeAlso");
+ world->uris.rdfs_subClassOf = NEW_URI(LILV_NS_RDFS "subClassOf");
+ world->uris.xsd_base64Binary = NEW_URI(LILV_NS_XSD "base64Binary");
+ world->uris.xsd_boolean = NEW_URI(LILV_NS_XSD "boolean");
+ world->uris.xsd_decimal = NEW_URI(LILV_NS_XSD "decimal");
+ world->uris.xsd_double = NEW_URI(LILV_NS_XSD "double");
+ world->uris.xsd_integer = NEW_URI(LILV_NS_XSD "integer");
+ world->uris.null_uri = NULL;
+
+ world->lv2_plugin_class = lilv_plugin_class_new(
+ world, NULL, world->uris.lv2_Plugin, "Plugin");
+ assert(world->lv2_plugin_class);
+
+ world->n_read_files = 0;
+ world->opt.filter_language = true;
+ world->opt.dyn_manifest = true;
+
+ return world;
+
+fail:
+ /* keep on rockin' in the */ free(world);
+ return NULL;
+}
+
+LILV_API void
+lilv_world_free(LilvWorld* world)
+{
+ if (!world) {
+ return;
+ }
+
+ lilv_plugin_class_free(world->lv2_plugin_class);
+ world->lv2_plugin_class = NULL;
+
+ for (SordNode** n = (SordNode**)&world->uris; *n; ++n) {
+ sord_node_free(world->world, *n);
+ }
+
+ for (LilvSpec* spec = world->specs; spec;) {
+ LilvSpec* next = spec->next;
+ sord_node_free(world->world, spec->spec);
+ sord_node_free(world->world, spec->bundle);
+ lilv_nodes_free(spec->data_uris);
+ free(spec);
+ spec = next;
+ }
+ world->specs = NULL;
+
+ LILV_FOREACH(plugins, i, world->plugins) {
+ const LilvPlugin* p = lilv_plugins_get(world->plugins, i);
+ lilv_plugin_free((LilvPlugin*)p);
+ }
+ zix_tree_free((ZixTree*)world->plugins);
+ world->plugins = NULL;
+
+ LILV_FOREACH(plugins, i, world->zombies) {
+ const LilvPlugin* p = lilv_plugins_get(world->zombies, i);
+ lilv_plugin_free((LilvPlugin*)p);
+ }
+ zix_tree_free((ZixTree*)world->zombies);
+ world->zombies = NULL;
+
+ zix_tree_free((ZixTree*)world->loaded_files);
+ world->loaded_files = NULL;
+
+ zix_tree_free(world->libs);
+ world->libs = NULL;
+
+ zix_tree_free((ZixTree*)world->plugin_classes);
+ world->plugin_classes = NULL;
+
+ sord_free(world->model);
+ world->model = NULL;
+
+ sord_world_free(world->world);
+ world->world = NULL;
+
+ free(world);
+}
+
+LILV_API void
+lilv_world_set_option(LilvWorld* world,
+ const char* uri,
+ const LilvNode* value)
+{
+ if (!strcmp(uri, LILV_OPTION_DYN_MANIFEST)) {
+ if (lilv_node_is_bool(value)) {
+ world->opt.dyn_manifest = lilv_node_as_bool(value);
+ return;
+ }
+ } else if (!strcmp(uri, LILV_OPTION_FILTER_LANG)) {
+ if (lilv_node_is_bool(value)) {
+ world->opt.filter_language = lilv_node_as_bool(value);
+ return;
+ }
+ }
+ LILV_WARNF("Unrecognized or invalid option `%s'\n", uri);
+}
+
+LILV_API LilvNodes*
+lilv_world_find_nodes(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object)
+{
+ if (subject && !lilv_node_is_uri(subject) && !lilv_node_is_blank(subject)) {
+ LILV_ERRORF("Subject `%s' is not a resource\n",
+ sord_node_get_string(subject->node));
+ return NULL;
+ } else if (!predicate) {
+ LILV_ERROR("Missing required predicate\n");
+ return NULL;
+ } else if (!lilv_node_is_uri(predicate)) {
+ LILV_ERRORF("Predicate `%s' is not a URI\n",
+ sord_node_get_string(predicate->node));
+ return NULL;
+ } else if (!subject && !object) {
+ LILV_ERROR("Both subject and object are NULL\n");
+ return NULL;
+ }
+
+ return lilv_world_find_nodes_internal(world,
+ subject ? subject->node : NULL,
+ predicate->node,
+ object ? object->node : NULL);
+}
+
+LILV_API LilvNode*
+lilv_world_get(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object)
+{
+ SordNode* snode = sord_get(world->model,
+ subject ? subject->node : NULL,
+ predicate ? predicate->node : NULL,
+ object ? object->node : NULL,
+ NULL);
+ LilvNode* lnode = lilv_node_new_from_node(world, snode);
+ sord_node_free(world->world, snode);
+ return lnode;
+}
+
+SordIter*
+lilv_world_query_internal(LilvWorld* world,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object)
+{
+ return sord_search(world->model, subject, predicate, object, NULL);
+}
+
+bool
+lilv_world_ask_internal(LilvWorld* world,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object)
+{
+ return sord_ask(world->model, subject, predicate, object, NULL);
+}
+
+LILV_API bool
+lilv_world_ask(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object)
+{
+ return sord_ask(world->model,
+ subject ? subject->node : NULL,
+ predicate ? predicate->node : NULL,
+ object ? object->node : NULL,
+ NULL);
+}
+
+SordModel*
+lilv_world_filter_model(LilvWorld* world,
+ SordModel* model,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object,
+ const SordNode* graph)
+{
+ SordModel* results = sord_new(world->world, SORD_SPO, false);
+ SordIter* i = sord_search(model, subject, predicate, object, graph);
+ for (; !sord_iter_end(i); sord_iter_next(i)) {
+ SordQuad quad;
+ sord_iter_get(i, quad);
+ sord_add(results, quad);
+ }
+ sord_iter_free(i);
+ return results;
+}
+
+LilvNodes*
+lilv_world_find_nodes_internal(LilvWorld* world,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object)
+{
+ return lilv_nodes_from_stream_objects(
+ world,
+ lilv_world_query_internal(world, subject, predicate, object),
+ (object == NULL) ? SORD_OBJECT : SORD_SUBJECT);
+}
+
+static SerdNode
+lilv_new_uri_relative_to_base(const uint8_t* uri_str,
+ const uint8_t* base_uri_str)
+{
+ SerdURI base_uri;
+ serd_uri_parse(base_uri_str, &base_uri);
+ return serd_node_new_uri_from_string(uri_str, &base_uri, NULL);
+}
+
+const uint8_t*
+lilv_world_blank_node_prefix(LilvWorld* world)
+{
+ static char str[32];
+ snprintf(str, sizeof(str), "%d", world->n_read_files++);
+ return (const uint8_t*)str;
+}
+
+/** Comparator for sequences (e.g. world->plugins). */
+int
+lilv_header_compare_by_uri(const void* a, const void* b, void* user_data)
+{
+ const struct LilvHeader* const header_a = (const struct LilvHeader*)a;
+ const struct LilvHeader* const header_b = (const struct LilvHeader*)b;
+ return strcmp(lilv_node_as_uri(header_a->uri),
+ lilv_node_as_uri(header_b->uri));
+}
+
+/**
+ Comparator for libraries (world->libs).
+
+ Libraries do have a LilvHeader, but we must also compare the bundle to
+ handle the case where the same library is loaded with different bundles, and
+ consequently different contents (mainly plugins).
+ */
+int
+lilv_lib_compare(const void* a, const void* b, void* user_data)
+{
+ const LilvLib* const lib_a = (const LilvLib*)a;
+ const LilvLib* const lib_b = (const LilvLib*)b;
+ int cmp = strcmp(lilv_node_as_uri(lib_a->uri),
+ lilv_node_as_uri(lib_b->uri));
+ return cmp ? cmp : strcmp(lib_a->bundle_path, lib_b->bundle_path);
+}
+
+/** Get an element of a collection of any object with an LilvHeader by URI. */
+static ZixTreeIter*
+lilv_collection_find_by_uri(const ZixTree* seq, const LilvNode* uri)
+{
+ ZixTreeIter* i = NULL;
+ if (lilv_node_is_uri(uri)) {
+ struct LilvHeader key = { NULL, (LilvNode*)uri };
+ zix_tree_find(seq, &key, &i);
+ }
+ return i;
+}
+
+/** Get an element of a collection of any object with an LilvHeader by URI. */
+struct LilvHeader*
+lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri)
+{
+ ZixTreeIter* const i = lilv_collection_find_by_uri(seq, uri);
+
+ return i ? (struct LilvHeader*)zix_tree_get(i) : NULL;
+}
+
+static void
+lilv_world_add_spec(LilvWorld* world,
+ const SordNode* specification_node,
+ const SordNode* bundle_node)
+{
+ LilvSpec* spec = (LilvSpec*)malloc(sizeof(LilvSpec));
+ spec->spec = sord_node_copy(specification_node);
+ spec->bundle = sord_node_copy(bundle_node);
+ spec->data_uris = lilv_nodes_new();
+
+ // Add all data files (rdfs:seeAlso)
+ SordIter* files = sord_search(world->model,
+ specification_node,
+ world->uris.rdfs_seeAlso,
+ NULL,
+ NULL);
+ FOREACH_MATCH(files) {
+ const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT);
+ zix_tree_insert((ZixTree*)spec->data_uris,
+ lilv_node_new_from_node(world, file_node),
+ NULL);
+ }
+ sord_iter_free(files);
+
+ // Add specification to world specification list
+ spec->next = world->specs;
+ world->specs = spec;
+}
+
+static void
+lilv_world_add_plugin(LilvWorld* world,
+ const SordNode* plugin_node,
+ const LilvNode* manifest_uri,
+ void* dynmanifest,
+ const SordNode* bundle)
+{
+ LilvNode* plugin_uri = lilv_node_new_from_node(world, plugin_node);
+ ZixTreeIter* z = NULL;
+ LilvPlugin* plugin = (LilvPlugin*)lilv_plugins_get_by_uri(
+ world->plugins, plugin_uri);
+
+ if (plugin) {
+ // Existing plugin, if this is different bundle, ignore it
+ // (use the first plugin found in LV2_PATH)
+ const LilvNode* last_bundle = lilv_plugin_get_bundle_uri(plugin);
+ const char* plugin_uri_str = lilv_node_as_uri(plugin_uri);
+ if (sord_node_equals(bundle, last_bundle->node)) {
+ LILV_WARNF("Reloading plugin <%s>\n", plugin_uri_str);
+ plugin->loaded = false;
+ lilv_node_free(plugin_uri);
+ } else {
+ LILV_WARNF("Duplicate plugin <%s>\n", plugin_uri_str);
+ LILV_WARNF("... found in %s\n", lilv_node_as_string(last_bundle));
+ LILV_WARNF("... and %s (ignored)\n", sord_node_get_string(bundle));
+ lilv_node_free(plugin_uri);
+ return;
+ }
+ } else if ((z = lilv_collection_find_by_uri((const ZixTree*)world->zombies,
+ plugin_uri))) {
+ // Plugin bundle has been re-loaded, move from zombies to plugins
+ plugin = (LilvPlugin*)zix_tree_get(z);
+ zix_tree_remove((ZixTree*)world->zombies, z);
+ zix_tree_insert((ZixTree*)world->plugins, plugin, NULL);
+ lilv_node_free(plugin_uri);
+ lilv_plugin_clear(plugin, lilv_node_new_from_node(world, bundle));
+ } else {
+ // Add new plugin to the world
+ plugin = lilv_plugin_new(
+ world, plugin_uri, lilv_node_new_from_node(world, bundle));
+
+ // Add manifest as plugin data file (as if it were rdfs:seeAlso)
+ zix_tree_insert((ZixTree*)plugin->data_uris,
+ lilv_node_duplicate(manifest_uri),
+ NULL);
+
+ // Add plugin to world plugin sequence
+ zix_tree_insert((ZixTree*)world->plugins, plugin, NULL);
+ }
+
+
+#ifdef LILV_DYN_MANIFEST
+ // Set dynamic manifest library URI, if applicable
+ if (dynmanifest) {
+ plugin->dynmanifest = (LilvDynManifest*)dynmanifest;
+ ++((LilvDynManifest*)dynmanifest)->refs;
+ }
+#endif
+
+ // Add all plugin data files (rdfs:seeAlso)
+ SordIter* files = sord_search(world->model,
+ plugin_node,
+ world->uris.rdfs_seeAlso,
+ NULL,
+ NULL);
+ FOREACH_MATCH(files) {
+ const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT);
+ zix_tree_insert((ZixTree*)plugin->data_uris,
+ lilv_node_new_from_node(world, file_node),
+ NULL);
+ }
+ sord_iter_free(files);
+}
+
+SerdStatus
+lilv_world_load_graph(LilvWorld* world, SordNode* graph, const LilvNode* uri)
+{
+ const SerdNode* base = sord_node_to_serd_node(uri->node);
+ SerdEnv* env = serd_env_new(base);
+ SerdReader* reader = sord_new_reader(
+ world->model, env, SERD_TURTLE, graph);
+
+ const SerdStatus st = lilv_world_load_file(world, reader, uri);
+
+ serd_env_free(env);
+ serd_reader_free(reader);
+ return st;
+}
+
+static void
+lilv_world_load_dyn_manifest(LilvWorld* world,
+ SordNode* bundle_node,
+ const LilvNode* manifest)
+{
+#ifdef LILV_DYN_MANIFEST
+ if (!world->opt.dyn_manifest) {
+ return;
+ }
+
+ LV2_Dyn_Manifest_Handle handle = NULL;
+
+ // ?dman a dynman:DynManifest bundle_node
+ SordModel* model = lilv_world_filter_model(world,
+ world->model,
+ NULL,
+ world->uris.rdf_a,
+ world->uris.dman_DynManifest,
+ bundle_node);
+ SordIter* iter = sord_begin(model);
+ for (; !sord_iter_end(iter); sord_iter_next(iter)) {
+ const SordNode* dmanifest = sord_iter_get_node(iter, SORD_SUBJECT);
+
+ // ?dman lv2:binary ?binary
+ SordIter* binaries = sord_search(world->model,
+ dmanifest,
+ world->uris.lv2_binary,
+ NULL,
+ bundle_node);
+ if (sord_iter_end(binaries)) {
+ sord_iter_free(binaries);
+ LILV_ERRORF("Dynamic manifest in <%s> has no binaries, ignored\n",
+ sord_node_get_string(bundle_node));
+ continue;
+ }
+
+ // Get binary path
+ const SordNode* binary = sord_iter_get_node(binaries, SORD_OBJECT);
+ const uint8_t* lib_uri = sord_node_get_string(binary);
+ char* lib_path = lilv_file_uri_parse((const char*)lib_uri, 0);
+ if (!lib_path) {
+ LILV_ERROR("No dynamic manifest library path\n");
+ sord_iter_free(binaries);
+ continue;
+ }
+
+ // Open library
+ dlerror();
+ void* lib = dlopen(lib_path, RTLD_LAZY);
+ if (!lib) {
+ LILV_ERRORF("Failed to open dynmanifest library `%s' (%s)\n",
+ lib_path, dlerror());
+ sord_iter_free(binaries);
+ lilv_free(lib_path);
+ continue;
+ }
+
+ // Open dynamic manifest
+ typedef int (*OpenFunc)(LV2_Dyn_Manifest_Handle*,
+ const LV2_Feature *const *);
+ OpenFunc dmopen = (OpenFunc)lilv_dlfunc(lib, "lv2_dyn_manifest_open");
+ if (!dmopen || dmopen(&handle, &dman_features)) {
+ LILV_ERRORF("No `lv2_dyn_manifest_open' in `%s'\n", lib_path);
+ sord_iter_free(binaries);
+ dlclose(lib);
+ lilv_free(lib_path);
+ continue;
+ }
+
+ // Get subjects (the data that would be in manifest.ttl)
+ typedef int (*GetSubjectsFunc)(LV2_Dyn_Manifest_Handle, FILE*);
+ GetSubjectsFunc get_subjects_func = (GetSubjectsFunc)lilv_dlfunc(
+ lib, "lv2_dyn_manifest_get_subjects");
+ if (!get_subjects_func) {
+ LILV_ERRORF("No `lv2_dyn_manifest_get_subjects' in `%s'\n",
+ lib_path);
+ sord_iter_free(binaries);
+ dlclose(lib);
+ lilv_free(lib_path);
+ continue;
+ }
+
+ LilvDynManifest* desc = malloc(sizeof(LilvDynManifest));
+ desc->bundle = lilv_node_new_from_node(world, bundle_node);
+ desc->lib = lib;
+ desc->handle = handle;
+ desc->refs = 0;
+
+ sord_iter_free(binaries);
+
+ // Generate data file
+ FILE* fd = tmpfile();
+ get_subjects_func(handle, fd);
+ rewind(fd);
+
+ // Parse generated data file into temporary model
+ // FIXME
+ const SerdNode* base = sord_node_to_serd_node(dmanifest);
+ SerdEnv* env = serd_env_new(base);
+ SerdReader* reader = sord_new_reader(
+ world->model, env, SERD_TURTLE, sord_node_copy(dmanifest));
+ serd_reader_add_blank_prefix(reader,
+ lilv_world_blank_node_prefix(world));
+ serd_reader_read_file_handle(reader, fd,
+ (const uint8_t*)"(dyn-manifest)");
+ serd_reader_free(reader);
+ serd_env_free(env);
+
+ // Close (and automatically delete) temporary data file
+ fclose(fd);
+
+ // ?plugin a lv2:Plugin
+ SordModel* plugins = lilv_world_filter_model(world,
+ world->model,
+ NULL,
+ world->uris.rdf_a,
+ world->uris.lv2_Plugin,
+ dmanifest);
+ SordIter* p = sord_begin(plugins);
+ FOREACH_MATCH(p) {
+ const SordNode* plug = sord_iter_get_node(p, SORD_SUBJECT);
+ lilv_world_add_plugin(world, plug, manifest, desc, bundle_node);
+ }
+ if (desc->refs == 0) {
+ free(desc);
+ }
+ sord_iter_free(p);
+ sord_free(plugins);
+ lilv_free(lib_path);
+ }
+ sord_iter_free(iter);
+ sord_free(model);
+#endif // LILV_DYN_MANIFEST
+}
+
+LilvNode*
+lilv_world_get_manifest_uri(LilvWorld* world, const LilvNode* bundle_uri)
+{
+ SerdNode manifest_uri = lilv_new_uri_relative_to_base(
+ (const uint8_t*)"manifest.ttl",
+ sord_node_get_string(bundle_uri->node));
+ LilvNode* manifest = lilv_new_uri(world, (const char*)manifest_uri.buf);
+ serd_node_free(&manifest_uri);
+ return manifest;
+}
+
+static SordModel*
+load_plugin_model(LilvWorld* world,
+ const LilvNode* bundle_uri,
+ const LilvNode* plugin_uri)
+{
+ // Create model and reader for loading into it
+ SordNode* bundle_node = bundle_uri->node;
+ SordModel* model = sord_new(world->world, SORD_SPO|SORD_OPS, false);
+ SerdEnv* env = serd_env_new(sord_node_to_serd_node(bundle_node));
+ SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
+
+ // Load manifest
+ LilvNode* manifest_uri = lilv_world_get_manifest_uri(world, bundle_uri);
+ serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world));
+ serd_reader_read_file(
+ reader, (const uint8_t*)lilv_node_as_string(manifest_uri));
+
+ // Load any seeAlso files
+ SordModel* files = lilv_world_filter_model(
+ world, model, plugin_uri->node, world->uris.rdfs_seeAlso, NULL, NULL);
+
+ SordIter* f = sord_begin(files);
+ FOREACH_MATCH(f) {
+ const SordNode* file = sord_iter_get_node(f, SORD_OBJECT);
+ const uint8_t* file_str = sord_node_get_string(file);
+ if (sord_node_get_type(file) == SORD_URI) {
+ serd_reader_add_blank_prefix(
+ reader, lilv_world_blank_node_prefix(world));
+ serd_reader_read_file(reader, file_str);
+ }
+ }
+
+ sord_iter_free(f);
+ sord_free(files);
+ serd_reader_free(reader);
+ serd_env_free(env);
+ lilv_node_free(manifest_uri);
+
+ return model;
+}
+
+static LilvVersion
+get_version(LilvWorld* world, SordModel* model, const LilvNode* subject)
+{
+ const SordNode* minor_node = sord_get(
+ model, subject->node, world->uris.lv2_minorVersion, NULL, NULL);
+ const SordNode* micro_node = sord_get(
+ model, subject->node, world->uris.lv2_microVersion, NULL, NULL);
+
+
+ LilvVersion version = { 0, 0 };
+ if (minor_node && micro_node) {
+ version.minor = atoi((const char*)sord_node_get_string(minor_node));
+ version.micro = atoi((const char*)sord_node_get_string(micro_node));
+ }
+
+ return version;
+}
+
+LILV_API void
+lilv_world_load_bundle(LilvWorld* world, const LilvNode* bundle_uri)
+{
+ if (!lilv_node_is_uri(bundle_uri)) {
+ LILV_ERRORF("Bundle URI `%s' is not a URI\n",
+ sord_node_get_string(bundle_uri->node));
+ return;
+ }
+
+ SordNode* bundle_node = bundle_uri->node;
+ LilvNode* manifest = lilv_world_get_manifest_uri(world, bundle_uri);
+
+ // Read manifest into model with graph = bundle_node
+ SerdStatus st = lilv_world_load_graph(world, bundle_node, manifest);
+ if (st > SERD_FAILURE) {
+ LILV_ERRORF("Error reading %s\n", lilv_node_as_string(manifest));
+ lilv_node_free(manifest);
+ return;
+ }
+
+ // ?plugin a lv2:Plugin
+ SordIter* plug_results = sord_search(world->model,
+ NULL,
+ world->uris.rdf_a,
+ world->uris.lv2_Plugin,
+ bundle_node);
+
+ // Find any loaded plugins that will be replaced with a newer version
+ LilvNodes* unload_uris = lilv_nodes_new();
+ FOREACH_MATCH(plug_results) {
+ const SordNode* plug = sord_iter_get_node(plug_results, SORD_SUBJECT);
+
+ LilvNode* plugin_uri = lilv_node_new_from_node(world, plug);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(world->plugins, plugin_uri);
+ const LilvNode* last_bundle = plugin ? lilv_plugin_get_bundle_uri(plugin) : NULL;
+ if (!plugin || sord_node_equals(bundle_node, last_bundle->node)) {
+ // No previously loaded version, or it's from the same bundle
+ lilv_node_free(plugin_uri);
+ continue;
+ }
+
+ // Compare versions
+ SordModel* this_model = load_plugin_model(world, bundle_uri, plugin_uri);
+ LilvVersion this_version = get_version(world, this_model, plugin_uri);
+ SordModel* last_model = load_plugin_model(world, last_bundle, plugin_uri);
+ LilvVersion last_version = get_version(world, last_model, plugin_uri);
+ sord_free(this_model);
+ sord_free(last_model);
+ const int cmp = lilv_version_cmp(&this_version, &last_version);
+ if (cmp > 0) {
+ zix_tree_insert((ZixTree*)unload_uris,
+ lilv_node_duplicate(plugin_uri),
+ NULL);
+ LILV_WARNF("Replacing version %d.%d of <%s> from <%s>\n",
+ last_version.minor, last_version.micro,
+ sord_node_get_string(plug),
+ sord_node_get_string(last_bundle->node));
+ LILV_NOTEF("New version %d.%d found in <%s>\n",
+ this_version.minor, this_version.micro,
+ sord_node_get_string(bundle_node));
+ } else if (cmp < 0) {
+ LILV_WARNF("Ignoring bundle <%s>\n",
+ sord_node_get_string(bundle_node));
+ LILV_NOTEF("Newer version of <%s> loaded from <%s>\n",
+ sord_node_get_string(plug),
+ sord_node_get_string(last_bundle->node));
+ lilv_node_free(plugin_uri);
+ sord_iter_free(plug_results);
+ lilv_world_drop_graph(world, bundle_node);
+ lilv_node_free(manifest);
+ lilv_nodes_free(unload_uris);
+ return;
+ }
+ lilv_node_free(plugin_uri);
+ }
+
+ sord_iter_free(plug_results);
+
+ // Unload any old conflicting plugins
+ LilvNodes* unload_bundles = lilv_nodes_new();
+ LILV_FOREACH(nodes, i, unload_uris) {
+ const LilvNode* uri = lilv_nodes_get(unload_uris, i);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(world->plugins, uri);
+ const LilvNode* bundle = lilv_plugin_get_bundle_uri(plugin);
+
+ // Unload plugin and record bundle for later unloading
+ lilv_world_unload_resource(world, uri);
+ zix_tree_insert((ZixTree*)unload_bundles,
+ lilv_node_duplicate(bundle),
+ NULL);
+
+ }
+ lilv_nodes_free(unload_uris);
+
+ // Now unload the associated bundles
+ // This must be done last since several plugins could be in the same bundle
+ LILV_FOREACH(nodes, i, unload_bundles) {
+ lilv_world_unload_bundle(world, lilv_nodes_get(unload_bundles, i));
+ }
+ lilv_nodes_free(unload_bundles);
+
+ // Re-search for plugin results now that old plugins are gone
+ plug_results = sord_search(world->model,
+ NULL,
+ world->uris.rdf_a,
+ world->uris.lv2_Plugin,
+ bundle_node);
+
+ FOREACH_MATCH(plug_results) {
+ const SordNode* plug = sord_iter_get_node(plug_results, SORD_SUBJECT);
+ lilv_world_add_plugin(world, plug, manifest, NULL, bundle_node);
+ }
+ sord_iter_free(plug_results);
+
+ lilv_world_load_dyn_manifest(world, bundle_node, manifest);
+
+ // ?spec a lv2:Specification
+ // ?spec a owl:Ontology
+ const SordNode* spec_preds[] = { world->uris.lv2_Specification,
+ world->uris.owl_Ontology,
+ NULL };
+ for (const SordNode** p = spec_preds; *p; ++p) {
+ SordIter* i = sord_search(
+ world->model, NULL, world->uris.rdf_a, *p, bundle_node);
+ FOREACH_MATCH(i) {
+ const SordNode* spec = sord_iter_get_node(i, SORD_SUBJECT);
+ lilv_world_add_spec(world, spec, bundle_node);
+ }
+ sord_iter_free(i);
+ }
+
+ lilv_node_free(manifest);
+}
+
+static int
+lilv_world_drop_graph(LilvWorld* world, const SordNode* graph)
+{
+ SordIter* i = sord_search(world->model, NULL, NULL, NULL, graph);
+ while (!sord_iter_end(i)) {
+ const SerdStatus st = sord_erase(world->model, i);
+ if (st) {
+ LILV_ERRORF("Error removing statement from <%s> (%s)\n",
+ sord_node_get_string(graph), serd_strerror(st));
+ return st;
+ }
+ }
+ sord_iter_free(i);
+
+ return 0;
+}
+
+/** Remove loaded_files entry so file will be reloaded if requested. */
+static int
+lilv_world_unload_file(LilvWorld* world, const LilvNode* file)
+{
+ ZixTreeIter* iter;
+ if (!zix_tree_find((ZixTree*)world->loaded_files, file, &iter)) {
+ zix_tree_remove((ZixTree*)world->loaded_files, iter);
+ return 0;
+ }
+ return 1;
+}
+
+LILV_API int
+lilv_world_unload_bundle(LilvWorld* world, const LilvNode* bundle_uri)
+{
+ if (!bundle_uri) {
+ return 0;
+ }
+
+ // Find all loaded files that are inside the bundle
+ LilvNodes* files = lilv_nodes_new();
+ LILV_FOREACH(nodes, i, world->loaded_files) {
+ const LilvNode* file = lilv_nodes_get(world->loaded_files, i);
+ if (!strncmp(lilv_node_as_string(file),
+ lilv_node_as_string(bundle_uri),
+ strlen(lilv_node_as_string(bundle_uri)))) {
+ zix_tree_insert((ZixTree*)files,
+ lilv_node_duplicate(file),
+ NULL);
+ }
+ }
+
+ // Unload all loaded files in the bundle
+ LILV_FOREACH(nodes, i, files) {
+ const LilvNode* file = lilv_nodes_get(world->plugins, i);
+ lilv_world_unload_file(world, file);
+ }
+
+ lilv_nodes_free(files);
+
+ /* Remove any plugins in the bundle from the plugin list. Since the
+ application may still have a pointer to the LilvPlugin, it can not be
+ destroyed here. Instead, we move it to the zombie plugin list, so it
+ will not be in the list returned by lilv_world_get_all_plugins() but can
+ still be used.
+ */
+ ZixTreeIter* i = zix_tree_begin((ZixTree*)world->plugins);
+ while (i != zix_tree_end((ZixTree*)world->plugins)) {
+ LilvPlugin* p = (LilvPlugin*)zix_tree_get(i);
+ ZixTreeIter* next = zix_tree_iter_next(i);
+
+ if (lilv_node_equals(lilv_plugin_get_bundle_uri(p), bundle_uri)) {
+ zix_tree_remove((ZixTree*)world->plugins, i);
+ zix_tree_insert((ZixTree*)world->zombies, p, NULL);
+ }
+
+ i = next;
+ }
+
+ // Drop everything in bundle graph
+ return lilv_world_drop_graph(world, bundle_uri->node);
+}
+
+static void
+load_dir_entry(const char* dir, const char* name, void* data)
+{
+ LilvWorld* world = (LilvWorld*)data;
+ if (!strcmp(name, ".") || !strcmp(name, "..")) {
+ return;
+ }
+
+ char* path = lilv_strjoin(dir, "/", name, "/", NULL);
+ SerdNode suri = serd_node_new_file_uri((const uint8_t*)path, 0, 0, true);
+ LilvNode* node = lilv_new_uri(world, (const char*)suri.buf);
+
+ lilv_world_load_bundle(world, node);
+ lilv_node_free(node);
+ serd_node_free(&suri);
+ free(path);
+}
+
+/** Load all bundles in the directory at `dir_path`. */
+static void
+lilv_world_load_directory(LilvWorld* world, const char* dir_path)
+{
+ char* path = lilv_expand(dir_path);
+ if (path) {
+ lilv_dir_for_each(path, world, load_dir_entry);
+ free(path);
+ }
+}
+
+static const char*
+first_path_sep(const char* path)
+{
+ for (const char* p = path; *p != '\0'; ++p) {
+ if (*p == LILV_PATH_SEP[0]) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+/** Load all bundles found in `lv2_path`.
+ * @param lv2_path A colon-delimited list of directories. These directories
+ * should contain LV2 bundle directories (ie the search path is a list of
+ * parent directories of bundles, not a list of bundle directories).
+ */
+static void
+lilv_world_load_path(LilvWorld* world,
+ const char* lv2_path)
+{
+ while (lv2_path[0] != '\0') {
+ const char* const sep = first_path_sep(lv2_path);
+ if (sep) {
+ const size_t dir_len = sep - lv2_path;
+ char* const dir = (char*)malloc(dir_len + 1);
+ memcpy(dir, lv2_path, dir_len);
+ dir[dir_len] = '\0';
+ lilv_world_load_directory(world, dir);
+ free(dir);
+ lv2_path += dir_len + 1;
+ } else {
+ lilv_world_load_directory(world, lv2_path);
+ lv2_path = "\0";
+ }
+ }
+}
+
+void
+lilv_world_load_specifications(LilvWorld* world)
+{
+ for (LilvSpec* spec = world->specs; spec; spec = spec->next) {
+ LILV_FOREACH(nodes, f, spec->data_uris) {
+ LilvNode* file = (LilvNode*)lilv_collection_get(spec->data_uris, f);
+ lilv_world_load_graph(world, NULL, file);
+ }
+ }
+}
+
+void
+lilv_world_load_plugin_classes(LilvWorld* world)
+{
+ /* FIXME: This loads all classes, not just lv2:Plugin subclasses.
+ However, if the host gets all the classes via lilv_plugin_class_get_children
+ starting with lv2:Plugin as the root (which is e.g. how a host would build
+ a menu), they won't be seen anyway...
+ */
+
+ SordIter* classes = sord_search(world->model,
+ NULL,
+ world->uris.rdf_a,
+ world->uris.rdfs_Class,
+ NULL);
+ FOREACH_MATCH(classes) {
+ const SordNode* class_node = sord_iter_get_node(classes, SORD_SUBJECT);
+
+ SordNode* parent = sord_get(
+ world->model, class_node, world->uris.rdfs_subClassOf, NULL, NULL);
+ if (!parent || sord_node_get_type(parent) != SORD_URI) {
+ continue;
+ }
+
+ SordNode* label = sord_get(
+ world->model, class_node, world->uris.rdfs_label, NULL, NULL);
+ if (!label) {
+ sord_node_free(world->world, parent);
+ continue;
+ }
+
+ LilvPluginClass* pclass = lilv_plugin_class_new(
+ world, parent, class_node,
+ (const char*)sord_node_get_string(label));
+ if (pclass) {
+ zix_tree_insert((ZixTree*)world->plugin_classes, pclass, NULL);
+ }
+
+ sord_node_free(world->world, label);
+ sord_node_free(world->world, parent);
+ }
+ sord_iter_free(classes);
+}
+
+LILV_API void
+lilv_world_load_all(LilvWorld* world)
+{
+ const char* lv2_path = getenv("LV2_PATH");
+ if (!lv2_path) {
+ lv2_path = LILV_DEFAULT_LV2_PATH;
+ }
+
+ // Discover bundles and read all manifest files into model
+ lilv_world_load_path(world, lv2_path);
+
+ LILV_FOREACH(plugins, p, world->plugins) {
+ const LilvPlugin* plugin = (const LilvPlugin*)lilv_collection_get(
+ (ZixTree*)world->plugins, p);
+
+ // ?new dc:replaces plugin
+ if (sord_ask(world->model,
+ NULL,
+ world->uris.dc_replaces,
+ lilv_plugin_get_uri(plugin)->node,
+ NULL)) {
+ // TODO: Check if replacement is a known plugin? (expensive)
+ ((LilvPlugin*)plugin)->replaced = true;
+ }
+ }
+
+ // Query out things to cache
+ lilv_world_load_specifications(world);
+ lilv_world_load_plugin_classes(world);
+}
+
+SerdStatus
+lilv_world_load_file(LilvWorld* world, SerdReader* reader, const LilvNode* uri)
+{
+ ZixTreeIter* iter;
+ if (!zix_tree_find((ZixTree*)world->loaded_files, uri, &iter)) {
+ return SERD_FAILURE; // File has already been loaded
+ }
+
+ size_t uri_len;
+ const uint8_t* const uri_str = sord_node_get_string_counted(
+ uri->node, &uri_len);
+ if (strncmp((const char*)uri_str, "file:", 5)) {
+ return SERD_FAILURE; // Not a local file
+ } else if (strcmp((const char*)uri_str + uri_len - 4, ".ttl")) {
+ return SERD_FAILURE; // Not a Turtle file
+ }
+
+ serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world));
+ const SerdStatus st = serd_reader_read_file(reader, uri_str);
+ if (st) {
+ LILV_ERRORF("Error loading file `%s'\n", lilv_node_as_string(uri));
+ return st;
+ }
+
+ zix_tree_insert((ZixTree*)world->loaded_files,
+ lilv_node_duplicate(uri),
+ NULL);
+ return SERD_SUCCESS;
+}
+
+LILV_API int
+lilv_world_load_resource(LilvWorld* world,
+ const LilvNode* resource)
+{
+ if (!lilv_node_is_uri(resource) && !lilv_node_is_blank(resource)) {
+ LILV_ERRORF("Node `%s' is not a resource\n",
+ sord_node_get_string(resource->node));
+ return -1;
+ }
+
+ SordModel* files = lilv_world_filter_model(world,
+ world->model,
+ resource->node,
+ world->uris.rdfs_seeAlso,
+ NULL, NULL);
+
+ SordIter* f = sord_begin(files);
+ int n_read = 0;
+ FOREACH_MATCH(f) {
+ const SordNode* file = sord_iter_get_node(f, SORD_OBJECT);
+ const uint8_t* file_str = sord_node_get_string(file);
+ LilvNode* file_node = lilv_node_new_from_node(world, file);
+ if (sord_node_get_type(file) != SORD_URI) {
+ LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n", file_str);
+ } else if (!lilv_world_load_graph(world, (SordNode*)file, file_node)) {
+ ++n_read;
+ }
+ lilv_node_free(file_node);
+ }
+ sord_iter_free(f);
+
+ sord_free(files);
+ return n_read;
+}
+
+LILV_API int
+lilv_world_unload_resource(LilvWorld* world,
+ const LilvNode* resource)
+{
+ if (!lilv_node_is_uri(resource) && !lilv_node_is_blank(resource)) {
+ LILV_ERRORF("Node `%s' is not a resource\n",
+ sord_node_get_string(resource->node));
+ return -1;
+ }
+
+ SordModel* files = lilv_world_filter_model(world,
+ world->model,
+ resource->node,
+ world->uris.rdfs_seeAlso,
+ NULL, NULL);
+
+ SordIter* f = sord_begin(files);
+ int n_dropped = 0;
+ FOREACH_MATCH(f) {
+ const SordNode* file = sord_iter_get_node(f, SORD_OBJECT);
+ LilvNode* file_node = lilv_node_new_from_node(world, file);
+ if (sord_node_get_type(file) != SORD_URI) {
+ LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n",
+ sord_node_get_string(file));
+ } else if (!lilv_world_drop_graph(world, file_node->node)) {
+ lilv_world_unload_file(world, file_node);
+ ++n_dropped;
+ }
+ lilv_node_free(file_node);
+ }
+ sord_iter_free(f);
+
+ sord_free(files);
+ return n_dropped;
+}
+
+LILV_API const LilvPluginClass*
+lilv_world_get_plugin_class(const LilvWorld* world)
+{
+ return world->lv2_plugin_class;
+}
+
+LILV_API const LilvPluginClasses*
+lilv_world_get_plugin_classes(const LilvWorld* world)
+{
+ return world->plugin_classes;
+}
+
+LILV_API const LilvPlugins*
+lilv_world_get_all_plugins(const LilvWorld* world)
+{
+ return world->plugins;
+}
+
+LILV_API LilvNode*
+lilv_world_get_symbol(LilvWorld* world, const LilvNode* subject)
+{
+ // Check for explicitly given symbol
+ SordNode* snode = sord_get(
+ world->model, subject->node, world->uris.lv2_symbol, NULL, NULL);
+
+ if (snode) {
+ LilvNode* ret = lilv_node_new_from_node(world, snode);
+ sord_node_free(world->world, snode);
+ return ret;
+ }
+
+ if (!lilv_node_is_uri(subject)) {
+ return NULL;
+ }
+
+ // Find rightmost segment of URI
+ SerdURI uri;
+ serd_uri_parse((const uint8_t*)lilv_node_as_uri(subject), &uri);
+ const char* str = "_";
+ if (uri.fragment.buf) {
+ str = (const char*)uri.fragment.buf + 1;
+ } else if (uri.query.buf) {
+ str = (const char*)uri.query.buf;
+ } else if (uri.path.buf) {
+ const char* last_slash = strrchr((const char*)uri.path.buf, '/');
+ str = last_slash ? (last_slash + 1) : (const char*)uri.path.buf;
+ }
+
+ // Replace invalid characters
+ const size_t len = strlen(str);
+ char* const sym = (char*)calloc(1, len + 1);
+ for (size_t i = 0; i < len; ++i) {
+ const char c = str[i];
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c == '_') || (i > 0 && c >= '0' && c <= '9'))) {
+ sym[i] = '_';
+ } else {
+ sym[i] = str[i];
+ }
+ }
+
+ LilvNode* ret = lilv_new_string(world, sym);
+ free(sym);
+ return ret;
+}
diff --git a/src/zix/common.h b/src/zix/common.h
new file mode 100644
index 0000000..32019e9
--- /dev/null
+++ b/src/zix/common.h
@@ -0,0 +1,88 @@
+/*
+ Copyright 2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef ZIX_COMMON_H
+#define ZIX_COMMON_H
+
+/**
+ @addtogroup zix
+ @{
+*/
+
+/** @cond */
+#ifdef ZIX_SHARED
+# ifdef _WIN32
+# define ZIX_LIB_IMPORT __declspec(dllimport)
+# define ZIX_LIB_EXPORT __declspec(dllexport)
+# else
+# define ZIX_LIB_IMPORT __attribute__((visibility("default")))
+# define ZIX_LIB_EXPORT __attribute__((visibility("default")))
+# endif
+# ifdef ZIX_INTERNAL
+# define ZIX_API ZIX_LIB_EXPORT
+# else
+# define ZIX_API ZIX_LIB_IMPORT
+# endif
+# define ZIX_PRIVATE static
+#elif defined(ZIX_INLINE)
+# define ZIX_API static inline
+# define ZIX_PRIVATE static inline
+#else
+# define ZIX_API
+# define ZIX_PRIVATE static
+#endif
+/** @endcond */
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+typedef enum {
+ ZIX_STATUS_SUCCESS,
+ ZIX_STATUS_ERROR,
+ ZIX_STATUS_NO_MEM,
+ ZIX_STATUS_NOT_FOUND,
+ ZIX_STATUS_EXISTS,
+ ZIX_STATUS_BAD_ARG,
+ ZIX_STATUS_BAD_PERMS
+} ZixStatus;
+
+/**
+ Function for comparing two elements.
+*/
+typedef int (*ZixComparator)(const void* a, const void* b, void* user_data);
+
+/**
+ Function for testing equality of two elements.
+*/
+typedef bool (*ZixEqualFunc)(const void* a, const void* b);
+
+/**
+ Function to destroy an element.
+*/
+typedef void (*ZixDestroyFunc)(void* ptr);
+
+/**
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ZIX_COMMON_H */
diff --git a/src/zix/tree.c b/src/zix/tree.c
new file mode 100644
index 0000000..1646709
--- /dev/null
+++ b/src/zix/tree.c
@@ -0,0 +1,716 @@
+/*
+ Copyright 2011-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zix/common.h"
+#include "zix/tree.h"
+
+typedef struct ZixTreeNodeImpl ZixTreeNode;
+
+struct ZixTreeImpl {
+ ZixTreeNode* root;
+ ZixDestroyFunc destroy;
+ ZixComparator cmp;
+ void* cmp_data;
+ size_t size;
+ bool allow_duplicates;
+};
+
+struct ZixTreeNodeImpl {
+ void* data;
+ struct ZixTreeNodeImpl* left;
+ struct ZixTreeNodeImpl* right;
+ struct ZixTreeNodeImpl* parent;
+ int_fast8_t balance;
+};
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+// Uncomment these for debugging features
+// #define ZIX_TREE_DUMP 1
+// #define ZIX_TREE_VERIFY 1
+// #define ZIX_TREE_HYPER_VERIFY 1
+
+#if defined(ZIX_TREE_VERIFY) || defined(ZIX_TREE_HYPER_VERIFY)
+# include "tree_debug.h"
+# define ASSERT_BALANCE(n) assert(verify_balance(n))
+#else
+# define ASSERT_BALANCE(n)
+#endif
+
+#ifdef ZIX_TREE_DUMP
+# include "tree_debug.h"
+# define DUMP(t) zix_tree_print(t->root, 0)
+# define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
+#else
+# define DUMP(t)
+# define DEBUG_PRINTF(fmt, ...)
+#endif
+
+ZIX_API ZixTree*
+zix_tree_new(bool allow_duplicates,
+ ZixComparator cmp,
+ void* cmp_data,
+ ZixDestroyFunc destroy)
+{
+ ZixTree* t = (ZixTree*)malloc(sizeof(ZixTree));
+ t->root = NULL;
+ t->destroy = destroy;
+ t->cmp = cmp;
+ t->cmp_data = cmp_data;
+ t->size = 0;
+ t->allow_duplicates = allow_duplicates;
+ return t;
+}
+
+ZIX_PRIVATE void
+zix_tree_free_rec(ZixTree* t, ZixTreeNode* n)
+{
+ if (n) {
+ zix_tree_free_rec(t, n->left);
+ zix_tree_free_rec(t, n->right);
+ if (t->destroy) {
+ t->destroy(n->data);
+ }
+ free(n);
+ }
+}
+
+ZIX_API void
+zix_tree_free(ZixTree* t)
+{
+ if (t) {
+ zix_tree_free_rec(t, t->root);
+ free(t);
+ }
+}
+
+ZIX_API size_t
+zix_tree_size(const ZixTree* t)
+{
+ return t->size;
+}
+
+ZIX_PRIVATE void
+rotate(ZixTreeNode* p, ZixTreeNode* q)
+{
+ assert(q->parent == p);
+ assert(p->left == q || p->right == q);
+
+ q->parent = p->parent;
+ if (q->parent) {
+ if (q->parent->left == p) {
+ q->parent->left = q;
+ } else {
+ q->parent->right = q;
+ }
+ }
+
+ if (p->right == q) {
+ // Rotate left
+ p->right = q->left;
+ q->left = p;
+ if (p->right) {
+ p->right->parent = p;
+ }
+ } else {
+ // Rotate right
+ assert(p->left == q);
+ p->left = q->right;
+ q->right = p;
+ if (p->left) {
+ p->left->parent = p;
+ }
+ }
+
+ p->parent = q;
+}
+
+/**
+ * Rotate left about `p`.
+ *
+ * p q
+ * / \ / \
+ * A q => p C
+ * / \ / \
+ * B C A B
+ */
+ZIX_PRIVATE ZixTreeNode*
+rotate_left(ZixTreeNode* p, int* height_change)
+{
+ ZixTreeNode* const q = p->right;
+ *height_change = (q->balance == 0) ? 0 : -1;
+
+ DEBUG_PRINTF("LL %ld\n", (intptr_t)p->data);
+
+ assert(p->balance == 2);
+ assert(q->balance == 0 || q->balance == 1);
+
+ rotate(p, q);
+
+ // p->balance -= 1 + MAX(0, q->balance);
+ // q->balance -= 1 - MIN(0, p->balance);
+ --q->balance;
+ p->balance = -(q->balance);
+
+ ASSERT_BALANCE(p);
+ ASSERT_BALANCE(q);
+ return q;
+}
+
+/**
+ * Rotate right about `p`.
+ *
+ * p q
+ * / \ / \
+ * q C => A p
+ * / \ / \
+ * A B B C
+ *
+ */
+ZIX_PRIVATE ZixTreeNode*
+rotate_right(ZixTreeNode* p, int* height_change)
+{
+ ZixTreeNode* const q = p->left;
+ *height_change = (q->balance == 0) ? 0 : -1;
+
+ DEBUG_PRINTF("RR %ld\n", (intptr_t)p->data);
+
+ assert(p->balance == -2);
+ assert(q->balance == 0 || q->balance == -1);
+
+ rotate(p, q);
+
+ // p->balance += 1 - MIN(0, q->balance);
+ // q->balance += 1 + MAX(0, p->balance);
+ ++q->balance;
+ p->balance = -(q->balance);
+
+ ASSERT_BALANCE(p);
+ ASSERT_BALANCE(q);
+ return q;
+}
+
+/**
+ * Rotate left about `p->left` then right about `p`.
+ *
+ * p r
+ * / \ / \
+ * q D => q p
+ * / \ / \ / \
+ * A r A B C D
+ * / \
+ * B C
+ *
+ */
+ZIX_PRIVATE ZixTreeNode*
+rotate_left_right(ZixTreeNode* p, int* height_change)
+{
+ ZixTreeNode* const q = p->left;
+ ZixTreeNode* const r = q->right;
+
+ assert(p->balance == -2);
+ assert(q->balance == 1);
+ assert(r->balance == -1 || r->balance == 0 || r->balance == 1);
+
+ DEBUG_PRINTF("LR %ld P: %2d Q: %2d R: %2d\n",
+ (intptr_t)p->data, p->balance, q->balance, r->balance);
+
+ rotate(q, r);
+ rotate(p, r);
+
+ q->balance -= 1 + MAX(0, r->balance);
+ p->balance += 1 - MIN(MIN(0, r->balance) - 1, r->balance + q->balance);
+ // r->balance += MAX(0, p->balance) + MIN(0, q->balance);
+
+ // p->balance = (p->left && p->right) ? -MIN(r->balance, 0) : 0;
+ // q->balance = - MAX(r->balance, 0);
+ r->balance = 0;
+
+ *height_change = -1;
+
+ ASSERT_BALANCE(p);
+ ASSERT_BALANCE(q);
+ ASSERT_BALANCE(r);
+ return r;
+}
+
+/**
+ * Rotate right about `p->right` then right about `p`.
+ *
+ * p r
+ * / \ / \
+ * A q => p q
+ * / \ / \ / \
+ * r D A B C D
+ * / \
+ * B C
+ *
+ */
+ZIX_PRIVATE ZixTreeNode*
+rotate_right_left(ZixTreeNode* p, int* height_change)
+{
+ ZixTreeNode* const q = p->right;
+ ZixTreeNode* const r = q->left;
+
+ assert(p->balance == 2);
+ assert(q->balance == -1);
+ assert(r->balance == -1 || r->balance == 0 || r->balance == 1);
+
+ DEBUG_PRINTF("RL %ld P: %2d Q: %2d R: %2d\n",
+ (intptr_t)p->data, p->balance, q->balance, r->balance);
+
+ rotate(q, r);
+ rotate(p, r);
+
+ q->balance += 1 - MIN(0, r->balance);
+ p->balance -= 1 + MAX(MAX(0, r->balance) + 1, r->balance + q->balance);
+ // r->balance += MAX(0, q->balance) + MIN(0, p->balance);
+
+ // p->balance = (p->left && p->right) ? -MAX(r->balance, 0) : 0;
+ // q->balance = - MIN(r->balance, 0);
+ r->balance = 0;
+ // assert(r->balance == 0);
+
+ *height_change = -1;
+
+ ASSERT_BALANCE(p);
+ ASSERT_BALANCE(q);
+ ASSERT_BALANCE(r);
+ return r;
+}
+
+ZIX_PRIVATE ZixTreeNode*
+zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change)
+{
+#ifdef ZIX_TREE_HYPER_VERIFY
+ const size_t old_height = height(node);
+#endif
+ DEBUG_PRINTF("REBALANCE %ld (%d)\n", (intptr_t)node->data, node->balance);
+ *height_change = 0;
+ const bool is_root = !node->parent;
+ assert((is_root && t->root == node) || (!is_root && t->root != node));
+ ZixTreeNode* replacement = node;
+ if (node->balance == -2) {
+ assert(node->left);
+ if (node->left->balance == 1) {
+ replacement = rotate_left_right(node, height_change);
+ } else {
+ replacement = rotate_right(node, height_change);
+ }
+ } else if (node->balance == 2) {
+ assert(node->right);
+ if (node->right->balance == -1) {
+ replacement = rotate_right_left(node, height_change);
+ } else {
+ replacement = rotate_left(node, height_change);
+ }
+ }
+ if (is_root) {
+ assert(!replacement->parent);
+ t->root = replacement;
+ }
+ DUMP(t);
+#ifdef ZIX_TREE_HYPER_VERIFY
+ assert(old_height + *height_change == height(replacement));
+#endif
+ return replacement;
+}
+
+ZIX_API ZixStatus
+zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti)
+{
+ DEBUG_PRINTF("**** INSERT %ld\n", (intptr_t)e);
+ int cmp = 0;
+ ZixTreeNode* n = t->root;
+ ZixTreeNode* p = NULL;
+
+ // Find the parent p of e
+ while (n) {
+ p = n;
+ cmp = t->cmp(e, n->data, t->cmp_data);
+ if (cmp < 0) {
+ n = n->left;
+ } else if (cmp > 0) {
+ n = n->right;
+ } else if (t->allow_duplicates) {
+ n = n->right;
+ } else {
+ if (ti) {
+ *ti = n;
+ }
+ DEBUG_PRINTF("%ld EXISTS!\n", (intptr_t)e);
+ return ZIX_STATUS_EXISTS;
+ }
+ }
+
+ // Allocate a new node n
+ if (!(n = (ZixTreeNode*)malloc(sizeof(ZixTreeNode)))) {
+ return ZIX_STATUS_NO_MEM;
+ }
+ memset(n, '\0', sizeof(ZixTreeNode));
+ n->data = e;
+ n->balance = 0;
+ if (ti) {
+ *ti = n;
+ }
+
+ bool p_height_increased = false;
+
+ // Make p the parent of n
+ n->parent = p;
+ if (!p) {
+ t->root = n;
+ } else {
+ if (cmp < 0) {
+ assert(!p->left);
+ assert(p->balance == 0 || p->balance == 1);
+ p->left = n;
+ --p->balance;
+ p_height_increased = !p->right;
+ } else {
+ assert(!p->right);
+ assert(p->balance == 0 || p->balance == -1);
+ p->right = n;
+ ++p->balance;
+ p_height_increased = !p->left;
+ }
+ }
+
+ DUMP(t);
+
+ // Rebalance if necessary (at most 1 rotation)
+ assert(!p || p->balance == -1 || p->balance == 0 || p->balance == 1);
+ if (p && p_height_increased) {
+ int height_change = 0;
+ for (ZixTreeNode* i = p; i && i->parent; i = i->parent) {
+ if (i == i->parent->left) {
+ if (--i->parent->balance == -2) {
+ zix_tree_rebalance(t, i->parent, &height_change);
+ break;
+ }
+ } else {
+ assert(i == i->parent->right);
+ if (++i->parent->balance == 2) {
+ zix_tree_rebalance(t, i->parent, &height_change);
+ break;
+ }
+ }
+
+ if (i->parent->balance == 0) {
+ break;
+ }
+ }
+ }
+
+ DUMP(t);
+
+ ++t->size;
+
+#ifdef ZIX_TREE_VERIFY
+ if (!verify(t, t->root)) {
+ return ZIX_STATUS_ERROR;
+ }
+#endif
+
+ return ZIX_STATUS_SUCCESS;
+}
+
+ZIX_API ZixStatus
+zix_tree_remove(ZixTree* t, ZixTreeIter* ti)
+{
+ ZixTreeNode* const n = ti;
+ ZixTreeNode** pp = NULL; // parent pointer
+ ZixTreeNode* to_balance = n->parent; // lowest node to balance
+ int8_t d_balance = 0; // delta(balance) for n->parent
+
+ DEBUG_PRINTF("*** REMOVE %ld\n", (intptr_t)n->data);
+
+ if ((n == t->root) && !n->left && !n->right) {
+ t->root = NULL;
+ if (t->destroy) {
+ t->destroy(n->data);
+ }
+ free(n);
+ --t->size;
+ assert(t->size == 0);
+ return ZIX_STATUS_SUCCESS;
+ }
+
+ // Set pp to the parent pointer to n, if applicable
+ if (n->parent) {
+ assert(n->parent->left == n || n->parent->right == n);
+ if (n->parent->left == n) { // n is left child
+ pp = &n->parent->left;
+ d_balance = 1;
+ } else { // n is right child
+ assert(n->parent->right == n);
+ pp = &n->parent->right;
+ d_balance = -1;
+ }
+ }
+
+ assert(!pp || *pp == n);
+
+ int height_change = 0;
+ if (!n->left && !n->right) {
+ // n is a leaf, just remove it
+ if (pp) {
+ *pp = NULL;
+ to_balance = n->parent;
+ height_change = (!n->parent->left && !n->parent->right) ? -1 : 0;
+ }
+ } else if (!n->left) {
+ // Replace n with right (only) child
+ if (pp) {
+ *pp = n->right;
+ to_balance = n->parent;
+ } else {
+ t->root = n->right;
+ }
+ n->right->parent = n->parent;
+ height_change = -1;
+ } else if (!n->right) {
+ // Replace n with left (only) child
+ if (pp) {
+ *pp = n->left;
+ to_balance = n->parent;
+ } else {
+ t->root = n->left;
+ }
+ n->left->parent = n->parent;
+ height_change = -1;
+ } else {
+ // Replace n with in-order successor (leftmost child of right subtree)
+ ZixTreeNode* replace = n->right;
+ while (replace->left) {
+ assert(replace->left->parent == replace);
+ replace = replace->left;
+ }
+
+ // Remove replace from parent (replace_p)
+ if (replace->parent->left == replace) {
+ height_change = replace->parent->right ? 0 : -1;
+ d_balance = 1;
+ to_balance = replace->parent;
+ replace->parent->left = replace->right;
+ } else {
+ assert(replace->parent == n);
+ height_change = replace->parent->left ? 0 : -1;
+ d_balance = -1;
+ to_balance = replace->parent;
+ replace->parent->right = replace->right;
+ }
+
+ if (to_balance == n) {
+ to_balance = replace;
+ }
+
+ if (replace->right) {
+ replace->right->parent = replace->parent;
+ }
+
+ replace->balance = n->balance;
+
+ // Swap node to delete with replace
+ if (pp) {
+ *pp = replace;
+ } else {
+ assert(t->root == n);
+ t->root = replace;
+ }
+ replace->parent = n->parent;
+ replace->left = n->left;
+ n->left->parent = replace;
+ replace->right = n->right;
+ if (n->right) {
+ n->right->parent = replace;
+ }
+
+ assert(!replace->parent
+ || replace->parent->left == replace
+ || replace->parent->right == replace);
+ }
+
+ // Rebalance starting at to_balance upwards.
+ for (ZixTreeNode* i = to_balance; i; i = i->parent) {
+ i->balance += d_balance;
+ if (d_balance == 0 || i->balance == -1 || i->balance == 1) {
+ break;
+ }
+
+ assert(i != n);
+ i = zix_tree_rebalance(t, i, &height_change);
+ if (i->balance == 0) {
+ height_change = -1;
+ }
+
+ if (i->parent) {
+ if (i == i->parent->left) {
+ d_balance = height_change * -1;
+ } else {
+ assert(i == i->parent->right);
+ d_balance = height_change;
+ }
+ }
+ }
+
+ DUMP(t);
+
+ if (t->destroy) {
+ t->destroy(n->data);
+ }
+ free(n);
+
+ --t->size;
+
+#ifdef ZIX_TREE_VERIFY
+ if (!verify(t, t->root)) {
+ return ZIX_STATUS_ERROR;
+ }
+#endif
+
+ return ZIX_STATUS_SUCCESS;
+}
+
+ZIX_API ZixStatus
+zix_tree_find(const ZixTree* t, const void* e, ZixTreeIter** ti)
+{
+ ZixTreeNode* n = t->root;
+ while (n) {
+ const int cmp = t->cmp(e, n->data, t->cmp_data);
+ if (cmp == 0) {
+ break;
+ } else if (cmp < 0) {
+ n = n->left;
+ } else {
+ n = n->right;
+ }
+ }
+
+ *ti = n;
+ return (n) ? ZIX_STATUS_SUCCESS : ZIX_STATUS_NOT_FOUND;
+}
+
+ZIX_API void*
+zix_tree_get(const ZixTreeIter* ti)
+{
+ return ti ? ti->data : NULL;
+}
+
+ZIX_API ZixTreeIter*
+zix_tree_begin(ZixTree* t)
+{
+ if (!t->root) {
+ return NULL;
+ }
+
+ ZixTreeNode* n = t->root;
+ while (n->left) {
+ n = n->left;
+ }
+ return n;
+}
+
+ZIX_API ZixTreeIter*
+zix_tree_end(ZixTree* t)
+{
+ return NULL;
+}
+
+ZIX_API ZixTreeIter*
+zix_tree_rbegin(ZixTree* t)
+{
+ if (!t->root) {
+ return NULL;
+ }
+
+ ZixTreeNode* n = t->root;
+ while (n->right) {
+ n = n->right;
+ }
+ return n;
+}
+
+ZIX_API ZixTreeIter*
+zix_tree_rend(ZixTree* t)
+{
+ return NULL;
+}
+
+ZIX_API bool
+zix_tree_iter_is_end(const ZixTreeIter* i)
+{
+ return !i;
+}
+
+ZIX_API bool
+zix_tree_iter_is_rend(const ZixTreeIter* i)
+{
+ return !i;
+}
+
+ZIX_API ZixTreeIter*
+zix_tree_iter_next(ZixTreeIter* i)
+{
+ if (!i) {
+ return NULL;
+ }
+
+ if (i->right) {
+ i = i->right;
+ while (i->left) {
+ i = i->left;
+ }
+ } else {
+ while (i->parent && i->parent->right == i) { // i is a right child
+ i = i->parent;
+ }
+
+ i = i->parent;
+ }
+
+ return i;
+}
+
+ZIX_API ZixTreeIter*
+zix_tree_iter_prev(ZixTreeIter* i)
+{
+ if (!i) {
+ return NULL;
+ }
+
+ if (i->left) {
+ i = i->left;
+ while (i->right) {
+ i = i->right;
+ }
+ } else {
+ while (i->parent && i->parent->left == i) { // i is a left child
+ i = i->parent;
+ }
+
+ i = i->parent;
+ }
+
+ return i;
+}
diff --git a/src/zix/tree.h b/src/zix/tree.h
new file mode 100644
index 0000000..e531a9b
--- /dev/null
+++ b/src/zix/tree.h
@@ -0,0 +1,148 @@
+/*
+ Copyright 2011-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef ZIX_TREE_H
+#define ZIX_TREE_H
+
+#include <stddef.h>
+
+#include "zix/common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ @addtogroup zix
+ @{
+ @name Tree
+ @{
+*/
+
+/**
+ A balanced binary search tree.
+*/
+typedef struct ZixTreeImpl ZixTree;
+
+/**
+ An iterator over a ZixTree.
+*/
+typedef struct ZixTreeNodeImpl ZixTreeIter;
+
+/**
+ Create a new (empty) tree.
+*/
+ZIX_API ZixTree*
+zix_tree_new(bool allow_duplicates,
+ ZixComparator cmp,
+ void* cmp_data,
+ ZixDestroyFunc destroy);
+
+/**
+ Free `t`.
+*/
+ZIX_API void
+zix_tree_free(ZixTree* t);
+
+/**
+ Return the number of elements in `t`.
+*/
+ZIX_API size_t
+zix_tree_size(const ZixTree* t);
+
+/**
+ Insert the element `e` into `t` and point `ti` at the new element.
+*/
+ZIX_API ZixStatus
+zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti);
+
+/**
+ Remove the item pointed at by `ti` from `t`.
+*/
+ZIX_API ZixStatus
+zix_tree_remove(ZixTree* t, ZixTreeIter* ti);
+
+/**
+ Set `ti` to an element equal to `e` in `t`.
+ If no such item exists, `ti` is set to NULL.
+*/
+ZIX_API ZixStatus
+zix_tree_find(const ZixTree* t, const void* e, ZixTreeIter** ti);
+
+/**
+ Return the data associated with the given tree item.
+*/
+ZIX_API void*
+zix_tree_get(const ZixTreeIter* ti);
+
+/**
+ Return an iterator to the first (smallest) element in `t`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_begin(ZixTree* t);
+
+/**
+ Return an iterator the the element one past the last element in `t`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_end(ZixTree* t);
+
+/**
+ Return true iff `i` is an iterator to the end of its tree.
+*/
+ZIX_API bool
+zix_tree_iter_is_end(const ZixTreeIter* i);
+
+/**
+ Return an iterator to the last (largest) element in `t`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_rbegin(ZixTree* t);
+
+/**
+ Return an iterator the the element one before the first element in `t`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_rend(ZixTree* t);
+
+/**
+ Return true iff `i` is an iterator to the reverse end of its tree.
+*/
+ZIX_API bool
+zix_tree_iter_is_rend(const ZixTreeIter* i);
+
+/**
+ Return an iterator that points to the element one past `i`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_iter_next(ZixTreeIter* i);
+
+/**
+ Return an iterator that points to the element one before `i`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_iter_prev(ZixTreeIter* i);
+
+/**
+ @}
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ZIX_TREE_H */
diff --git a/test/bad_syntax.lv2/bad_syntax.c b/test/bad_syntax.lv2/bad_syntax.c
new file mode 100644
index 0000000..52620bd
--- /dev/null
+++ b/test/bad_syntax.lv2/bad_syntax.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Bad syntax in plugin data file
+ Copyright 2011-2016 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/bad-syntax"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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;
+ default:
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* path,
+ const LV2_Feature* const* features)
+{
+ Test* test = (Test*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/bad_syntax.lv2/bad_syntax.ttl.in b/test/bad_syntax.lv2/bad_syntax.ttl.in
new file mode 100644
index 0000000..a96d1fc
--- /dev/null
+++ b/test/bad_syntax.lv2/bad_syntax.ttl.in
@@ -0,0 +1,22 @@
+# Lilv Test Plugin - Bad syntax in plugin data file
+# Copyright 2011-2016 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/bad-syntax>
+ a plugin with a clearly broken data file \ No newline at end of file
diff --git a/test/bad_syntax.lv2/manifest.ttl.in b/test/bad_syntax.lv2/manifest.ttl.in
new file mode 100644
index 0000000..6350d8d
--- /dev/null
+++ b/test/bad_syntax.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/bad-syntax>
+ a lv2:Plugin ;
+ lv2:binary <bad_syntax@SHLIB_EXT@> ;
+ rdfs:seeAlso <bad_syntax.ttl> .
diff --git a/test/bad_syntax.lv2/test_bad_syntax.c b/test/bad_syntax.lv2/test_bad_syntax.c
new file mode 100644
index 0000000..d20b8b7
--- /dev/null
+++ b/test/bad_syntax.lv2/test_bad_syntax.c
@@ -0,0 +1,45 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/bad-syntax"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+
+ TEST_ASSERT(!lilv_plugin_get_name(plugin));
+ TEST_ASSERT(!lilv_plugin_instantiate(plugin, 48000, NULL));
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/failed_instantiation.lv2/failed_instantiation.c b/test/failed_instantiation.lv2/failed_instantiation.c
new file mode 100644
index 0000000..aa90532
--- /dev/null
+++ b/test/failed_instantiation.lv2/failed_instantiation.c
@@ -0,0 +1,70 @@
+/*
+ Lilv Test Plugin - Failed instantiation
+ Copyright 2011-2016 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/failed-instantiation"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void* data)
+{}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* path,
+ const LV2_Feature* const* features)
+{
+ return NULL;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/failed_instantiation.lv2/failed_instantiation.ttl.in b/test/failed_instantiation.lv2/failed_instantiation.ttl.in
new file mode 100644
index 0000000..8c7f678
--- /dev/null
+++ b/test/failed_instantiation.lv2/failed_instantiation.ttl.in
@@ -0,0 +1,40 @@
+# Lilv Test Plugin - Failed instantiation
+# Copyright 2011-2016 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/failed-instantiation>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ doap:name "New version" ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:minorVersion 2 ;
+ lv2:microVersion 1 ;
+ 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"
+ ] .
diff --git a/test/failed_instantiation.lv2/manifest.ttl.in b/test/failed_instantiation.lv2/manifest.ttl.in
new file mode 100644
index 0000000..d55a573
--- /dev/null
+++ b/test/failed_instantiation.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/failed-instantiation>
+ a lv2:Plugin ;
+ lv2:binary <failed_instantiation@SHLIB_EXT@> ;
+ rdfs:seeAlso <failed_instantiation.ttl> .
diff --git a/test/failed_instantiation.lv2/test_failed_instantiation.c b/test/failed_instantiation.lv2/test_failed_instantiation.c
new file mode 100644
index 0000000..6efa0ae
--- /dev/null
+++ b/test/failed_instantiation.lv2/test_failed_instantiation.c
@@ -0,0 +1,45 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/failed-instantiation"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ TEST_ASSERT(!lilv_plugin_instantiate(plugin, 48000, NULL));
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/failed_lib_descriptor.lv2/failed_lib_descriptor.c b/test/failed_lib_descriptor.lv2/failed_lib_descriptor.c
new file mode 100644
index 0000000..6a1fd90
--- /dev/null
+++ b/test/failed_lib_descriptor.lv2/failed_lib_descriptor.c
@@ -0,0 +1,30 @@
+/*
+ Lilv Test Plugin - Failed lib descriptor
+ Copyright 2011-2015 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/failed-lib-descriptor"
+
+LV2_SYMBOL_EXPORT
+const LV2_Lib_Descriptor*
+lv2_lib_descriptor(const char* bundle_path,
+ const LV2_Feature*const* features)
+{
+ return NULL;
+}
diff --git a/test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in b/test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in
new file mode 100644
index 0000000..f41545f
--- /dev/null
+++ b/test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in
@@ -0,0 +1,38 @@
+# Lilv Test Plugin - Failed lib descriptor
+# Copyright 2011-2015 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/failed-lib-descriptor>
+ a lv2:Plugin ;
+ doap:name "Missing descriptor test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ 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"
+ ] .
diff --git a/test/failed_lib_descriptor.lv2/manifest.ttl.in b/test/failed_lib_descriptor.lv2/manifest.ttl.in
new file mode 100644
index 0000000..4724f0f
--- /dev/null
+++ b/test/failed_lib_descriptor.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/failed-lib-descriptor>
+ a lv2:Plugin ;
+ lv2:binary <failed_lib_descriptor@SHLIB_EXT@> ;
+ rdfs:seeAlso <failed_lib_descriptor.ttl> .
diff --git a/test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c b/test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c
new file mode 100644
index 0000000..b82eb0a
--- /dev/null
+++ b/test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c
@@ -0,0 +1,46 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/failed-lib-descriptor"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(!instance);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/lib_descriptor.lv2/lib_descriptor.c b/test/lib_descriptor.lv2/lib_descriptor.c
new file mode 100644
index 0000000..48dc775
--- /dev/null
+++ b/test/lib_descriptor.lv2/lib_descriptor.c
@@ -0,0 +1,112 @@
+/*
+ Lilv Test Plugin - Missing descriptor
+ Copyright 2011-2015 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/lib-descriptor"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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;
+ default:
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* path,
+ const LV2_Feature* const* features)
+{
+ Test* test = (Test*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+static const LV2_Descriptor*
+get_plugin(LV2_Lib_Handle handle, uint32_t index)
+{
+ switch (index) {
+ case 0:
+ return &descriptor;
+ default:
+ return NULL;
+ }
+}
+
+static const LV2_Lib_Descriptor lib = {
+ NULL,
+ sizeof(LV2_Lib_Descriptor),
+ NULL,
+ get_plugin };
+
+LV2_SYMBOL_EXPORT
+const LV2_Lib_Descriptor*
+lv2_lib_descriptor(const char* bundle_path,
+ const LV2_Feature*const* features)
+{
+ return &lib;
+}
diff --git a/test/lib_descriptor.lv2/lib_descriptor.ttl.in b/test/lib_descriptor.lv2/lib_descriptor.ttl.in
new file mode 100644
index 0000000..19c8c4a
--- /dev/null
+++ b/test/lib_descriptor.lv2/lib_descriptor.ttl.in
@@ -0,0 +1,41 @@
+# Lilv Test Plugin - Missing descriptor
+# Copyright 2011-2015 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+<http://example.org/lib-descriptor>
+ a lv2:Plugin ;
+ doap:name "Missing descriptor test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ <http://example.org/blob> "aGVsbG8sIHdvcmxk"^^xsd:base64Binary ;
+ <http://example.org/junk> "opaque"^^<http://example.org/binary> ;
+ 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"
+ ] .
diff --git a/test/lib_descriptor.lv2/manifest.ttl.in b/test/lib_descriptor.lv2/manifest.ttl.in
new file mode 100644
index 0000000..5f4eced
--- /dev/null
+++ b/test/lib_descriptor.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/lib-descriptor>
+ a lv2:Plugin ;
+ lv2:binary <lib_descriptor@SHLIB_EXT@> ;
+ rdfs:seeAlso <lib_descriptor.ttl> .
diff --git a/test/lib_descriptor.lv2/test_lib_descriptor.c b/test/lib_descriptor.lv2/test_lib_descriptor.c
new file mode 100644
index 0000000..ff14763
--- /dev/null
+++ b/test/lib_descriptor.lv2/test_lib_descriptor.c
@@ -0,0 +1,59 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/lib-descriptor"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(instance);
+ lilv_instance_free(instance);
+
+ LilvNode* eg_blob = lilv_new_uri(world, "http://example.org/blob");
+ LilvNode* blob = lilv_world_get(world, plugin_uri, eg_blob, NULL);
+ TEST_ASSERT(lilv_node_is_literal(blob));
+ lilv_node_free(blob);
+ lilv_node_free(eg_blob);
+
+ LilvNode* eg_junk = lilv_new_uri(world, "http://example.org/junk");
+ LilvNode* junk = lilv_world_get(world, plugin_uri, eg_junk, NULL);
+ TEST_ASSERT(lilv_node_is_literal(junk));
+ lilv_node_free(junk);
+ lilv_node_free(eg_junk);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/lilv_cxx_test.cpp b/test/lilv_cxx_test.cpp
new file mode 100644
index 0000000..25f30bf
--- /dev/null
+++ b/test/lilv_cxx_test.cpp
@@ -0,0 +1,23 @@
+/*
+ Copyright 2017 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "lilv/lilvmm.hpp"
+
+int
+main()
+{
+ return 0;
+}
diff --git a/test/lilv_test.c b/test/lilv_test.c
new file mode 100644
index 0000000..b55f3e9
--- /dev/null
+++ b/test/lilv_test.c
@@ -0,0 +1,2299 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+ Copyright 2008 Krzysztof Foltman
+
+ 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 /* for setenv */
+#define _XOPEN_SOURCE 600 /* for mkstemp */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+# include <direct.h>
+# include <io.h>
+# define mkdir(path, flags) _mkdir(path)
+# define setenv(n, v, r) SetEnvironmentVariable((n), (v))
+# define unsetenv(n) SetEnvironmentVariable((n), NULL)
+# define mkstemp(pat) _mktemp(pat)
+#else
+# include <dirent.h>
+# include <unistd.h>
+#endif
+
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+
+#define TEST_PATH_MAX 1024
+
+#if defined(__APPLE__)
+# define SHLIB_EXT ".dylib"
+#elif defined(_WIN32)
+# define SHLIB_EXT ".dll"
+#else
+# define SHLIB_EXT ".so"
+#endif
+
+static char bundle_dir_name[TEST_PATH_MAX + sizeof("/.lv2/lilv-test.lv2")];
+static char bundle_dir_uri[sizeof(bundle_dir_name) + sizeof("file:///")];
+static char manifest_name[sizeof(bundle_dir_name) + sizeof("/manifest.ttl")];
+static char content_name[sizeof(bundle_dir_name) + sizeof("plugin.ttl")];
+
+static LilvWorld* world;
+
+int test_count = 0;
+int error_count = 0;
+
+static void
+delete_bundle(void)
+{
+ unlink(content_name);
+ unlink(manifest_name);
+ remove(bundle_dir_name);
+}
+
+static void
+init_tests(void)
+{
+ snprintf(bundle_dir_name, sizeof(bundle_dir_name), "%s/.lv2/lilv-test.lv2",
+ getenv("HOME"));
+ lilv_mkdir_p(bundle_dir_name);
+
+ snprintf(bundle_dir_uri, sizeof(bundle_dir_uri), "file://%s/",
+ bundle_dir_name);
+ snprintf(manifest_name, sizeof(manifest_name), "%s/manifest.ttl",
+ bundle_dir_name);
+ snprintf(content_name, sizeof(content_name), "%s/plugin.ttl",
+ bundle_dir_name);
+
+ delete_bundle();
+}
+
+static void
+fatal_error(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ exit(1);
+}
+
+static void
+write_file(const char* name, const char* content)
+{
+ FILE* f = fopen(name, "w");
+ size_t len = strlen(content);
+ if (fwrite(content, 1, len, f) != len) {
+ fatal_error("Failed to write to file '%s' (%s)\n",
+ name, strerror(errno));
+ }
+ fclose(f);
+}
+
+static int
+init_world(void)
+{
+ world = lilv_world_new();
+ return world != NULL;
+}
+
+static int
+load_all_bundles(void)
+{
+ if (!init_world()) {
+ return 0;
+ }
+ lilv_world_load_all(world);
+ return 1;
+}
+
+static void
+create_bundle(const char* manifest, const char* content)
+{
+ if (mkdir(bundle_dir_name, 0700) && errno != EEXIST) {
+ fatal_error("Failed to create directory '%s' (%s)\n",
+ bundle_dir_name, strerror(errno));
+ }
+ write_file(manifest_name, manifest);
+ write_file(content_name, content);
+}
+
+static int
+start_bundle(const char* manifest, const char* content)
+{
+ create_bundle(manifest, content);
+ return load_all_bundles();
+}
+
+static void
+unload_bundle(void)
+{
+ if (world) {
+ lilv_world_free(world);
+ }
+ world = NULL;
+}
+
+static void
+cleanup(void)
+{
+ delete_bundle();
+}
+
+/*****************************************************************************/
+
+#define TEST_CASE(name) { #name, test_##name }
+#define TEST_ASSERT(check) do {\
+ test_count++;\
+ if (!(check)) {\
+ error_count++;\
+ fprintf(stderr, "lilv_test.c:%d: error: test `%s' failed\n", __LINE__, #check);\
+ abort();\
+ }\
+} while (0)
+
+typedef int (*TestFunc)(void);
+
+struct TestCase {
+ const char* title;
+ TestFunc func;
+};
+
+#define PREFIX_ATOM "@prefix atom: <http://lv2plug.in/ns/ext/atom#> . \n"
+#define PREFIX_LINE "@prefix : <http://example.org/> .\n"
+#define PREFIX_LV2 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
+#define PREFIX_LV2EV "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> . \n"
+#define PREFIX_LV2UI "@prefix lv2ui: <http://lv2plug.in/ns/extensions/ui#> .\n"
+#define PREFIX_RDF "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
+#define PREFIX_RDFS "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
+#define PREFIX_FOAF "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"
+#define PREFIX_DOAP "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"
+#define PREFIX_PSET "@prefix pset: <http://lv2plug.in/ns/ext/presets#> .\n"
+
+#define MANIFEST_PREFIXES PREFIX_LINE PREFIX_LV2 PREFIX_RDFS
+#define BUNDLE_PREFIXES PREFIX_ATOM PREFIX_LINE PREFIX_LV2 PREFIX_RDF PREFIX_RDFS PREFIX_FOAF PREFIX_DOAP PREFIX_PSET
+#define PLUGIN_NAME(name) "doap:name \"" name "\""
+#define LICENSE_GPL "doap:license <http://usefulinc.com/doap/licenses/gpl>"
+
+static const char* uris_plugin = "http://example.org/plug";
+static LilvNode* plugin_uri_value;
+static LilvNode* plugin2_uri_value;
+
+/*****************************************************************************/
+
+static void
+init_uris(void)
+{
+ plugin_uri_value = lilv_new_uri(world, uris_plugin);
+ plugin2_uri_value = lilv_new_uri(world, "http://example.org/foobar");
+ TEST_ASSERT(plugin_uri_value);
+ TEST_ASSERT(plugin2_uri_value);
+}
+
+static void
+cleanup_uris(void)
+{
+ lilv_node_free(plugin2_uri_value);
+ lilv_node_free(plugin_uri_value);
+ plugin2_uri_value = NULL;
+ plugin_uri_value = NULL;
+}
+
+/*****************************************************************************/
+
+static int
+test_value(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"Foo\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ LilvNode* uval = lilv_new_uri(world, "http://example.org");
+ LilvNode* sval = lilv_new_string(world, "Foo");
+ LilvNode* ival = lilv_new_int(world, 42);
+ LilvNode* fval = lilv_new_float(world, 1.6180);
+
+ TEST_ASSERT(lilv_node_is_uri(uval));
+ TEST_ASSERT(lilv_node_is_string(sval));
+ TEST_ASSERT(lilv_node_is_int(ival));
+ TEST_ASSERT(lilv_node_is_float(fval));
+
+ TEST_ASSERT(!lilv_node_is_literal(NULL));
+ TEST_ASSERT(!lilv_node_is_literal(uval));
+ TEST_ASSERT(lilv_node_is_literal(sval));
+ TEST_ASSERT(lilv_node_is_literal(ival));
+ TEST_ASSERT(lilv_node_is_literal(fval));
+ TEST_ASSERT(!lilv_node_get_path(fval, NULL));
+
+ TEST_ASSERT(!strcmp(lilv_node_as_uri(uval), "http://example.org"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(sval), "Foo"));
+ TEST_ASSERT(lilv_node_as_int(ival) == 42);
+ TEST_ASSERT(fabs(lilv_node_as_float(fval) - 1.6180) < FLT_EPSILON);
+ TEST_ASSERT(isnan(lilv_node_as_float(sval)));
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+ TEST_ASSERT(!strcmp(lilv_uri_to_path("file:///foo"), "/foo"));
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+# pragma GCC diagnostic pop
+#endif
+
+ LilvNode* loc_abs = lilv_new_file_uri(world, NULL, "/foo/bar");
+ LilvNode* loc_rel = lilv_new_file_uri(world, NULL, "foo");
+ LilvNode* host_abs = lilv_new_file_uri(world, "host", "/foo/bar");
+ LilvNode* host_rel = lilv_new_file_uri(world, "host", "foo");
+
+ TEST_ASSERT(!strcmp(lilv_node_as_uri(loc_abs), "file:///foo/bar"));
+ TEST_ASSERT(!strncmp(lilv_node_as_uri(loc_rel), "file:///", 8));
+ TEST_ASSERT(!strcmp(lilv_node_as_uri(host_abs), "file://host/foo/bar"));
+ TEST_ASSERT(!strncmp(lilv_node_as_uri(host_rel), "file://host/", 12));
+
+ lilv_node_free(host_rel);
+ lilv_node_free(host_abs);
+ lilv_node_free(loc_rel);
+ lilv_node_free(loc_abs);
+
+ char* tok = lilv_node_get_turtle_token(uval);
+ TEST_ASSERT(!strcmp(tok, "<http://example.org>"));
+ lilv_free(tok);
+ tok = lilv_node_get_turtle_token(sval);
+ TEST_ASSERT(!strcmp(tok, "Foo"));
+ lilv_free(tok);
+ tok = lilv_node_get_turtle_token(ival);
+ TEST_ASSERT(!strcmp(tok, "42"));
+ lilv_free(tok);
+ tok = lilv_node_get_turtle_token(fval);
+ TEST_ASSERT(!strncmp(tok, "1.6180", 6));
+ lilv_free(tok);
+
+ LilvNode* uval_e = lilv_new_uri(world, "http://example.org");
+ LilvNode* sval_e = lilv_new_string(world, "Foo");
+ LilvNode* ival_e = lilv_new_int(world, 42);
+ LilvNode* fval_e = lilv_new_float(world, 1.6180);
+ LilvNode* uval_ne = lilv_new_uri(world, "http://no-example.org");
+ LilvNode* sval_ne = lilv_new_string(world, "Bar");
+ LilvNode* ival_ne = lilv_new_int(world, 24);
+ LilvNode* fval_ne = lilv_new_float(world, 3.14159);
+
+ TEST_ASSERT(lilv_node_equals(uval, uval_e));
+ TEST_ASSERT(lilv_node_equals(sval, sval_e));
+ TEST_ASSERT(lilv_node_equals(ival, ival_e));
+ TEST_ASSERT(lilv_node_equals(fval, fval_e));
+
+ TEST_ASSERT(!lilv_node_equals(uval, uval_ne));
+ TEST_ASSERT(!lilv_node_equals(sval, sval_ne));
+ TEST_ASSERT(!lilv_node_equals(ival, ival_ne));
+ TEST_ASSERT(!lilv_node_equals(fval, fval_ne));
+
+ TEST_ASSERT(!lilv_node_equals(uval, sval));
+ TEST_ASSERT(!lilv_node_equals(sval, ival));
+ TEST_ASSERT(!lilv_node_equals(ival, fval));
+
+ LilvNode* uval_dup = lilv_node_duplicate(uval);
+ TEST_ASSERT(lilv_node_equals(uval, uval_dup));
+
+ LilvNode* ifval = lilv_new_float(world, 42.0);
+ TEST_ASSERT(!lilv_node_equals(ival, ifval));
+ lilv_node_free(ifval);
+
+ LilvNode* nil = NULL;
+ TEST_ASSERT(!lilv_node_equals(uval, nil));
+ TEST_ASSERT(!lilv_node_equals(nil, uval));
+ TEST_ASSERT(lilv_node_equals(nil, nil));
+
+ LilvNode* nil2 = lilv_node_duplicate(nil);
+ TEST_ASSERT(lilv_node_equals(nil, nil2));
+
+ lilv_node_free(uval);
+ lilv_node_free(sval);
+ lilv_node_free(ival);
+ lilv_node_free(fval);
+ lilv_node_free(uval_e);
+ lilv_node_free(sval_e);
+ lilv_node_free(ival_e);
+ lilv_node_free(fval_e);
+ lilv_node_free(uval_ne);
+ lilv_node_free(sval_ne);
+ lilv_node_free(ival_ne);
+ lilv_node_free(fval_ne);
+ lilv_node_free(uval_dup);
+ lilv_node_free(nil2);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_util(void)
+{
+ TEST_ASSERT(!lilv_realpath(NULL));
+
+ char a_path[16];
+ char b_path[16];
+ strncpy(a_path, "copy_a_XXXXXX", sizeof(a_path));
+ strncpy(b_path, "copy_b_XXXXXX", sizeof(b_path));
+ mkstemp(a_path);
+ mkstemp(b_path);
+
+ FILE* fa = fopen(a_path, "w");
+ FILE* fb = fopen(b_path, "w");
+ fprintf(fa, "AA\n");
+ fprintf(fb, "AB\n");
+ fclose(fa);
+ fclose(fb);
+
+ TEST_ASSERT(lilv_copy_file("does/not/exist", "copy"));
+ TEST_ASSERT(lilv_copy_file(a_path, "not/a/dir/copy"));
+ TEST_ASSERT(!lilv_copy_file(a_path, "copy_c"));
+ TEST_ASSERT(!lilv_file_equals(a_path, b_path));
+ TEST_ASSERT(lilv_file_equals(a_path, a_path));
+ TEST_ASSERT(lilv_file_equals(a_path, "copy_c"));
+ TEST_ASSERT(!lilv_file_equals("does/not/exist", b_path));
+ TEST_ASSERT(!lilv_file_equals(a_path, "does/not/exist"));
+ TEST_ASSERT(!lilv_file_equals("does/not/exist", "/does/not/either"));
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int discovery_plugin_found = 0;
+
+static void
+discovery_verify_plugin(const LilvPlugin* plugin)
+{
+ const LilvNode* value = lilv_plugin_get_uri(plugin);
+ if (lilv_node_equals(value, plugin_uri_value)) {
+ const LilvNode* lib_uri = NULL;
+ TEST_ASSERT(!lilv_node_equals(value, plugin2_uri_value));
+ discovery_plugin_found = 1;
+ lib_uri = lilv_plugin_get_library_uri(plugin);
+ TEST_ASSERT(lib_uri);
+ TEST_ASSERT(lilv_node_is_uri(lib_uri));
+ TEST_ASSERT(lilv_node_as_uri(lib_uri));
+ TEST_ASSERT(strstr(lilv_node_as_uri(lib_uri), "foo" SHLIB_EXT));
+ TEST_ASSERT(lilv_plugin_verify(plugin));
+ }
+}
+
+static int
+test_discovery(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ;"
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ a lv2:ControlPort ; a lv2:InputPort ;"
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; ] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ TEST_ASSERT(lilv_plugins_size(plugins) > 0);
+
+ const LilvPlugin* explug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(explug != NULL);
+ const LilvPlugin* explug2 = lilv_plugins_get_by_uri(plugins, plugin2_uri_value);
+ TEST_ASSERT(explug2 == NULL);
+
+ if (explug) {
+ LilvNode* name = lilv_plugin_get_name(explug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "Test plugin"));
+ lilv_node_free(name);
+ }
+
+ discovery_plugin_found = 0;
+ LILV_FOREACH(plugins, i, plugins)
+ discovery_verify_plugin(lilv_plugins_get(plugins, i));
+
+ TEST_ASSERT(discovery_plugin_found);
+ plugins = NULL;
+
+ cleanup_uris();
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_lv2_path(void)
+{
+#ifndef _WIN32
+ char* orig_lv2_path = lilv_strdup(getenv("LV2_PATH"));
+
+ setenv("LV2_PATH", "~/.lv2:/usr/local/lib/lv2:/usr/lib/lv2", 1);
+
+ world = lilv_world_new();
+ lilv_world_load_all(world);
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const size_t n_plugins = lilv_plugins_size(plugins);
+
+ lilv_world_free(world);
+
+ setenv("LV2_PATH", "$HOME/.lv2:/usr/local/lib/lv2:/usr/lib/lv2", 1);
+ world = lilv_world_new();
+ lilv_world_load_all(world);
+ plugins = lilv_world_get_all_plugins(world);
+ TEST_ASSERT(lilv_plugins_size(plugins) == n_plugins);
+ lilv_world_free(world);
+ world = NULL;
+
+ if (orig_lv2_path) {
+ setenv("LV2_PATH", orig_lv2_path, 1);
+ } else {
+ unsetenv("LV2_PATH");
+ }
+ free(orig_lv2_path);
+#endif
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_verify(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ a lv2:ControlPort ; a lv2:InputPort ;"
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ] .")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* explug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(explug);
+ TEST_ASSERT(lilv_plugin_verify(explug));
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_no_verify(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin . ")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* explug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(explug);
+ TEST_ASSERT(!lilv_plugin_verify(explug));
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_classes(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"Foo\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPluginClass* plugin = lilv_world_get_plugin_class(world);
+ const LilvPluginClasses* classes = lilv_world_get_plugin_classes(world);
+ LilvPluginClasses* children = lilv_plugin_class_get_children(plugin);
+
+ TEST_ASSERT(lilv_plugin_class_get_parent_uri(plugin) == NULL);
+ TEST_ASSERT(lilv_plugin_classes_size(classes) > lilv_plugin_classes_size(children));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_plugin_class_get_label(plugin)), "Plugin"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_plugin_class_get_uri(plugin)),
+ "http://lv2plug.in/ns/lv2core#Plugin"));
+
+ LILV_FOREACH(plugin_classes, i, children) {
+ TEST_ASSERT(lilv_node_equals(
+ lilv_plugin_class_get_parent_uri(lilv_plugin_classes_get(children, i)),
+ lilv_plugin_class_get_uri(plugin)));
+ }
+
+ LilvNode* some_uri = lilv_new_uri(world, "http://example.org/whatever");
+ TEST_ASSERT(lilv_plugin_classes_get_by_uri(classes, some_uri) == NULL);
+ lilv_node_free(some_uri);
+
+ lilv_plugin_classes_free(children);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_plugin(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:optionalFeature lv2:hardRTCapable ; "
+ "lv2:requiredFeature <http://lv2plug.in/ns/ext/event> ; "
+ "lv2:extensionData <http://example.org/extdata> ;"
+ ":foo 1.6180 ; "
+ ":bar true ; "
+ ":baz false ; "
+ ":blank [ a <http://example.org/blank> ] ; "
+ "doap:maintainer [ foaf:name \"David Robillard\" ; "
+ " foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ const LilvPluginClass* klass = lilv_plugin_get_class(plug);
+ const LilvNode* klass_uri = lilv_plugin_class_get_uri(klass);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(klass_uri),
+ "http://lv2plug.in/ns/lv2core#CompressorPlugin"));
+
+ LilvNode* rdf_type = lilv_new_uri(
+ world, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
+ TEST_ASSERT(lilv_world_ask(world,
+ lilv_plugin_get_uri(plug),
+ rdf_type,
+ klass_uri));
+ lilv_node_free(rdf_type);
+
+ TEST_ASSERT(!lilv_plugin_is_replaced(plug));
+ TEST_ASSERT(!lilv_plugin_get_related(plug, NULL));
+
+ const LilvNode* plug_bundle_uri = lilv_plugin_get_bundle_uri(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(plug_bundle_uri), bundle_dir_uri));
+
+ const LilvNodes* data_uris = lilv_plugin_get_data_uris(plug);
+ TEST_ASSERT(lilv_nodes_size(data_uris) == 2);
+
+ LilvNode* project = lilv_plugin_get_project(plug);
+ TEST_ASSERT(!project);
+
+ char* manifest_uri = (char*)malloc(TEST_PATH_MAX);
+ char* data_uri = (char*)malloc(TEST_PATH_MAX);
+ snprintf(manifest_uri, TEST_PATH_MAX, "%s%s",
+ lilv_node_as_string(plug_bundle_uri), "manifest.ttl");
+ snprintf(data_uri, TEST_PATH_MAX, "%s%s",
+ lilv_node_as_string(plug_bundle_uri), "plugin.ttl");
+
+ LilvNode* manifest_uri_val = lilv_new_uri(world, manifest_uri);
+ TEST_ASSERT(lilv_nodes_contains(data_uris, manifest_uri_val));
+ lilv_node_free(manifest_uri_val);
+
+ LilvNode* data_uri_val = lilv_new_uri(world, data_uri);
+ TEST_ASSERT(lilv_nodes_contains(data_uris, data_uri_val));
+ lilv_node_free(data_uri_val);
+
+ LilvNode* unknown_uri_val = lilv_new_uri(world, "http://example.org/unknown");
+ TEST_ASSERT(!lilv_nodes_contains(data_uris, unknown_uri_val));
+ lilv_node_free(unknown_uri_val);
+
+ free(manifest_uri);
+ free(data_uri);
+
+ float mins[3];
+ float maxs[3];
+ float defs[3];
+ lilv_plugin_get_port_ranges_float(plug, mins, maxs, defs);
+ TEST_ASSERT(mins[0] == -1.0f);
+ TEST_ASSERT(maxs[0] == 1.0f);
+ TEST_ASSERT(defs[0] == 0.5f);
+
+ LilvNode* audio_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#AudioPort");
+ LilvNode* control_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#ControlPort");
+ LilvNode* in_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#InputPort");
+ LilvNode* out_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#OutputPort");
+
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, NULL) == 3);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class, NULL) == 0);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, in_class, NULL) == 2);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, out_class, NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, in_class, NULL) == 2);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, out_class, NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class, in_class, NULL) == 0);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class, out_class, NULL) == 0);
+
+ TEST_ASSERT(lilv_plugin_has_latency(plug));
+ TEST_ASSERT(lilv_plugin_get_latency_port_index(plug) == 2);
+
+ LilvNode* lv2_latency = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#latency");
+ const LilvPort* latency_port = lilv_plugin_get_port_by_designation(
+ plug, out_class, lv2_latency);
+ lilv_node_free(lv2_latency);
+
+ TEST_ASSERT(latency_port);
+ TEST_ASSERT(lilv_port_get_index(plug, latency_port) == 2);
+ TEST_ASSERT(lilv_node_is_blank(lilv_port_get_node(plug, latency_port)));
+
+ LilvNode* rt_feature = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#hardRTCapable");
+ LilvNode* event_feature = lilv_new_uri(world,
+ "http://lv2plug.in/ns/ext/event");
+ LilvNode* pretend_feature = lilv_new_uri(world,
+ "http://example.org/solvesWorldHunger");
+
+ TEST_ASSERT(lilv_plugin_has_feature(plug, rt_feature));
+ TEST_ASSERT(lilv_plugin_has_feature(plug, event_feature));
+ TEST_ASSERT(!lilv_plugin_has_feature(plug, pretend_feature));
+
+ lilv_node_free(rt_feature);
+ lilv_node_free(event_feature);
+ lilv_node_free(pretend_feature);
+
+ LilvNodes* supported = lilv_plugin_get_supported_features(plug);
+ LilvNodes* required = lilv_plugin_get_required_features(plug);
+ LilvNodes* optional = lilv_plugin_get_optional_features(plug);
+ TEST_ASSERT(lilv_nodes_size(supported) == 2);
+ TEST_ASSERT(lilv_nodes_size(required) == 1);
+ TEST_ASSERT(lilv_nodes_size(optional) == 1);
+ lilv_nodes_free(supported);
+ lilv_nodes_free(required);
+ lilv_nodes_free(optional);
+
+ LilvNode* foo_p = lilv_new_uri(world, "http://example.org/foo");
+ LilvNodes* foos = lilv_plugin_get_value(plug, foo_p);
+ TEST_ASSERT(lilv_nodes_size(foos) == 1);
+ TEST_ASSERT(fabs(lilv_node_as_float(lilv_nodes_get_first(foos)) - 1.6180) < FLT_EPSILON);
+ lilv_node_free(foo_p);
+ lilv_nodes_free(foos);
+
+ LilvNode* bar_p = lilv_new_uri(world, "http://example.org/bar");
+ LilvNodes* bars = lilv_plugin_get_value(plug, bar_p);
+ TEST_ASSERT(lilv_nodes_size(bars) == 1);
+ TEST_ASSERT(lilv_node_as_bool(lilv_nodes_get_first(bars)) == true);
+ lilv_node_free(bar_p);
+ lilv_nodes_free(bars);
+
+ LilvNode* baz_p = lilv_new_uri(world, "http://example.org/baz");
+ LilvNodes* bazs = lilv_plugin_get_value(plug, baz_p);
+ TEST_ASSERT(lilv_nodes_size(bazs) == 1);
+ TEST_ASSERT(lilv_node_as_bool(lilv_nodes_get_first(bazs)) == false);
+ lilv_node_free(baz_p);
+ lilv_nodes_free(bazs);
+
+ LilvNode* blank_p = lilv_new_uri(world, "http://example.org/blank");
+ LilvNodes* blanks = lilv_plugin_get_value(plug, blank_p);
+ TEST_ASSERT(lilv_nodes_size(blanks) == 1);
+ LilvNode* blank = lilv_nodes_get_first(blanks);
+ TEST_ASSERT(lilv_node_is_blank(blank));
+ const char* blank_str = lilv_node_as_blank(blank);
+ char* blank_tok = lilv_node_get_turtle_token(blank);
+ TEST_ASSERT(!strncmp(blank_tok, "_:", 2));
+ TEST_ASSERT(!strcmp(blank_tok + 2, blank_str));
+ lilv_free(blank_tok);
+ lilv_node_free(blank_p);
+ lilv_nodes_free(blanks);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_name), "David Robillard"));
+ lilv_node_free(author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_email), "mailto:d@drobilla.net"));
+ lilv_node_free(author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_homepage), "http://drobilla.net"));
+ lilv_node_free(author_homepage);
+
+ LilvNode* thing_uri = lilv_new_uri(world, "http://example.org/thing");
+ LilvNode* name_p = lilv_new_uri(world, "http://usefulinc.com/ns/doap#name");
+ LilvNodes* thing_names = lilv_world_find_nodes(world, thing_uri, name_p, NULL);
+ TEST_ASSERT(lilv_nodes_size(thing_names) == 1);
+ LilvNode* thing_name = lilv_nodes_get_first(thing_names);
+ TEST_ASSERT(thing_name);
+ TEST_ASSERT(lilv_node_is_string(thing_name));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(thing_name), "Something else"));
+ LilvNode* thing_name2 = lilv_world_get(world, thing_uri, name_p, NULL);
+ TEST_ASSERT(lilv_node_equals(thing_name, thing_name2));
+
+ LilvUIs* uis = lilv_plugin_get_uis(plug);
+ TEST_ASSERT(lilv_uis_size(uis) == 0);
+ lilv_uis_free(uis);
+
+ LilvNode* extdata = lilv_new_uri(world, "http://example.org/extdata");
+ LilvNode* noextdata = lilv_new_uri(world, "http://example.org/noextdata");
+ LilvNodes* extdatas = lilv_plugin_get_extension_data(plug);
+ TEST_ASSERT(lilv_plugin_has_extension_data(plug, extdata));
+ TEST_ASSERT(!lilv_plugin_has_extension_data(plug, noextdata));
+ TEST_ASSERT(lilv_nodes_size(extdatas) == 1);
+ TEST_ASSERT(lilv_node_equals(lilv_nodes_get_first(extdatas), extdata));
+ lilv_node_free(noextdata);
+ lilv_node_free(extdata);
+ lilv_nodes_free(extdatas);
+
+ lilv_nodes_free(thing_names);
+ lilv_node_free(thing_uri);
+ lilv_node_free(thing_name2);
+ lilv_node_free(name_p);
+ lilv_node_free(control_class);
+ lilv_node_free(audio_class);
+ lilv_node_free(in_class);
+ lilv_node_free(out_class);
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_project(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:maintainer [ "
+ " foaf:name \"David Robillard\" ; "
+ " foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_name), "David Robillard"));
+ lilv_node_free(author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_email), "mailto:d@drobilla.net"));
+ lilv_node_free(author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_homepage), "http://drobilla.net"));
+ lilv_node_free(author_homepage);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_no_author(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!author_homepage);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_project_no_author(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:name \"Fake project\" ;"
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!author_homepage);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_preset(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:name \"Fake project\" ;"
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ "<http://example.org/preset> a pset:Preset ;"
+ " lv2:appliesTo :plug ;"
+ " rdfs:label \"some preset\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* pset_Preset = lilv_new_uri(world, LV2_PRESETS__Preset);
+ LilvNodes* related = lilv_plugin_get_related(plug, pset_Preset);
+
+ TEST_ASSERT(lilv_nodes_size(related) == 1);
+
+ lilv_node_free(pset_Preset);
+ lilv_nodes_free(related);
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_prototype(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":prot a lv2:PluginBase ; rdfs:seeAlso <plugin.ttl> .\n"
+ ":plug a lv2:Plugin ; lv2:binary <inst" SHLIB_EXT "> ; lv2:prototype :prot .\n",
+ BUNDLE_PREFIXES
+ ":prot a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:name \"Fake project\" ;"
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":plug doap:name \"Instance\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ // Test non-inherited property
+ LilvNode* name = lilv_plugin_get_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "Instance"));
+ lilv_node_free(name);
+
+ // Test inherited property
+ const LilvNode* binary = lilv_plugin_get_library_uri(plug);
+ TEST_ASSERT(strstr(lilv_node_as_string(binary), "inst" SHLIB_EXT));
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_port(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "doap:homepage <http://example.org/someplug> ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; "
+ " lv2:name \"store\" ; "
+ " lv2:name \"dépanneur\"@fr-ca ; lv2:name \"épicerie\"@fr-fr ; "
+ " lv2:name \"tienda\"@es ; "
+ " rdfs:comment \"comment\"@en , \"commentaires\"@fr ; "
+ " lv2:portProperty lv2:integer ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 ; "
+ " lv2:scalePoint [ rdfs:label \"Sin\"; rdf:value 3 ] ; "
+ " lv2:scalePoint [ rdfs:label \"Cos\"; rdf:value 4 ] "
+ "] , [\n"
+ " a lv2:EventPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"event_in\" ; "
+ " lv2:name \"Event Input\" ; "
+ " lv2ev:supportsEvent <http://example.org/event> ;"
+ " atom:supports <http://example.org/atomEvent> "
+ "] , [\n"
+ " a lv2:AudioPort ; a lv2:InputPort ; "
+ " lv2:index 2 ; lv2:symbol \"audio_in\" ; "
+ " lv2:name \"Audio Input\" ; "
+ "] , [\n"
+ " a lv2:AudioPort ; a lv2:OutputPort ; "
+ " lv2:index 3 ; lv2:symbol \"audio_out\" ; "
+ " lv2:name \"Audio Output\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* psym = lilv_new_string(world, "foo");
+ const LilvPort* p = lilv_plugin_get_port_by_index(plug, 0);
+ const LilvPort* p2 = lilv_plugin_get_port_by_symbol(plug, psym);
+ lilv_node_free(psym);
+ TEST_ASSERT(p != NULL);
+ TEST_ASSERT(p2 != NULL);
+ TEST_ASSERT(p == p2);
+
+ LilvNode* nopsym = lilv_new_string(world, "thisaintnoportfoo");
+ const LilvPort* p3 = lilv_plugin_get_port_by_symbol(plug, nopsym);
+ TEST_ASSERT(p3 == NULL);
+ lilv_node_free(nopsym);
+
+ // Try getting an invalid property
+ LilvNode* num = lilv_new_int(world, 1);
+ LilvNodes* nothing = lilv_port_get_value(plug, p, num);
+ TEST_ASSERT(!nothing);
+ lilv_node_free(num);
+
+ LilvNode* audio_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#AudioPort");
+ LilvNode* control_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#ControlPort");
+ LilvNode* in_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#InputPort");
+ LilvNode* out_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#OutputPort");
+
+ TEST_ASSERT(lilv_nodes_size(lilv_port_get_classes(plug, p)) == 2);
+ TEST_ASSERT(lilv_plugin_get_num_ports(plug) == 4);
+ TEST_ASSERT(lilv_port_is_a(plug, p, control_class));
+ TEST_ASSERT(lilv_port_is_a(plug, p, in_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, p, audio_class));
+
+ LilvNodes* port_properties = lilv_port_get_properties(plug, p);
+ TEST_ASSERT(lilv_nodes_size(port_properties) == 1);
+ lilv_nodes_free(port_properties);
+
+ // Untranslated name (current locale is set to "C" in main)
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_port_get_symbol(plug, p)), "foo"));
+ LilvNode* name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "store"));
+ lilv_node_free(name);
+
+ // Exact language match
+ setenv("LANG", "fr_FR", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "épicerie"));
+ lilv_node_free(name);
+
+ // Exact language match (with charset suffix)
+ setenv("LANG", "fr_CA.utf8", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "dépanneur"));
+ lilv_node_free(name);
+
+ // Partial language match (choose value translated for different country)
+ setenv("LANG", "fr_BE", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT((!strcmp(lilv_node_as_string(name), "dépanneur"))
+ ||(!strcmp(lilv_node_as_string(name), "épicerie")));
+ lilv_node_free(name);
+
+ // Partial language match (choose country-less language tagged value)
+ setenv("LANG", "es_MX", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "tienda"));
+ lilv_node_free(name);
+
+ // No language match (choose untranslated value)
+ setenv("LANG", "cn", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "store"));
+ lilv_node_free(name);
+
+ // Invalid language
+ setenv("LANG", "1!", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "store"));
+ lilv_node_free(name);
+
+ setenv("LANG", "en_CA.utf-8", 1);
+
+ // Language tagged value with no untranslated values
+ LilvNode* rdfs_comment = lilv_new_uri(world, LILV_NS_RDFS "comment");
+ LilvNodes* comments = lilv_port_get_value(plug, p, rdfs_comment);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(comments)),
+ "comment"));
+ LilvNode* comment = lilv_port_get(plug, p, rdfs_comment);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(comment), "comment"));
+ lilv_node_free(comment);
+ lilv_nodes_free(comments);
+
+ setenv("LANG", "fr", 1);
+
+ comments = lilv_port_get_value(plug, p, rdfs_comment);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(comments)),
+ "commentaires"));
+ lilv_nodes_free(comments);
+
+ setenv("LANG", "cn", 1);
+
+ comments = lilv_port_get_value(plug, p, rdfs_comment);
+ TEST_ASSERT(!comments);
+ lilv_nodes_free(comments);
+
+ lilv_node_free(rdfs_comment);
+
+ setenv("LANG", "C", 1); // Reset locale
+
+ LilvScalePoints* points = lilv_port_get_scale_points(plug, p);
+ TEST_ASSERT(lilv_scale_points_size(points) == 2);
+
+ LilvIter* sp_iter = lilv_scale_points_begin(points);
+ const LilvScalePoint* sp0 = lilv_scale_points_get(points, sp_iter);
+ TEST_ASSERT(sp0);
+ sp_iter = lilv_scale_points_next(points, sp_iter);
+ const LilvScalePoint* sp1 = lilv_scale_points_get(points, sp_iter);
+ TEST_ASSERT(sp1);
+
+ TEST_ASSERT(
+ ((!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp0)), "Sin")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp0)) == 3)
+ &&
+ (!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp1)), "Cos")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp1)) == 4))
+ ||
+ ((!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp0)), "Cos")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp0)) == 4)
+ &&
+ (!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp1)), "Sin")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp1)) == 3)));
+
+ LilvNode* homepage_p = lilv_new_uri(world, "http://usefulinc.com/ns/doap#homepage");
+ LilvNodes* homepages = lilv_plugin_get_value(plug, homepage_p);
+ TEST_ASSERT(lilv_nodes_size(homepages) == 1);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(homepages)),
+ "http://example.org/someplug"));
+
+ LilvNode *min, *max, *def;
+ lilv_port_get_range(plug, p, &def, &min, &max);
+ TEST_ASSERT(def);
+ TEST_ASSERT(min);
+ TEST_ASSERT(max);
+ TEST_ASSERT(lilv_node_as_float(def) == 0.5);
+ TEST_ASSERT(lilv_node_as_float(min) == -1.0);
+ TEST_ASSERT(lilv_node_as_float(max) == 1.0);
+
+ LilvNode* integer_prop = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#integer");
+ LilvNode* toggled_prop = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#toggled");
+
+ TEST_ASSERT(lilv_port_has_property(plug, p, integer_prop));
+ TEST_ASSERT(!lilv_port_has_property(plug, p, toggled_prop));
+
+ const LilvPort* ep = lilv_plugin_get_port_by_index(plug, 1);
+
+ LilvNode* event_type = lilv_new_uri(world, "http://example.org/event");
+ LilvNode* event_type_2 = lilv_new_uri(world, "http://example.org/otherEvent");
+ LilvNode* atom_event = lilv_new_uri(world, "http://example.org/atomEvent");
+ TEST_ASSERT(lilv_port_supports_event(plug, ep, event_type));
+ TEST_ASSERT(!lilv_port_supports_event(plug, ep, event_type_2));
+ TEST_ASSERT(lilv_port_supports_event(plug, ep, atom_event));
+
+ LilvNode* name_p = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#name");
+ LilvNodes* names = lilv_port_get_value(plug, p, name_p);
+ TEST_ASSERT(lilv_nodes_size(names) == 1);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(names)),
+ "store"));
+ lilv_nodes_free(names);
+
+ LilvNode* true_val = lilv_new_bool(world, true);
+ LilvNode* false_val = lilv_new_bool(world, false);
+
+ TEST_ASSERT(!lilv_node_equals(true_val, false_val));
+
+ lilv_world_set_option(world, LILV_OPTION_FILTER_LANG, false_val);
+ names = lilv_port_get_value(plug, p, name_p);
+ TEST_ASSERT(lilv_nodes_size(names) == 4);
+ lilv_nodes_free(names);
+ lilv_world_set_option(world, LILV_OPTION_FILTER_LANG, true_val);
+
+ lilv_node_free(false_val);
+ lilv_node_free(true_val);
+
+ names = lilv_port_get_value(plug, ep, name_p);
+ TEST_ASSERT(lilv_nodes_size(names) == 1);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(names)),
+ "Event Input"));
+
+ const LilvPort* ap_in = lilv_plugin_get_port_by_index(plug, 2);
+
+ TEST_ASSERT(lilv_port_is_a(plug, ap_in, in_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_in, out_class));
+ TEST_ASSERT(lilv_port_is_a(plug, ap_in, audio_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_in, control_class));
+
+ const LilvPort* ap_out = lilv_plugin_get_port_by_index(plug, 3);
+
+ TEST_ASSERT(lilv_port_is_a(plug, ap_out, out_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_out, in_class));
+ TEST_ASSERT(lilv_port_is_a(plug, ap_out, audio_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_out, control_class));
+
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, in_class , NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class , in_class , NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class , out_class, NULL) == 1);
+
+ lilv_nodes_free(names);
+ lilv_node_free(name_p);
+
+ lilv_node_free(integer_prop);
+ lilv_node_free(toggled_prop);
+ lilv_node_free(event_type);
+ lilv_node_free(event_type_2);
+ lilv_node_free(atom_event);
+
+ lilv_node_free(min);
+ lilv_node_free(max);
+ lilv_node_free(def);
+
+ lilv_node_free(homepage_p);
+ lilv_nodes_free(homepages);
+
+ lilv_scale_points_free(points);
+ lilv_node_free(control_class);
+ lilv_node_free(audio_class);
+ lilv_node_free(out_class);
+ lilv_node_free(in_class);
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static unsigned
+ui_supported(const char* container_type_uri,
+ const char* ui_type_uri)
+{
+ return !strcmp(container_type_uri, ui_type_uri);
+}
+
+static int
+test_ui(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2UI
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:optionalFeature lv2:hardRTCapable ; "
+ "lv2:requiredFeature <http://lv2plug.in/ns/ext/event> ; "
+ "lv2ui:ui :ui , :ui2 , :ui3 , :ui4 ; "
+ "doap:maintainer [ foaf:name \"David Robillard\" ; "
+ " foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency "
+ "] .\n"
+ ":ui a lv2ui:GtkUI ; "
+ " lv2ui:requiredFeature lv2ui:makeResident ; "
+ " lv2ui:binary <ui" SHLIB_EXT "> ; "
+ " lv2ui:optionalFeature lv2ui:ext_presets . "
+ ":ui2 a lv2ui:GtkUI ; lv2ui:binary <ui2" SHLIB_EXT "> . "
+ ":ui3 a lv2ui:GtkUI ; lv2ui:binary <ui3" SHLIB_EXT "> . "
+ ":ui4 a lv2ui:GtkUI ; lv2ui:binary <ui4" SHLIB_EXT "> . ")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvUIs* uis = lilv_plugin_get_uis(plug);
+ TEST_ASSERT(lilv_uis_size(uis) == 4);
+
+ const LilvUI* ui0 = lilv_uis_get(uis, lilv_uis_begin(uis));
+ TEST_ASSERT(ui0);
+
+ LilvNode* ui_uri = lilv_new_uri(world, "http://example.org/ui");
+ LilvNode* ui2_uri = lilv_new_uri(world, "http://example.org/ui3");
+ LilvNode* ui3_uri = lilv_new_uri(world, "http://example.org/ui4");
+ LilvNode* noui_uri = lilv_new_uri(world, "http://example.org/notaui");
+
+ const LilvUI* ui0_2 = lilv_uis_get_by_uri(uis, ui_uri);
+ TEST_ASSERT(ui0 == ui0_2);
+ TEST_ASSERT(lilv_node_equals(lilv_ui_get_uri(ui0_2), ui_uri));
+
+ const LilvUI* ui2 = lilv_uis_get_by_uri(uis, ui2_uri);
+ TEST_ASSERT(ui2 != ui0);
+
+ const LilvUI* ui3 = lilv_uis_get_by_uri(uis, ui3_uri);
+ TEST_ASSERT(ui3 != ui0);
+
+ const LilvUI* noui = lilv_uis_get_by_uri(uis, noui_uri);
+ TEST_ASSERT(noui == NULL);
+
+ const LilvNodes* classes = lilv_ui_get_classes(ui0);
+ TEST_ASSERT(lilv_nodes_size(classes) == 1);
+
+ LilvNode* ui_class_uri = lilv_new_uri(world,
+ "http://lv2plug.in/ns/extensions/ui#GtkUI");
+
+ LilvNode* unknown_ui_class_uri = lilv_new_uri(world,
+ "http://example.org/mysteryUI");
+
+ TEST_ASSERT(lilv_node_equals(lilv_nodes_get_first(classes), ui_class_uri));
+ TEST_ASSERT(lilv_ui_is_a(ui0, ui_class_uri));
+
+ const LilvNode* ui_type = NULL;
+ TEST_ASSERT(lilv_ui_is_supported(ui0, ui_supported, ui_class_uri, &ui_type));
+ TEST_ASSERT(!lilv_ui_is_supported(ui0, ui_supported, unknown_ui_class_uri, &ui_type));
+ TEST_ASSERT(lilv_node_equals(ui_type, ui_class_uri));
+
+ const LilvNode* plug_bundle_uri = lilv_plugin_get_bundle_uri(plug);
+ const LilvNode* ui_bundle_uri = lilv_ui_get_bundle_uri(ui0);
+ TEST_ASSERT(lilv_node_equals(plug_bundle_uri, ui_bundle_uri));
+
+ char* ui_binary_uri_str = (char*)malloc(TEST_PATH_MAX);
+ snprintf(ui_binary_uri_str, TEST_PATH_MAX, "%s%s",
+ lilv_node_as_string(plug_bundle_uri), "ui" SHLIB_EXT);
+
+ const LilvNode* ui_binary_uri = lilv_ui_get_binary_uri(ui0);
+
+ LilvNode* expected_uri = lilv_new_uri(world, ui_binary_uri_str);
+ TEST_ASSERT(lilv_node_equals(expected_uri, ui_binary_uri));
+
+ free(ui_binary_uri_str);
+ lilv_node_free(unknown_ui_class_uri);
+ lilv_node_free(ui_class_uri);
+ lilv_node_free(ui_uri);
+ lilv_node_free(ui2_uri);
+ lilv_node_free(ui3_uri);
+ lilv_node_free(noui_uri);
+ lilv_node_free(expected_uri);
+ lilv_uis_free(uis);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+uint32_t atom_Float = 0;
+float in = 1.0;
+float out = 42.0;
+float control = 1234.0;
+
+static const void*
+get_port_value(const char* port_symbol,
+ void* user_data,
+ uint32_t* size,
+ uint32_t* type)
+{
+ if (!strcmp(port_symbol, "input")) {
+ *size = sizeof(float);
+ *type = atom_Float;
+ return &in;
+ } else if (!strcmp(port_symbol, "output")) {
+ *size = sizeof(float);
+ *type = atom_Float;
+ return &out;
+ } else if (!strcmp(port_symbol, "control")) {
+ *size = sizeof(float);
+ *type = atom_Float;
+ return &control;
+ } else {
+ fprintf(stderr, "error: get_port_value for nonexistent port `%s'\n",
+ port_symbol);
+ *size = *type = 0;
+ return NULL;
+ }
+}
+
+static void
+set_port_value(const char* port_symbol,
+ void* user_data,
+ const void* value,
+ uint32_t size,
+ uint32_t type)
+{
+ if (!strcmp(port_symbol, "input")) {
+ in = *(const float*)value;
+ } else if (!strcmp(port_symbol, "output")) {
+ out = *(const float*)value;
+ } else if (!strcmp(port_symbol, "control")) {
+ control = *(const float*)value;
+ } else {
+ fprintf(stderr, "error: set_port_value for nonexistent port `%s'\n",
+ port_symbol);
+ }
+}
+
+char** uris = NULL;
+size_t n_uris = 0;
+
+static LV2_URID
+map_uri(LV2_URID_Map_Handle handle,
+ const char* uri)
+{
+ for (size_t i = 0; i < n_uris; ++i) {
+ if (!strcmp(uris[i], uri)) {
+ return i + 1;
+ }
+ }
+
+ assert(serd_uri_string_has_scheme((const uint8_t*)uri));
+ uris = (char**)realloc(uris, ++n_uris * sizeof(char*));
+ uris[n_uris - 1] = lilv_strdup(uri);
+ return n_uris;
+}
+
+static const char*
+unmap_uri(LV2_URID_Map_Handle handle,
+ LV2_URID urid)
+{
+ if (urid > 0 && urid <= n_uris) {
+ return uris[urid - 1];
+ }
+ return NULL;
+}
+
+static char* temp_dir = NULL;
+
+static char*
+lilv_make_path(LV2_State_Make_Path_Handle handle,
+ const char* path)
+{
+ return lilv_path_join(temp_dir, path);
+}
+
+static int
+test_state(void)
+{
+ init_world();
+
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(LILV_TEST_BUNDLE);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ LilvNode* plugin_uri = lilv_new_uri(world,
+ "http://example.org/lilv-test-plugin");
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LV2_URID_Map map = { NULL, map_uri };
+ LV2_Feature map_feature = { LV2_URID_MAP_URI, &map };
+ LV2_URID_Unmap unmap = { NULL, unmap_uri };
+ LV2_Feature unmap_feature = { LV2_URID_UNMAP_URI, &unmap };
+ const LV2_Feature* features[] = { &map_feature, &unmap_feature, NULL };
+
+ atom_Float = map.map(map.handle, "http://lv2plug.in/ns/ext/atom#Float");
+
+ LilvNode* num = lilv_new_int(world, 5);
+ LilvState* nostate = lilv_state_new_from_file(world, &map, num, "/junk");
+ TEST_ASSERT(!nostate);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, features);
+ TEST_ASSERT(instance);
+ lilv_instance_activate(instance);
+ lilv_instance_connect_port(instance, 0, &in);
+ lilv_instance_connect_port(instance, 1, &out);
+ lilv_instance_run(instance, 1);
+ TEST_ASSERT(in == 1.0);
+ TEST_ASSERT(out == 1.0);
+
+ temp_dir = lilv_realpath("temp");
+
+ const char* file_dir = NULL;
+ char* copy_dir = NULL;
+ char* link_dir = NULL;
+ char* save_dir = NULL;
+
+ // Get instance state state
+ LilvState* state = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+
+ // Get another instance state
+ LilvState* state2 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+
+ // Ensure they are equal
+ TEST_ASSERT(lilv_state_equals(state, state2));
+
+ // Check that we can't delete unsaved state
+ TEST_ASSERT(lilv_state_delete(world, state));
+
+ // Check that state has no URI
+ TEST_ASSERT(!lilv_state_get_uri(state));
+
+ // Check that we can't save a state with no URI
+ char* bad_state_str = lilv_state_to_string(
+ world, &map, &unmap, state, NULL, NULL);
+ TEST_ASSERT(!bad_state_str);
+
+ // Check that we can't restore the NULL string (and it doesn't crash)
+ LilvState* bad_state = lilv_state_new_from_string(world, &map, NULL);
+ TEST_ASSERT(!bad_state);
+
+ // Save state to a string
+ char* state1_str = lilv_state_to_string(
+ world, &map, &unmap, state, "http://example.org/state1", NULL);
+
+ // Restore from string
+ LilvState* from_str = lilv_state_new_from_string(world, &map, state1_str);
+
+ // Ensure they are equal
+ TEST_ASSERT(lilv_state_equals(state, from_str));
+ free(state1_str);
+
+ const LilvNode* state_plugin_uri = lilv_state_get_plugin_uri(state);
+ TEST_ASSERT(lilv_node_equals(state_plugin_uri, plugin_uri));
+
+ // Tinker with the label of the first state
+ TEST_ASSERT(lilv_state_get_label(state) == NULL);
+ lilv_state_set_label(state, "Test State Old Label");
+ TEST_ASSERT(!strcmp(lilv_state_get_label(state), "Test State Old Label"));
+ lilv_state_set_label(state, "Test State");
+ TEST_ASSERT(!strcmp(lilv_state_get_label(state), "Test State"));
+
+ TEST_ASSERT(!lilv_state_equals(state, state2)); // Label changed
+
+ // Run and get a new instance state (which should now differ)
+ lilv_instance_run(instance, 1);
+ LilvState* state3 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+ TEST_ASSERT(!lilv_state_equals(state2, state3)); // num_runs changed
+
+ // Restore instance state to original state
+ lilv_state_restore(state2, instance, set_port_value, NULL, 0, NULL);
+
+ // Take a new snapshot and ensure it matches the set state
+ LilvState* state4 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+ TEST_ASSERT(lilv_state_equals(state2, state4));
+
+ // Set some metadata properties
+ lilv_state_set_metadata(state, map.map(map.handle, LILV_NS_RDFS "comment"),
+ "This is a comment",
+ strlen("This is a comment") + 1,
+ map.map(map.handle, "http://lv2plug.in/ns/ext/atom#Literal"),
+ LV2_STATE_IS_POD);
+ lilv_state_set_metadata(state, map.map(map.handle, "http://example.org/metablob"),
+ "LIVEBEEF",
+ strlen("LIVEBEEF") + 1,
+ map.map(map.handle, "http://example.org/MetaBlob"),
+ 0);
+
+ // Save state to a directory
+ int ret = lilv_state_save(world, &map, &unmap, state, NULL,
+ "state/state.lv2", "state.ttl");
+ TEST_ASSERT(!ret);
+
+ // Load state from directory
+ LilvState* state5 = lilv_state_new_from_file(world, &map, NULL,
+ "state/state.lv2/state.ttl");
+
+ TEST_ASSERT(lilv_state_equals(state, state5)); // Round trip accuracy
+ TEST_ASSERT(lilv_state_get_num_properties(state) == 8);
+
+ // Attempt to save state to nowhere (error)
+ ret = lilv_state_save(world, &map, &unmap, state, NULL, NULL, NULL);
+ TEST_ASSERT(ret);
+
+ // Save another state to the same directory (update manifest)
+ ret = lilv_state_save(world, &map, &unmap, state, NULL,
+ "state/state.lv2", "state2.ttl");
+ TEST_ASSERT(!ret);
+
+ // Save state with URI to a directory
+ const char* state_uri = "http://example.org/state";
+ ret = lilv_state_save(world, &map, &unmap, state, state_uri,
+ "state/state6.lv2", "state6.ttl");
+ TEST_ASSERT(!ret);
+
+ // Load default bundle into world and load state from it
+ uint8_t* state6_path = (uint8_t*)lilv_path_absolute("state/state6.lv2/");
+ SerdNode state6_uri = serd_node_new_file_uri(state6_path, 0, 0, true);
+ LilvNode* test_state_bundle = lilv_new_uri(world, (const char*)state6_uri.buf);
+ LilvNode* test_state_node = lilv_new_uri(world, state_uri);
+ lilv_world_load_bundle(world, test_state_bundle);
+ lilv_world_load_resource(world, test_state_node);
+ serd_node_free(&state6_uri);
+ free(state6_path);
+
+ LilvState* state6 = lilv_state_new_from_world(world, &map, test_state_node);
+ TEST_ASSERT(lilv_state_equals(state, state6)); // Round trip accuracy
+
+ // Check that loaded state has correct URI
+ TEST_ASSERT(lilv_state_get_uri(state6));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_state_get_uri(state6)),
+ state_uri));
+
+ lilv_world_unload_resource(world, test_state_node);
+ lilv_world_unload_bundle(world, test_state_bundle);
+
+ LilvState* state6_2 = lilv_state_new_from_world(world, &map, test_state_node);
+ TEST_ASSERT(!state6_2); // No longer present
+ lilv_state_free(state6_2);
+
+ lilv_node_free(test_state_bundle);
+ lilv_node_free(test_state_node);
+
+ unsetenv("LV2_STATE_BUNDLE");
+
+ // Make directories and test files support
+ mkdir("temp", 0700);
+ file_dir = temp_dir;
+ mkdir("files", 0700);
+ copy_dir = lilv_realpath("files");
+ mkdir("links", 0700);
+ link_dir = lilv_realpath("links");
+
+ LV2_State_Make_Path make_path = { NULL, lilv_make_path };
+ LV2_Feature make_path_feature = { LV2_STATE__makePath, &make_path };
+ const LV2_Feature* ffeatures[] = { &make_path_feature, &map_feature, NULL };
+
+ lilv_instance_deactivate(instance);
+ lilv_instance_free(instance);
+ instance = lilv_plugin_instantiate(plugin, 48000.0, ffeatures);
+ lilv_instance_activate(instance);
+ lilv_instance_connect_port(instance, 0, &in);
+ lilv_instance_connect_port(instance, 1, &out);
+ lilv_instance_run(instance, 1);
+
+ // Test instantiating twice
+ LilvInstance* instance2 = lilv_plugin_instantiate(plugin, 48000.0, ffeatures);
+ if (!instance2) {
+ fatal_error("Failed to create multiple instances of <%s>\n",
+ lilv_node_as_uri(state_plugin_uri));
+ return 0;
+ }
+ lilv_instance_free(instance2);
+
+ // Get instance state state
+ LilvState* fstate = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate.lv2",
+ get_port_value, world, 0, ffeatures);
+
+ // Get another instance state
+ LilvState* fstate2 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate2.lv2",
+ get_port_value, world, 0, ffeatures);
+
+ // Should be identical
+ TEST_ASSERT(lilv_state_equals(fstate, fstate2));
+
+ // Run, writing more to rec file
+ lilv_instance_run(instance, 2);
+
+ // Get yet another instance state
+ LilvState* fstate3 = lilv_state_new_from_instance(
+ plugin, instance, &map, file_dir, copy_dir, link_dir, "state/fstate3.lv2",
+ get_port_value, world, 0, ffeatures);
+
+ // Should be different
+ TEST_ASSERT(!lilv_state_equals(fstate, fstate3));
+
+ // Save state to a directory
+ ret = lilv_state_save(world, &map, &unmap, fstate, NULL,
+ "state/fstate.lv2", "fstate.ttl");
+ TEST_ASSERT(!ret);
+
+ // Load state from directory
+ LilvState* fstate4 = lilv_state_new_from_file(world, &map, NULL,
+ "state/fstate.lv2/fstate.ttl");
+ TEST_ASSERT(lilv_state_equals(fstate, fstate4)); // Round trip accuracy
+
+ // Restore instance state to loaded state
+ lilv_state_restore(fstate4, instance, set_port_value, NULL, 0, ffeatures);
+
+ // Take a new snapshot and ensure it matches
+ LilvState* fstate5 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate5.lv2",
+ get_port_value, world, 0, ffeatures);
+ TEST_ASSERT(lilv_state_equals(fstate3, fstate5));
+
+ // Save state to a (different) directory again
+ ret = lilv_state_save(world, &map, &unmap, fstate, NULL,
+ "state/fstate6.lv2", "fstate6.ttl");
+ TEST_ASSERT(!ret);
+
+ // Reload it and ensure it's identical to the other loaded version
+ LilvState* fstate6 = lilv_state_new_from_file(world, &map, NULL,
+ "state/fstate6.lv2/fstate6.ttl");
+ TEST_ASSERT(lilv_state_equals(fstate4, fstate6));
+
+ // Run, changing rec file (without changing size)
+ lilv_instance_run(instance, 3);
+
+ // Take a new snapshot
+ LilvState* fstate7 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate7.lv2",
+ get_port_value, world, 0, ffeatures);
+ TEST_ASSERT(!lilv_state_equals(fstate6, fstate7));
+
+ // Save the changed state to a (different) directory again
+ ret = lilv_state_save(world, &map, &unmap, fstate7, NULL,
+ "state/fstate7.lv2", "fstate7.ttl");
+ TEST_ASSERT(!ret);
+
+ // Reload it and ensure it's changed
+ LilvState* fstate72 = lilv_state_new_from_file(world, &map, NULL,
+ "state/fstate7.lv2/fstate7.ttl");
+ TEST_ASSERT(lilv_state_equals(fstate72, fstate7));
+ TEST_ASSERT(!lilv_state_equals(fstate6, fstate72));
+
+ // Delete saved state
+ lilv_state_delete(world, fstate7);
+
+ lilv_instance_deactivate(instance);
+ lilv_instance_free(instance);
+
+ lilv_node_free(num);
+
+ lilv_state_free(state);
+ lilv_state_free(from_str);
+ lilv_state_free(state2);
+ lilv_state_free(state3);
+ lilv_state_free(state4);
+ lilv_state_free(state5);
+ lilv_state_free(state6);
+ lilv_state_free(fstate);
+ lilv_state_free(fstate2);
+ lilv_state_free(fstate3);
+ lilv_state_free(fstate4);
+ lilv_state_free(fstate5);
+ lilv_state_free(fstate6);
+ lilv_state_free(fstate7);
+ lilv_state_free(fstate72);
+
+ // Free URI map
+ for (size_t i = 0; i < n_uris; ++i) {
+ free(uris[i]);
+ }
+ free(uris);
+ n_uris = 0;
+
+ lilv_node_free(plugin_uri);
+ lilv_node_free(bundle_uri);
+ free(link_dir);
+ free(copy_dir);
+ free(temp_dir);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_bad_port_symbol(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "doap:homepage <http://example.org/someplug> ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"0invalid\" ;"
+ " lv2:name \"Invalid\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+
+ uint32_t n_ports = lilv_plugin_get_num_ports(plug);
+ TEST_ASSERT(n_ports == 0);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_bad_port_index(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "doap:homepage <http://example.org/someplug> ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index \"notaninteger\" ; lv2:symbol \"invalid\" ;"
+ " lv2:name \"Invalid\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+
+ uint32_t n_ports = lilv_plugin_get_num_ports(plug);
+ TEST_ASSERT(n_ports == 0);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_string(void)
+{
+ char* s = NULL;
+
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo/bar")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo/bar/")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo///bar/")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo///bar//")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("foo")), ".")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo")), "/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/")), "/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("//")), "/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a/b", "/a/")), "b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a", "/b/c/")), "/a")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a/b/c", "/a/b/d/")), "../c")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a/b/c", "/a/b/d/e/")), "../../c")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a", "b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a", "/b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a/", "/b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a/", "b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a", NULL)), "/a/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join(NULL, "/b")), "/b")); free(s);
+
+#ifndef _WIN32
+ setenv("LILV_TEST_1", "test", 1);
+ char* home_foo = lilv_strjoin(getenv("HOME"), "/foo", NULL);
+ TEST_ASSERT(!strcmp((s = lilv_expand("$LILV_TEST_1")), "test")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("~")), getenv("HOME"))); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("~foo")), "~foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("~/foo")), home_foo)); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("$NOT_A_VAR")), "$NOT_A_VAR")); free(s);
+ free(home_foo);
+ unsetenv("LILV_TEST_1");
+#endif
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_world(void)
+{
+ if (!init_world()) {
+ return 0;
+ }
+
+ LilvNode* num = lilv_new_int(world, 4);
+ LilvNode* uri = lilv_new_uri(world, "http://example.org/object");
+
+ LilvNodes* matches = lilv_world_find_nodes(world, num, NULL, NULL);
+ TEST_ASSERT(!matches);
+
+ matches = lilv_world_find_nodes(world, NULL, num, NULL);
+ TEST_ASSERT(!matches);
+
+ matches = lilv_world_find_nodes(world, NULL, uri, NULL);
+ TEST_ASSERT(!matches);
+
+ lilv_node_free(uri);
+ lilv_node_free(num);
+
+ lilv_world_unload_bundle(world, NULL);
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_reload_bundle(void)
+{
+ // Create a simple plugin bundle
+ create_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("First name") " .");
+
+ if (!init_world()) {
+ return 0;
+ }
+
+ init_uris();
+ lilv_world_load_specifications(world);
+
+ // Load bundle
+ LilvNode* bundle_uri = lilv_new_uri(world, bundle_dir_uri);
+ lilv_world_load_bundle(world, bundle_uri);
+
+ // Check that plugin is present
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ // Check that plugin name is correct
+ LilvNode* name = lilv_plugin_get_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "First name"));
+ lilv_node_free(name);
+
+ // Unload bundle from world and delete it
+ lilv_world_unload_bundle(world, bundle_uri);
+ delete_bundle();
+
+ // Create a new version of the same bundle, but with a different name
+ create_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Second name") " .");
+
+ // Check that plugin is no longer in the world's plugin list
+ TEST_ASSERT(lilv_plugins_size(plugins) == 0);
+
+ // Load new bundle
+ lilv_world_load_bundle(world, bundle_uri);
+
+ // Check that plugin is present again and is the same LilvPlugin
+ const LilvPlugin* plug2 = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug2);
+ TEST_ASSERT(plug2 == plug);
+
+ // Check that plugin now has new name
+ LilvNode* name2 = lilv_plugin_get_name(plug2);
+ TEST_ASSERT(name2);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name2), "Second name"));
+ lilv_node_free(name2);
+
+ // Load new bundle again (noop)
+ lilv_world_load_bundle(world, bundle_uri);
+
+ cleanup_uris();
+ lilv_node_free(bundle_uri);
+ lilv_world_free(world);
+ world = NULL;
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_replace_version(void)
+{
+ if (!init_world()) {
+ return 0;
+ }
+
+ LilvNode* plug_uri = lilv_new_uri(world, "http://example.org/versioned");
+ LilvNode* lv2_minorVersion = lilv_new_uri(world, LV2_CORE__minorVersion);
+ LilvNode* lv2_microVersion = lilv_new_uri(world, LV2_CORE__microVersion);
+ LilvNode* minor = NULL;
+ LilvNode* micro = NULL;
+
+ char* old_bundle_path = lilv_strjoin(LILV_TEST_DIR, "old_version.lv2/", 0);
+
+ // Load plugin from old bundle
+ LilvNode* old_bundle = lilv_new_file_uri(world, NULL, old_bundle_path);
+ lilv_world_load_bundle(world, old_bundle);
+ lilv_world_load_resource(world, plug_uri);
+
+ // Check version
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* old_plug = lilv_plugins_get_by_uri(plugins, plug_uri);
+ TEST_ASSERT(old_plug);
+ minor = lilv_world_get(world, plug_uri, lv2_minorVersion, 0);
+ micro = lilv_world_get(world, plug_uri, lv2_microVersion, 0);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(minor), "1"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(micro), "0"));
+ lilv_node_free(micro);
+ lilv_node_free(minor);
+
+ char* new_bundle_path = lilv_strjoin(LILV_TEST_DIR, "new_version.lv2/", 0);
+
+ // Load plugin from new bundle
+ LilvNode* new_bundle = lilv_new_file_uri(world, NULL, new_bundle_path);
+ lilv_world_load_bundle(world, new_bundle);
+ lilv_world_load_resource(world, plug_uri);
+
+ // Check that version in the world model has changed
+ plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* new_plug = lilv_plugins_get_by_uri(plugins, plug_uri);
+ TEST_ASSERT(new_plug);
+ TEST_ASSERT(lilv_node_equals(lilv_plugin_get_bundle_uri(new_plug), new_bundle));
+ minor = lilv_world_get(world, plug_uri, lv2_minorVersion, 0);
+ micro = lilv_world_get(world, plug_uri, lv2_microVersion, 0);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(minor), "2"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(micro), "1"));
+ lilv_node_free(micro);
+ lilv_node_free(minor);
+
+ // Try to load the old version again
+ lilv_world_load_bundle(world, old_bundle);
+ lilv_world_load_resource(world, plug_uri);
+
+ // Check that version in the world model has not changed
+ plugins = lilv_world_get_all_plugins(world);
+ new_plug = lilv_plugins_get_by_uri(plugins, plug_uri);
+ TEST_ASSERT(new_plug);
+ minor = lilv_world_get(world, plug_uri, lv2_minorVersion, 0);
+ micro = lilv_world_get(world, plug_uri, lv2_microVersion, 0);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(minor), "2"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(micro), "1"));
+ lilv_node_free(micro);
+ lilv_node_free(minor);
+
+ lilv_node_free(new_bundle);
+ lilv_node_free(old_bundle);
+ free(new_bundle_path);
+ free(old_bundle_path);
+ lilv_node_free(plug_uri);
+ lilv_node_free(lv2_minorVersion);
+ lilv_node_free(lv2_microVersion);
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_get_symbol(void)
+{
+ if (!start_bundle(
+ MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:symbol \"plugsym\" ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ "lv2:symbol \"plugsym\" .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ LilvNode* plug_sym = lilv_world_get_symbol(world, plugin_uri_value);
+ LilvNode* path = lilv_new_uri(world, "http://example.org/foo");
+ LilvNode* path_sym = lilv_world_get_symbol(world, path);
+ LilvNode* query = lilv_new_uri(world, "http://example.org/foo?bar=baz");
+ LilvNode* query_sym = lilv_world_get_symbol(world, query);
+ LilvNode* frag = lilv_new_uri(world, "http://example.org/foo#bar");
+ LilvNode* frag_sym = lilv_world_get_symbol(world, frag);
+ LilvNode* queryfrag = lilv_new_uri(world, "http://example.org/foo?bar=baz#quux");
+ LilvNode* queryfrag_sym = lilv_world_get_symbol(world, queryfrag);
+ LilvNode* nonuri = lilv_new_int(world, 42);
+
+ TEST_ASSERT(lilv_world_get_symbol(world, nonuri) == NULL);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(plug_sym), "plugsym"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(path_sym), "foo"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(query_sym), "bar_baz"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(frag_sym), "bar"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(queryfrag_sym), "quux"));
+
+ lilv_node_free(nonuri);
+ lilv_node_free(queryfrag_sym);
+ lilv_node_free(queryfrag);
+ lilv_node_free(frag_sym);
+ lilv_node_free(frag);
+ lilv_node_free(query_sym);
+ lilv_node_free(query);
+ lilv_node_free(path_sym);
+ lilv_node_free(path);
+ lilv_node_free(plug_sym);
+ cleanup_uris();
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+/* add tests here */
+static struct TestCase tests[] = {
+ TEST_CASE(util),
+ TEST_CASE(value),
+ TEST_CASE(verify),
+ TEST_CASE(no_verify),
+ TEST_CASE(discovery),
+ TEST_CASE(lv2_path),
+ TEST_CASE(classes),
+ TEST_CASE(plugin),
+ TEST_CASE(project),
+ TEST_CASE(no_author),
+ TEST_CASE(project_no_author),
+ TEST_CASE(preset),
+ TEST_CASE(prototype),
+ TEST_CASE(port),
+ TEST_CASE(ui),
+ TEST_CASE(bad_port_symbol),
+ TEST_CASE(bad_port_index),
+ TEST_CASE(bad_port_index),
+ TEST_CASE(string),
+ TEST_CASE(world),
+ TEST_CASE(state),
+ TEST_CASE(reload_bundle),
+ TEST_CASE(replace_version),
+ TEST_CASE(get_symbol),
+ { NULL, NULL }
+};
+
+static void
+run_tests(void)
+{
+ int i;
+ for (i = 0; tests[i].title; i++) {
+ printf("*** Test %s\n", tests[i].title);
+ if (!tests[i].func()) {
+ printf("\nTest failed\n");
+ /* test case that wasn't able to be executed at all counts as 1 test + 1 error */
+ error_count++;
+ test_count++;
+ }
+ unload_bundle();
+ cleanup();
+ }
+}
+
+int
+main(int argc, char* argv[])
+{
+ if (argc != 1) {
+ printf("Syntax: %s\n", argv[0]);
+ return 0;
+ }
+ setenv("LANG", "C", 1);
+ init_tests();
+ run_tests();
+ cleanup();
+ printf("\n*** Test Results: %d tests, %d errors\n\n", test_count, error_count);
+ return error_count ? 1 : 0;
+}
diff --git a/test/missing_descriptor.lv2/manifest.ttl.in b/test/missing_descriptor.lv2/manifest.ttl.in
new file mode 100644
index 0000000..789d1ec
--- /dev/null
+++ b/test/missing_descriptor.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-descriptor>
+ a lv2:Plugin ;
+ lv2:binary <missing_descriptor@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_descriptor.ttl> .
diff --git a/test/missing_descriptor.lv2/missing_descriptor.c b/test/missing_descriptor.lv2/missing_descriptor.c
new file mode 100644
index 0000000..0c49f23
--- /dev/null
+++ b/test/missing_descriptor.lv2/missing_descriptor.c
@@ -0,0 +1,21 @@
+/*
+ Lilv Test Plugin - Missing descriptor
+ Copyright 2011-2015 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+LV2_SYMBOL_EXPORT
+const char* msg = "this is not the thing you're looking for";
diff --git a/test/missing_descriptor.lv2/missing_descriptor.ttl.in b/test/missing_descriptor.lv2/missing_descriptor.ttl.in
new file mode 100644
index 0000000..9e2aad8
--- /dev/null
+++ b/test/missing_descriptor.lv2/missing_descriptor.ttl.in
@@ -0,0 +1,38 @@
+# Lilv Test Plugin - Missing descriptor
+# Copyright 2011-2015 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/missing-descriptor>
+ a lv2:Plugin ;
+ doap:name "Missing descriptor test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ 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"
+ ] .
diff --git a/test/missing_descriptor.lv2/test_missing_descriptor.c b/test/missing_descriptor.lv2/test_missing_descriptor.c
new file mode 100644
index 0000000..49909e1
--- /dev/null
+++ b/test/missing_descriptor.lv2/test_missing_descriptor.c
@@ -0,0 +1,46 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-descriptor"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(!instance);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/missing_name.lv2/manifest.ttl.in b/test/missing_name.lv2/manifest.ttl.in
new file mode 100644
index 0000000..62f4813
--- /dev/null
+++ b/test/missing_name.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-name>
+ a lv2:Plugin ;
+ lv2:binary <missing_name@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_name.ttl> .
diff --git a/test/missing_name.lv2/missing_name.c b/test/missing_name.lv2/missing_name.c
new file mode 100644
index 0000000..6b86e09
--- /dev/null
+++ b/test/missing_name.lv2/missing_name.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Missing name
+ Copyright 2011-2015 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/missing-name"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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;
+ default:
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* path,
+ const LV2_Feature* const* features)
+{
+ Test* test = (Test*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/missing_name.lv2/missing_name.ttl.in b/test/missing_name.lv2/missing_name.ttl.in
new file mode 100644
index 0000000..68ce23e
--- /dev/null
+++ b/test/missing_name.lv2/missing_name.ttl.in
@@ -0,0 +1,37 @@
+# Lilv Test Plugin - Missing plugin name
+# Copyright 2011-2015 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/missing-name>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ 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"
+ ] .
diff --git a/test/missing_name.lv2/test_missing_name.c b/test/missing_name.lv2/test_missing_name.c
new file mode 100644
index 0000000..960eec0
--- /dev/null
+++ b/test/missing_name.lv2/test_missing_name.c
@@ -0,0 +1,47 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-name"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(instance);
+ lilv_instance_free(instance);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/missing_plugin.lv2/manifest.ttl.in b/test/missing_plugin.lv2/manifest.ttl.in
new file mode 100644
index 0000000..d969cec
--- /dev/null
+++ b/test/missing_plugin.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-plugin>
+ a lv2:Plugin ;
+ lv2:binary <missing_plugin@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_plugin.ttl> .
diff --git a/test/missing_plugin.lv2/missing_plugin.c b/test/missing_plugin.lv2/missing_plugin.c
new file mode 100644
index 0000000..4d21226
--- /dev/null
+++ b/test/missing_plugin.lv2/missing_plugin.c
@@ -0,0 +1,43 @@
+/*
+ Lilv Test Plugin - Missing plugin
+ Copyright 2011-2015 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/missing-plugin"
+
+static const LV2_Descriptor descriptor = {
+ "http://example.org/not-the-plugin-you-are-looking-for",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ if (index == 0) {
+ return &descriptor;
+ }
+
+ return NULL;
+}
diff --git a/test/missing_plugin.lv2/missing_plugin.ttl.in b/test/missing_plugin.lv2/missing_plugin.ttl.in
new file mode 100644
index 0000000..ed8a7f3
--- /dev/null
+++ b/test/missing_plugin.lv2/missing_plugin.ttl.in
@@ -0,0 +1,38 @@
+# Lilv Test Plugin - Missing plugin
+# Copyright 2011-2015 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/missing-plugin>
+ a lv2:Plugin ;
+ doap:name "Missing descriptor test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ 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"
+ ] .
diff --git a/test/missing_plugin.lv2/test_missing_plugin.c b/test/missing_plugin.lv2/test_missing_plugin.c
new file mode 100644
index 0000000..bfc695b
--- /dev/null
+++ b/test/missing_plugin.lv2/test_missing_plugin.c
@@ -0,0 +1,46 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-plugin"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(!instance);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/missing_port.lv2/manifest.ttl.in b/test/missing_port.lv2/manifest.ttl.in
new file mode 100644
index 0000000..c090042
--- /dev/null
+++ b/test/missing_port.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-port>
+ a lv2:Plugin ;
+ lv2:binary <missing_port@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_port.ttl> .
diff --git a/test/missing_port.lv2/missing_port.c b/test/missing_port.lv2/missing_port.c
new file mode 100644
index 0000000..dd7e9ff
--- /dev/null
+++ b/test/missing_port.lv2/missing_port.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Missing port
+ Copyright 2011-2016 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/missing-port"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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;
+ default:
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* path,
+ const LV2_Feature* const* features)
+{
+ Test* test = (Test*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/missing_port.lv2/missing_port.ttl.in b/test/missing_port.lv2/missing_port.ttl.in
new file mode 100644
index 0000000..0dec1cf
--- /dev/null
+++ b/test/missing_port.lv2/missing_port.ttl.in
@@ -0,0 +1,31 @@
+# Lilv Test Plugin - Missing plugin port
+# Copyright 2011-2016 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/missing-port>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:OutputPort ,
+ lv2:ControlPort ;
+ lv2:index 1 ;
+ lv2:symbol "output" ;
+ lv2:name "Output"
+ ] .
diff --git a/test/missing_port.lv2/test_missing_port.c b/test/missing_port.lv2/test_missing_port.c
new file mode 100644
index 0000000..c67a1b8
--- /dev/null
+++ b/test/missing_port.lv2/test_missing_port.c
@@ -0,0 +1,45 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-port"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+
+ // Check that all ports are ignored
+ TEST_ASSERT(lilv_plugin_get_num_ports(plugin) == 0);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/missing_port_name.lv2/manifest.ttl.in b/test/missing_port_name.lv2/manifest.ttl.in
new file mode 100644
index 0000000..d6a4e39
--- /dev/null
+++ b/test/missing_port_name.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-port-name>
+ a lv2:Plugin ;
+ lv2:binary <missing_port_name@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_port_name.ttl> .
diff --git a/test/missing_port_name.lv2/missing_port_name.c b/test/missing_port_name.lv2/missing_port_name.c
new file mode 100644
index 0000000..4ed44ed
--- /dev/null
+++ b/test/missing_port_name.lv2/missing_port_name.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Missing port name
+ Copyright 2011-2015 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/missing-port-name"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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;
+ default:
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* path,
+ const LV2_Feature* const* features)
+{
+ Test* test = (Test*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/missing_port_name.lv2/missing_port_name.ttl.in b/test/missing_port_name.lv2/missing_port_name.ttl.in
new file mode 100644
index 0000000..5a58a80
--- /dev/null
+++ b/test/missing_port_name.lv2/missing_port_name.ttl.in
@@ -0,0 +1,30 @@
+# Lilv Test Plugin - Missing port name
+# Copyright 2011-2015 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/missing-port-name>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:InputPort ,
+ lv2:ControlPort ;
+ lv2:index 0 ;
+ lv2:symbol "input" ;
+ ] . \ No newline at end of file
diff --git a/test/missing_port_name.lv2/test_missing_port_name.c b/test/missing_port_name.lv2/test_missing_port_name.c
new file mode 100644
index 0000000..c000247
--- /dev/null
+++ b/test/missing_port_name.lv2/test_missing_port_name.c
@@ -0,0 +1,49 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-port-name"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ const LilvPort* port = lilv_plugin_get_port_by_index(plugin, 0);
+ TEST_ASSERT(port);
+ LilvNode* name = lilv_port_get_name(plugin, port);
+ TEST_ASSERT(!name);
+ lilv_node_free(name);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/new_version.lv2/manifest.ttl.in b/test/new_version.lv2/manifest.ttl.in
new file mode 100644
index 0000000..e76f7cf
--- /dev/null
+++ b/test/new_version.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/versioned>
+ a lv2:Plugin ;
+ lv2:binary <new_version@SHLIB_EXT@> ;
+ rdfs:seeAlso <new_version.ttl> .
diff --git a/test/new_version.lv2/new_version.c b/test/new_version.lv2/new_version.c
new file mode 100644
index 0000000..37d9fbf
--- /dev/null
+++ b/test/new_version.lv2/new_version.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - New version
+ Copyright 2011-2016 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/versioned"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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;
+ default:
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* path,
+ const LV2_Feature* const* features)
+{
+ Test* test = (Test*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/new_version.lv2/new_version.ttl.in b/test/new_version.lv2/new_version.ttl.in
new file mode 100644
index 0000000..7994666
--- /dev/null
+++ b/test/new_version.lv2/new_version.ttl.in
@@ -0,0 +1,40 @@
+# Lilv Test Plugin - New version
+# Copyright 2011-2016 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/versioned>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ doap:name "New version" ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:minorVersion 2 ;
+ lv2:microVersion 1 ;
+ 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"
+ ] .
diff --git a/test/old_version.lv2/manifest.ttl.in b/test/old_version.lv2/manifest.ttl.in
new file mode 100644
index 0000000..3c96cb5
--- /dev/null
+++ b/test/old_version.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/versioned>
+ a lv2:Plugin ;
+ lv2:binary <old_version@SHLIB_EXT@> ;
+ rdfs:seeAlso <old_version.ttl> .
diff --git a/test/old_version.lv2/old_version.c b/test/old_version.lv2/old_version.c
new file mode 100644
index 0000000..303f09c
--- /dev/null
+++ b/test/old_version.lv2/old_version.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Old version
+ Copyright 2011-2016 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/versioned"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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;
+ default:
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* path,
+ const LV2_Feature* const* features)
+{
+ Test* test = (Test*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/old_version.lv2/old_version.ttl.in b/test/old_version.lv2/old_version.ttl.in
new file mode 100644
index 0000000..2b68f76
--- /dev/null
+++ b/test/old_version.lv2/old_version.ttl.in
@@ -0,0 +1,40 @@
+# Lilv Test Plugin - Old version
+# Copyright 2011-2016 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/versioned>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ doap:name "Old version" ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:minorVersion 1 ;
+ lv2:microVersion 0 ;
+ 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"
+ ] .
diff --git a/test/test.lv2/manifest.ttl.in b/test/test.lv2/manifest.ttl.in
new file mode 100644
index 0000000..bc3952c
--- /dev/null
+++ b/test/test.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/lilv-test-plugin>
+ a lv2:Plugin ;
+ lv2:binary <test@SHLIB_EXT@> ;
+ rdfs:seeAlso <test.ttl> .
diff --git a/test/test.lv2/test.c b/test/test.lv2/test.c
new file mode 100644
index 0000000..d16e4a9
--- /dev/null
+++ b/test/test.lv2/test.c
@@ -0,0 +1,394 @@
+/*
+ Lilv Test Plugin
+ Copyright 2011-2017 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#ifdef _WIN32
+# include <io.h>
+# define mkstemp(pat) _mktemp(pat)
+#endif
+
+#define TEST_URI "http://example.org/lilv-test-plugin"
+
+#define TMP_TEMPLATE "lilv_testXXXXXX"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1,
+ TEST_CONTROL = 2
+};
+
+typedef struct {
+ LV2_URID_Map* map;
+
+ struct {
+ LV2_URID atom_Float;
+ } uris;
+
+ char tmp_file_path[sizeof(TMP_TEMPLATE)];
+ char* rec_file_path;
+ FILE* rec_file;
+
+ float* input;
+ float* output;
+ unsigned num_runs;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Test* test = (Test*)instance;
+ if (test->rec_file) {
+ fclose(test->rec_file);
+ }
+ free(test->rec_file_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)
+{
+ Test* test = (Test*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ strncpy(test->tmp_file_path, TMP_TEMPLATE, strlen(TMP_TEMPLATE) + 1);
+ mkstemp(test->tmp_file_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;
+ }
+ }
+
+ if (!test->map) {
+ fprintf(stderr, "Host does not support urid:map\n");
+ free(test);
+ return NULL;
+ }
+
+ if (make_path) {
+ 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)
+{
+ Test* plugin = (Test*)instance;
+
+ LV2_State_Map_Path* map_path = NULL;
+ LV2_State_Make_Path* make_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;
+ }
+ }
+
+ 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);
+
+ 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) {
+ FILE* file = fopen(plugin->tmp_file_path, "w");
+ fprintf(file, "Hello\n");
+ fclose(file);
+ char* apath = map_path->abstract_path(map_path->handle,
+ plugin->tmp_file_path);
+ char* apath2 = map_path->abstract_path(map_path->handle,
+ plugin->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(apath);
+ free(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(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(apath);
+ free(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)
+{
+ Test* plugin = (Test*)instance;
+
+ LV2_State_Map_Path* map_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;
+ }
+ }
+
+ size_t size;
+ uint32_t type;
+ uint32_t valflags;
+
+ plugin->num_runs = *(int32_t*)retrieve(
+ callback_data,
+ map_uri(plugin, "http://example.org/num-runs"),
+ &size, &type, &valflags);
+
+ if (!map_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);
+ }
+
+ 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(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
new file mode 100644
index 0000000..1c16b4c
--- /dev/null
+++ b/test/test.lv2/test.ttl.in
@@ -0,0 +1,46 @@
+# Lilv Test Plugin
+# Copyright 2011-2015 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/lilv-test-plugin>
+ a lv2:Plugin ;
+ doap:name "Lilv Test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:requiredFeature <http://lv2plug.in/ns/ext/urid#Mapper> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:extensionData <http://lv2plug.in/ns/ext/state#Interface> ;
+ 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/utils/bench.h b/utils/bench.h
new file mode 100644
index 0000000..83d7853
--- /dev/null
+++ b/utils/bench.h
@@ -0,0 +1,52 @@
+/*
+ Copyright 2011-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file bench.h A simple real-time benchmarking API.
+*/
+
+#ifndef BENCH_H
+#define BENCH_H
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <time.h>
+#include <sys/time.h>
+
+static inline double
+bench_elapsed_s(const struct timespec* start, const struct timespec* end)
+{
+ return ((end->tv_sec - start->tv_sec)
+ + ((end->tv_nsec - start->tv_nsec) * 0.000000001));
+}
+
+static inline struct timespec
+bench_start(void)
+{
+ struct timespec start_t;
+ clock_gettime(CLOCK_REALTIME, &start_t);
+ return start_t;
+}
+
+static inline double
+bench_end(const struct timespec* start_t)
+{
+ struct timespec end_t;
+ clock_gettime(CLOCK_REALTIME, &end_t);
+ return bench_elapsed_s(start_t, &end_t);
+}
+
+#endif /* BENCH_H */
diff --git a/utils/lilv-bench.c b/utils/lilv-bench.c
new file mode 100644
index 0000000..c0355df
--- /dev/null
+++ b/utils/lilv-bench.c
@@ -0,0 +1,38 @@
+/*
+ Copyright 2007-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdio.h>
+
+#include "lilv/lilv.h"
+
+#include "lilv_config.h"
+
+int
+main(int argc, char** argv)
+{
+ 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
new file mode 100644
index 0000000..4a553a7
--- /dev/null
+++ b/utils/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 <d@drobilla.net> on 2012-01-08.
+# Written by Lars Luthman <lars.luthman@gmail.com> 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
new file mode 100644
index 0000000..66a8c3a
--- /dev/null
+++ b/utils/lv2apply.c
@@ -0,0 +1,354 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <assert.h>
+#include <math.h>
+#include <sndfile.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv/lilv.h"
+
+/** 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;
+ 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. */
+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 %d 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 %d 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-2016 David Robillard <http://drobilla.net>\n"
+ "License: <http://www.opensource.org/licenses/isc-license>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n");
+}
+
+static 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;
+ } else if (!strcmp(argv[i], "--help")) {
+ free(self.params);
+ return print_usage(0);
+ } else 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 %d 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))) {
+ 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];
+ float out_buf[self.n_audio_out];
+ 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
new file mode 100644
index 0000000..e3eb57f
--- /dev/null
+++ b/utils/lv2bench.c
@@ -0,0 +1,268 @@
+/*
+ Copyright 2012-2017 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv/lilv.h"
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+
+#include "lilv_config.h"
+#include "bench.h"
+#include "uri_table.h"
+
+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-2017 David Robillard <http://drobilla.net>\n"
+ "License: <http://www.opensource.org/licenses/isc-license>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n");
+}
+
+static void
+print_usage(void)
+{
+ printf("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 * 2, 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(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(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 %d 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 %d 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(seq_out);
+
+ uri_table_destroy(&uri_table);
+
+ if (full_output) {
+ printf("%d %d ", block_size, sample_count);
+ }
+ printf("%lf %s\n", elapsed, uri);
+
+ free(buf);
+ free(controls);
+ 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;
+ } else if (!strcmp(argv[a], "--help")) {
+ print_usage();
+ return 0;
+ } else 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);
+ } 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
new file mode 100644
index 0000000..75a093f
--- /dev/null
+++ b/utils/lv2info.c
@@ -0,0 +1,460 @@
+/*
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h"
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+#include "lv2/lv2plug.in/ns/ext/event/event.h"
+
+#include "lilv/lilv.h"
+
+#include "lilv_config.h"
+
+#ifdef _MSC_VER
+# define isnan _isnan
+#endif
+
+LilvNode* applies_to_pred = NULL;
+LilvNode* control_class = NULL;
+LilvNode* event_class = NULL;
+LilvNode* group_pred = NULL;
+LilvNode* label_pred = NULL;
+LilvNode* preset_class = NULL;
+LilvNode* designation_pred = NULL;
+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 %d:\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 %d\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-2014 David Robillard <http://drobilla.net>\n"
+ "License: <http://www.opensource.org/licenses/isc-license>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n");
+}
+
+static void
+print_usage(void)
+{
+ printf(
+ "Usage: 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;
+ } else if (!strcmp(argv[i], "--help")) {
+ print_usage();
+ return 0;
+ } else 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
new file mode 100644
index 0000000..1f007f1
--- /dev/null
+++ b/utils/lv2ls.c
@@ -0,0 +1,93 @@
+/*
+ Copyright 2007-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lilv/lilv.h"
+
+#include "lilv_config.h"
+
+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-2012 David Robillard <http://drobilla.net>\n"
+ "License: <http://www.opensource.org/licenses/isc-license>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n");
+}
+
+static void
+print_usage(void)
+{
+ printf("Usage: 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
new file mode 100644
index 0000000..880d560
--- /dev/null
+++ b/utils/uri_table.h
@@ -0,0 +1,73 @@
+/*
+ Copyright 2011-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file uri_table.h A toy URI map/unmap implementation.
+
+ This file contains function definitions and must only be included once.
+*/
+
+#ifndef URI_TABLE_H
+#define URI_TABLE_H
+
+typedef struct {
+ char** uris;
+ size_t n_uris;
+} URITable;
+
+static void
+uri_table_init(URITable* table)
+{
+ table->uris = NULL;
+ table->n_uris = 0;
+}
+
+static void
+uri_table_destroy(URITable* table)
+{
+ free(table->uris);
+}
+
+static LV2_URID
+uri_table_map(LV2_URID_Map_Handle handle,
+ const char* uri)
+{
+ URITable* table = (URITable*)handle;
+ for (size_t i = 0; i < table->n_uris; ++i) {
+ if (!strcmp(table->uris[i], uri)) {
+ return i + 1;
+ }
+ }
+
+ const size_t len = strlen(uri);
+ table->uris = (char**)realloc(table->uris, ++table->n_uris * sizeof(char*));
+ table->uris[table->n_uris - 1] = (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/waflib/.gitignore b/waflib/.gitignore
new file mode 100644
index 0000000..8d35cb3
--- /dev/null
+++ b/waflib/.gitignore
@@ -0,0 +1,2 @@
+__pycache__
+*.pyc
diff --git a/Build.py b/waflib/Build.py
index 1afcba6..1afcba6 100644
--- a/Build.py
+++ b/waflib/Build.py
diff --git a/waflib/COPYING b/waflib/COPYING
new file mode 100644
index 0000000..a4147d2
--- /dev/null
+++ b/waflib/COPYING
@@ -0,0 +1,25 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/ConfigSet.py b/waflib/ConfigSet.py
index b300bb5..b300bb5 100644
--- a/ConfigSet.py
+++ b/waflib/ConfigSet.py
diff --git a/Configure.py b/waflib/Configure.py
index d0a4793..d0a4793 100644
--- a/Configure.py
+++ b/waflib/Configure.py
diff --git a/Context.py b/waflib/Context.py
index bb47c92..bb47c92 100644
--- a/Context.py
+++ b/waflib/Context.py
diff --git a/Errors.py b/waflib/Errors.py
index bf75c1b..bf75c1b 100644
--- a/Errors.py
+++ b/waflib/Errors.py
diff --git a/Logs.py b/waflib/Logs.py
index 2a47516..2a47516 100644
--- a/Logs.py
+++ b/waflib/Logs.py
diff --git a/Node.py b/waflib/Node.py
index 4ac1ea8..4ac1ea8 100644
--- a/Node.py
+++ b/waflib/Node.py
diff --git a/Options.py b/waflib/Options.py
index ad802d4..ad802d4 100644
--- a/Options.py
+++ b/waflib/Options.py
diff --git a/README.md b/waflib/README.md
index c5361b9..c5361b9 100644
--- a/README.md
+++ b/waflib/README.md
diff --git a/Runner.py b/waflib/Runner.py
index 261084d..261084d 100644
--- a/Runner.py
+++ b/waflib/Runner.py
diff --git a/Scripting.py b/waflib/Scripting.py
index 749d4f2..749d4f2 100644
--- a/Scripting.py
+++ b/waflib/Scripting.py
diff --git a/Task.py b/waflib/Task.py
index 0fc449d..0fc449d 100644
--- a/Task.py
+++ b/waflib/Task.py
diff --git a/TaskGen.py b/waflib/TaskGen.py
index a74e643..a74e643 100644
--- a/TaskGen.py
+++ b/waflib/TaskGen.py
diff --git a/Tools/__init__.py b/waflib/Tools/__init__.py
index 079df35..079df35 100644
--- a/Tools/__init__.py
+++ b/waflib/Tools/__init__.py
diff --git a/Tools/ar.py b/waflib/Tools/ar.py
index b39b645..b39b645 100644
--- a/Tools/ar.py
+++ b/waflib/Tools/ar.py
diff --git a/Tools/asm.py b/waflib/Tools/asm.py
index b6f26fb..b6f26fb 100644
--- a/Tools/asm.py
+++ b/waflib/Tools/asm.py
diff --git a/Tools/bison.py b/waflib/Tools/bison.py
index eef56dc..eef56dc 100644
--- a/Tools/bison.py
+++ b/waflib/Tools/bison.py
diff --git a/Tools/c.py b/waflib/Tools/c.py
index effd6b6..effd6b6 100644
--- a/Tools/c.py
+++ b/waflib/Tools/c.py
diff --git a/Tools/c_aliases.py b/waflib/Tools/c_aliases.py
index c9d5369..c9d5369 100644
--- a/Tools/c_aliases.py
+++ b/waflib/Tools/c_aliases.py
diff --git a/Tools/c_config.py b/waflib/Tools/c_config.py
index d2b3c0d..d2b3c0d 100644
--- a/Tools/c_config.py
+++ b/waflib/Tools/c_config.py
diff --git a/Tools/c_osx.py b/waflib/Tools/c_osx.py
index f70b128..f70b128 100644
--- a/Tools/c_osx.py
+++ b/waflib/Tools/c_osx.py
diff --git a/Tools/c_preproc.py b/waflib/Tools/c_preproc.py
index 7e04b4a..7e04b4a 100644
--- a/Tools/c_preproc.py
+++ b/waflib/Tools/c_preproc.py
diff --git a/Tools/c_tests.py b/waflib/Tools/c_tests.py
index f858df5..f858df5 100644
--- a/Tools/c_tests.py
+++ b/waflib/Tools/c_tests.py
diff --git a/Tools/ccroot.py b/waflib/Tools/ccroot.py
index cfef8bf..cfef8bf 100644
--- a/Tools/ccroot.py
+++ b/waflib/Tools/ccroot.py
diff --git a/Tools/clang.py b/waflib/Tools/clang.py
index 3828e39..3828e39 100644
--- a/Tools/clang.py
+++ b/waflib/Tools/clang.py
diff --git a/Tools/clangxx.py b/waflib/Tools/clangxx.py
index 152013c..152013c 100644
--- a/Tools/clangxx.py
+++ b/waflib/Tools/clangxx.py
diff --git a/Tools/compiler_c.py b/waflib/Tools/compiler_c.py
index 2dba3f8..2dba3f8 100644
--- a/Tools/compiler_c.py
+++ b/waflib/Tools/compiler_c.py
diff --git a/Tools/compiler_cxx.py b/waflib/Tools/compiler_cxx.py
index 1af65a2..1af65a2 100644
--- a/Tools/compiler_cxx.py
+++ b/waflib/Tools/compiler_cxx.py
diff --git a/Tools/compiler_d.py b/waflib/Tools/compiler_d.py
index 43bb1f6..43bb1f6 100644
--- a/Tools/compiler_d.py
+++ b/waflib/Tools/compiler_d.py
diff --git a/Tools/compiler_fc.py b/waflib/Tools/compiler_fc.py
index 96b58e7..96b58e7 100644
--- a/Tools/compiler_fc.py
+++ b/waflib/Tools/compiler_fc.py
diff --git a/Tools/cs.py b/waflib/Tools/cs.py
index aecca6d..aecca6d 100644
--- a/Tools/cs.py
+++ b/waflib/Tools/cs.py
diff --git a/Tools/cxx.py b/waflib/Tools/cxx.py
index 194fad7..194fad7 100644
--- a/Tools/cxx.py
+++ b/waflib/Tools/cxx.py
diff --git a/Tools/d.py b/waflib/Tools/d.py
index e4cf73b..e4cf73b 100644
--- a/Tools/d.py
+++ b/waflib/Tools/d.py
diff --git a/Tools/d_config.py b/waflib/Tools/d_config.py
index 6637556..6637556 100644
--- a/Tools/d_config.py
+++ b/waflib/Tools/d_config.py
diff --git a/Tools/d_scan.py b/waflib/Tools/d_scan.py
index 14c6c31..14c6c31 100644
--- a/Tools/d_scan.py
+++ b/waflib/Tools/d_scan.py
diff --git a/Tools/dbus.py b/waflib/Tools/dbus.py
index d520f1c..d520f1c 100644
--- a/Tools/dbus.py
+++ b/waflib/Tools/dbus.py
diff --git a/Tools/dmd.py b/waflib/Tools/dmd.py
index 8917ca1..8917ca1 100644
--- a/Tools/dmd.py
+++ b/waflib/Tools/dmd.py
diff --git a/Tools/errcheck.py b/waflib/Tools/errcheck.py
index de8d75a..de8d75a 100644
--- a/Tools/errcheck.py
+++ b/waflib/Tools/errcheck.py
diff --git a/Tools/fc.py b/waflib/Tools/fc.py
index d9e8d8c..d9e8d8c 100644
--- a/Tools/fc.py
+++ b/waflib/Tools/fc.py
diff --git a/Tools/fc_config.py b/waflib/Tools/fc_config.py
index 222f3a5..222f3a5 100644
--- a/Tools/fc_config.py
+++ b/waflib/Tools/fc_config.py
diff --git a/Tools/fc_scan.py b/waflib/Tools/fc_scan.py
index 12cb0fc..12cb0fc 100644
--- a/Tools/fc_scan.py
+++ b/waflib/Tools/fc_scan.py
diff --git a/Tools/flex.py b/waflib/Tools/flex.py
index 2256657..2256657 100644
--- a/Tools/flex.py
+++ b/waflib/Tools/flex.py
diff --git a/Tools/g95.py b/waflib/Tools/g95.py
index f69ba4f..f69ba4f 100644
--- a/Tools/g95.py
+++ b/waflib/Tools/g95.py
diff --git a/Tools/gas.py b/waflib/Tools/gas.py
index 77afed7..77afed7 100644
--- a/Tools/gas.py
+++ b/waflib/Tools/gas.py
diff --git a/Tools/gcc.py b/waflib/Tools/gcc.py
index acdd473..acdd473 100644
--- a/Tools/gcc.py
+++ b/waflib/Tools/gcc.py
diff --git a/Tools/gdc.py b/waflib/Tools/gdc.py
index d89a66d..d89a66d 100644
--- a/Tools/gdc.py
+++ b/waflib/Tools/gdc.py
diff --git a/Tools/gfortran.py b/waflib/Tools/gfortran.py
index 1050667..1050667 100644
--- a/Tools/gfortran.py
+++ b/waflib/Tools/gfortran.py
diff --git a/Tools/glib2.py b/waflib/Tools/glib2.py
index 949fe37..949fe37 100644
--- a/Tools/glib2.py
+++ b/waflib/Tools/glib2.py
diff --git a/Tools/gnu_dirs.py b/waflib/Tools/gnu_dirs.py
index 2847071..2847071 100644
--- a/Tools/gnu_dirs.py
+++ b/waflib/Tools/gnu_dirs.py
diff --git a/Tools/gxx.py b/waflib/Tools/gxx.py
index 22c5d26..22c5d26 100644
--- a/Tools/gxx.py
+++ b/waflib/Tools/gxx.py
diff --git a/Tools/icc.py b/waflib/Tools/icc.py
index b6492c8..b6492c8 100644
--- a/Tools/icc.py
+++ b/waflib/Tools/icc.py
diff --git a/Tools/icpc.py b/waflib/Tools/icpc.py
index 8a6cc6c..8a6cc6c 100644
--- a/Tools/icpc.py
+++ b/waflib/Tools/icpc.py
diff --git a/Tools/ifort.py b/waflib/Tools/ifort.py
index 74934f3..74934f3 100644
--- a/Tools/ifort.py
+++ b/waflib/Tools/ifort.py
diff --git a/Tools/intltool.py b/waflib/Tools/intltool.py
index af95ba8..af95ba8 100644
--- a/Tools/intltool.py
+++ b/waflib/Tools/intltool.py
diff --git a/Tools/irixcc.py b/waflib/Tools/irixcc.py
index c3ae1ac..c3ae1ac 100644
--- a/Tools/irixcc.py
+++ b/waflib/Tools/irixcc.py
diff --git a/Tools/javaw.py b/waflib/Tools/javaw.py
index f6fd20c..f6fd20c 100644
--- a/Tools/javaw.py
+++ b/waflib/Tools/javaw.py
diff --git a/Tools/ldc2.py b/waflib/Tools/ldc2.py
index a51c344..a51c344 100644
--- a/Tools/ldc2.py
+++ b/waflib/Tools/ldc2.py
diff --git a/Tools/lua.py b/waflib/Tools/lua.py
index 15a333a..15a333a 100644
--- a/Tools/lua.py
+++ b/waflib/Tools/lua.py
diff --git a/Tools/md5_tstamp.py b/waflib/Tools/md5_tstamp.py
index 6428e46..6428e46 100644
--- a/Tools/md5_tstamp.py
+++ b/waflib/Tools/md5_tstamp.py
diff --git a/Tools/msvc.py b/waflib/Tools/msvc.py
index 17b347d..17b347d 100644
--- a/Tools/msvc.py
+++ b/waflib/Tools/msvc.py
diff --git a/Tools/nasm.py b/waflib/Tools/nasm.py
index 411d582..411d582 100644
--- a/Tools/nasm.py
+++ b/waflib/Tools/nasm.py
diff --git a/Tools/nobuild.py b/waflib/Tools/nobuild.py
index 2e4b055..2e4b055 100644
--- a/Tools/nobuild.py
+++ b/waflib/Tools/nobuild.py
diff --git a/Tools/perl.py b/waflib/Tools/perl.py
index 32b03fb..32b03fb 100644
--- a/Tools/perl.py
+++ b/waflib/Tools/perl.py
diff --git a/Tools/python.py b/waflib/Tools/python.py
index 25841d0..25841d0 100644
--- a/Tools/python.py
+++ b/waflib/Tools/python.py
diff --git a/Tools/qt5.py b/waflib/Tools/qt5.py
index 4f9c690..4f9c690 100644
--- a/Tools/qt5.py
+++ b/waflib/Tools/qt5.py
diff --git a/Tools/ruby.py b/waflib/Tools/ruby.py
index 8d92a79..8d92a79 100644
--- a/Tools/ruby.py
+++ b/waflib/Tools/ruby.py
diff --git a/Tools/suncc.py b/waflib/Tools/suncc.py
index 33d34fc..33d34fc 100644
--- a/Tools/suncc.py
+++ b/waflib/Tools/suncc.py
diff --git a/Tools/suncxx.py b/waflib/Tools/suncxx.py
index 3b384f6..3b384f6 100644
--- a/Tools/suncxx.py
+++ b/waflib/Tools/suncxx.py
diff --git a/Tools/tex.py b/waflib/Tools/tex.py
index eaf9fdb..eaf9fdb 100644
--- a/Tools/tex.py
+++ b/waflib/Tools/tex.py
diff --git a/Tools/vala.py b/waflib/Tools/vala.py
index 822ec50..822ec50 100644
--- a/Tools/vala.py
+++ b/waflib/Tools/vala.py
diff --git a/Tools/waf_unit_test.py b/waflib/Tools/waf_unit_test.py
index a71ed1c..a71ed1c 100644
--- a/Tools/waf_unit_test.py
+++ b/waflib/Tools/waf_unit_test.py
diff --git a/Tools/winres.py b/waflib/Tools/winres.py
index 586c596..586c596 100644
--- a/Tools/winres.py
+++ b/waflib/Tools/winres.py
diff --git a/Tools/xlc.py b/waflib/Tools/xlc.py
index 134dd41..134dd41 100644
--- a/Tools/xlc.py
+++ b/waflib/Tools/xlc.py
diff --git a/Tools/xlcxx.py b/waflib/Tools/xlcxx.py
index 76aa59b..76aa59b 100644
--- a/Tools/xlcxx.py
+++ b/waflib/Tools/xlcxx.py
diff --git a/Utils.py b/waflib/Utils.py
index b4665c4..b4665c4 100644
--- a/Utils.py
+++ b/waflib/Utils.py
diff --git a/__init__.py b/waflib/__init__.py
index 079df35..079df35 100644
--- a/__init__.py
+++ b/waflib/__init__.py
diff --git a/ansiterm.py b/waflib/ansiterm.py
index 0d20c63..0d20c63 100644
--- a/ansiterm.py
+++ b/waflib/ansiterm.py
diff --git a/extras/__init__.py b/waflib/extras/__init__.py
index c8a3c34..c8a3c34 100644
--- a/extras/__init__.py
+++ b/waflib/extras/__init__.py
diff --git a/extras/autowaf.py b/waflib/extras/autowaf.py
index be3bfe2..be3bfe2 100644
--- a/extras/autowaf.py
+++ b/waflib/extras/autowaf.py
diff --git a/extras/batched_cc.py b/waflib/extras/batched_cc.py
index aad2872..aad2872 100644
--- a/extras/batched_cc.py
+++ b/waflib/extras/batched_cc.py
diff --git a/extras/biber.py b/waflib/extras/biber.py
index fd9db4e..fd9db4e 100644
--- a/extras/biber.py
+++ b/waflib/extras/biber.py
diff --git a/extras/bjam.py b/waflib/extras/bjam.py
index 8e04d3a..8e04d3a 100644
--- a/extras/bjam.py
+++ b/waflib/extras/bjam.py
diff --git a/extras/blender.py b/waflib/extras/blender.py
index e5efc28..e5efc28 100644
--- a/extras/blender.py
+++ b/waflib/extras/blender.py
diff --git a/extras/boo.py b/waflib/extras/boo.py
index 06623d4..06623d4 100644
--- a/extras/boo.py
+++ b/waflib/extras/boo.py
diff --git a/extras/boost.py b/waflib/extras/boost.py
index c2aaaa9..c2aaaa9 100644
--- a/extras/boost.py
+++ b/waflib/extras/boost.py
diff --git a/extras/build_file_tracker.py b/waflib/extras/build_file_tracker.py
index c4f26fd..c4f26fd 100644
--- a/extras/build_file_tracker.py
+++ b/waflib/extras/build_file_tracker.py
diff --git a/extras/build_logs.py b/waflib/extras/build_logs.py
index cdf8ed0..cdf8ed0 100644
--- a/extras/build_logs.py
+++ b/waflib/extras/build_logs.py
diff --git a/extras/buildcopy.py b/waflib/extras/buildcopy.py
index a6d9ac8..a6d9ac8 100644
--- a/extras/buildcopy.py
+++ b/waflib/extras/buildcopy.py
diff --git a/extras/c_bgxlc.py b/waflib/extras/c_bgxlc.py
index 6e3eaf7..6e3eaf7 100644
--- a/extras/c_bgxlc.py
+++ b/waflib/extras/c_bgxlc.py
diff --git a/extras/c_dumbpreproc.py b/waflib/extras/c_dumbpreproc.py
index ce9e1a4..ce9e1a4 100644
--- a/extras/c_dumbpreproc.py
+++ b/waflib/extras/c_dumbpreproc.py
diff --git a/extras/c_emscripten.py b/waflib/extras/c_emscripten.py
index e1ac494..e1ac494 100644
--- a/extras/c_emscripten.py
+++ b/waflib/extras/c_emscripten.py
diff --git a/extras/c_nec.py b/waflib/extras/c_nec.py
index 96bfae4..96bfae4 100644
--- a/extras/c_nec.py
+++ b/waflib/extras/c_nec.py
diff --git a/extras/cabal.py b/waflib/extras/cabal.py
index e10a0d1..e10a0d1 100644
--- a/extras/cabal.py
+++ b/waflib/extras/cabal.py
diff --git a/extras/cfg_altoptions.py b/waflib/extras/cfg_altoptions.py
index 47b1189..47b1189 100644
--- a/extras/cfg_altoptions.py
+++ b/waflib/extras/cfg_altoptions.py
diff --git a/extras/clang_compilation_database.py b/waflib/extras/clang_compilation_database.py
index 4d9b5e2..4d9b5e2 100644
--- a/extras/clang_compilation_database.py
+++ b/waflib/extras/clang_compilation_database.py
diff --git a/extras/codelite.py b/waflib/extras/codelite.py
index 523302c..523302c 100644
--- a/extras/codelite.py
+++ b/waflib/extras/codelite.py
diff --git a/extras/color_gcc.py b/waflib/extras/color_gcc.py
index b68c5eb..b68c5eb 100644
--- a/extras/color_gcc.py
+++ b/waflib/extras/color_gcc.py
diff --git a/extras/color_rvct.py b/waflib/extras/color_rvct.py
index f89ccbd..f89ccbd 100644
--- a/extras/color_rvct.py
+++ b/waflib/extras/color_rvct.py
diff --git a/extras/compat15.py b/waflib/extras/compat15.py
index 0e74df8..0e74df8 100644
--- a/extras/compat15.py
+++ b/waflib/extras/compat15.py
diff --git a/extras/cppcheck.py b/waflib/extras/cppcheck.py
index 13ff424..13ff424 100644
--- a/extras/cppcheck.py
+++ b/waflib/extras/cppcheck.py
diff --git a/extras/cpplint.py b/waflib/extras/cpplint.py
index e3302e5..e3302e5 100644
--- a/extras/cpplint.py
+++ b/waflib/extras/cpplint.py
diff --git a/extras/cross_gnu.py b/waflib/extras/cross_gnu.py
index 309f53b..309f53b 100644
--- a/extras/cross_gnu.py
+++ b/waflib/extras/cross_gnu.py
diff --git a/extras/cython.py b/waflib/extras/cython.py
index 481d6f4..481d6f4 100644
--- a/extras/cython.py
+++ b/waflib/extras/cython.py
diff --git a/extras/dcc.py b/waflib/extras/dcc.py
index c1a57c0..c1a57c0 100644
--- a/extras/dcc.py
+++ b/waflib/extras/dcc.py
diff --git a/extras/distnet.py b/waflib/extras/distnet.py
index 09a31a6..09a31a6 100644
--- a/extras/distnet.py
+++ b/waflib/extras/distnet.py
diff --git a/extras/doxygen.py b/waflib/extras/doxygen.py
index 28f56e9..28f56e9 100644
--- a/extras/doxygen.py
+++ b/waflib/extras/doxygen.py
diff --git a/extras/dpapi.py b/waflib/extras/dpapi.py
index b94d482..b94d482 100644
--- a/extras/dpapi.py
+++ b/waflib/extras/dpapi.py
diff --git a/extras/eclipse.py b/waflib/extras/eclipse.py
index bb78741..bb78741 100644
--- a/extras/eclipse.py
+++ b/waflib/extras/eclipse.py
diff --git a/extras/erlang.py b/waflib/extras/erlang.py
index 49f6d5b..49f6d5b 100644
--- a/extras/erlang.py
+++ b/waflib/extras/erlang.py
diff --git a/extras/fast_partial.py b/waflib/extras/fast_partial.py
index b3af513..b3af513 100644
--- a/extras/fast_partial.py
+++ b/waflib/extras/fast_partial.py
diff --git a/extras/fc_bgxlf.py b/waflib/extras/fc_bgxlf.py
index cca1810..cca1810 100644
--- a/extras/fc_bgxlf.py
+++ b/waflib/extras/fc_bgxlf.py
diff --git a/extras/fc_cray.py b/waflib/extras/fc_cray.py
index da733fa..da733fa 100644
--- a/extras/fc_cray.py
+++ b/waflib/extras/fc_cray.py
diff --git a/extras/fc_nag.py b/waflib/extras/fc_nag.py
index edcb218..edcb218 100644
--- a/extras/fc_nag.py
+++ b/waflib/extras/fc_nag.py
diff --git a/extras/fc_nec.py b/waflib/extras/fc_nec.py
index 67c8680..67c8680 100644
--- a/extras/fc_nec.py
+++ b/waflib/extras/fc_nec.py
diff --git a/extras/fc_open64.py b/waflib/extras/fc_open64.py
index 413719f..413719f 100644
--- a/extras/fc_open64.py
+++ b/waflib/extras/fc_open64.py
diff --git a/extras/fc_pgfortran.py b/waflib/extras/fc_pgfortran.py
index afb2817..afb2817 100644
--- a/extras/fc_pgfortran.py
+++ b/waflib/extras/fc_pgfortran.py
diff --git a/extras/fc_solstudio.py b/waflib/extras/fc_solstudio.py
index 53766df..53766df 100644
--- a/extras/fc_solstudio.py
+++ b/waflib/extras/fc_solstudio.py
diff --git a/extras/fc_xlf.py b/waflib/extras/fc_xlf.py
index 5a3da03..5a3da03 100644
--- a/extras/fc_xlf.py
+++ b/waflib/extras/fc_xlf.py
diff --git a/extras/file_to_object.py b/waflib/extras/file_to_object.py
index 1393b51..1393b51 100644
--- a/extras/file_to_object.py
+++ b/waflib/extras/file_to_object.py
diff --git a/extras/fluid.py b/waflib/extras/fluid.py
index 4814a35..4814a35 100644
--- a/extras/fluid.py
+++ b/waflib/extras/fluid.py
diff --git a/extras/freeimage.py b/waflib/extras/freeimage.py
index f27e525..f27e525 100644
--- a/extras/freeimage.py
+++ b/waflib/extras/freeimage.py
diff --git a/extras/fsb.py b/waflib/extras/fsb.py
index 1b8f398..1b8f398 100644
--- a/extras/fsb.py
+++ b/waflib/extras/fsb.py
diff --git a/extras/fsc.py b/waflib/extras/fsc.py
index c67e70b..c67e70b 100644
--- a/extras/fsc.py
+++ b/waflib/extras/fsc.py
diff --git a/extras/gccdeps.py b/waflib/extras/gccdeps.py
index d9758ab..d9758ab 100644
--- a/extras/gccdeps.py
+++ b/waflib/extras/gccdeps.py
diff --git a/extras/gdbus.py b/waflib/extras/gdbus.py
index 0e0476e..0e0476e 100644
--- a/extras/gdbus.py
+++ b/waflib/extras/gdbus.py
diff --git a/extras/gob2.py b/waflib/extras/gob2.py
index b4fa3b9..b4fa3b9 100644
--- a/extras/gob2.py
+++ b/waflib/extras/gob2.py
diff --git a/extras/halide.py b/waflib/extras/halide.py
index 6078e38..6078e38 100644
--- a/extras/halide.py
+++ b/waflib/extras/halide.py
diff --git a/extras/javatest.py b/waflib/extras/javatest.py
index 979b8d8..979b8d8 100755
--- a/extras/javatest.py
+++ b/waflib/extras/javatest.py
diff --git a/extras/kde4.py b/waflib/extras/kde4.py
index e49a9ec..e49a9ec 100644
--- a/extras/kde4.py
+++ b/waflib/extras/kde4.py
diff --git a/extras/local_rpath.py b/waflib/extras/local_rpath.py
index b2507e1..b2507e1 100644
--- a/extras/local_rpath.py
+++ b/waflib/extras/local_rpath.py
diff --git a/extras/lv2.py b/waflib/extras/lv2.py
index 815987f..815987f 100644
--- a/extras/lv2.py
+++ b/waflib/extras/lv2.py
diff --git a/extras/make.py b/waflib/extras/make.py
index 933d9ca..933d9ca 100644
--- a/extras/make.py
+++ b/waflib/extras/make.py
diff --git a/extras/midl.py b/waflib/extras/midl.py
index 43e6cf9..43e6cf9 100644
--- a/extras/midl.py
+++ b/waflib/extras/midl.py
diff --git a/extras/msvcdeps.py b/waflib/extras/msvcdeps.py
index fc1ecd4..fc1ecd4 100644
--- a/extras/msvcdeps.py
+++ b/waflib/extras/msvcdeps.py
diff --git a/extras/msvs.py b/waflib/extras/msvs.py
index 8aa2db0..8aa2db0 100644
--- a/extras/msvs.py
+++ b/waflib/extras/msvs.py
diff --git a/extras/netcache_client.py b/waflib/extras/netcache_client.py
index dc49048..dc49048 100644
--- a/extras/netcache_client.py
+++ b/waflib/extras/netcache_client.py
diff --git a/extras/objcopy.py b/waflib/extras/objcopy.py
index 82d8359..82d8359 100644
--- a/extras/objcopy.py
+++ b/waflib/extras/objcopy.py
diff --git a/extras/ocaml.py b/waflib/extras/ocaml.py
index afe73c0..afe73c0 100644
--- a/extras/ocaml.py
+++ b/waflib/extras/ocaml.py
diff --git a/extras/package.py b/waflib/extras/package.py
index c06498e..c06498e 100644
--- a/extras/package.py
+++ b/waflib/extras/package.py
diff --git a/extras/parallel_debug.py b/waflib/extras/parallel_debug.py
index 35883a3..35883a3 100644
--- a/extras/parallel_debug.py
+++ b/waflib/extras/parallel_debug.py
diff --git a/extras/pch.py b/waflib/extras/pch.py
index 103e752..103e752 100644
--- a/extras/pch.py
+++ b/waflib/extras/pch.py
diff --git a/extras/pep8.py b/waflib/extras/pep8.py
index 676beed..676beed 100644
--- a/extras/pep8.py
+++ b/waflib/extras/pep8.py
diff --git a/extras/pgicc.py b/waflib/extras/pgicc.py
index 9790b9c..9790b9c 100644
--- a/extras/pgicc.py
+++ b/waflib/extras/pgicc.py
diff --git a/extras/pgicxx.py b/waflib/extras/pgicxx.py
index eae121c..eae121c 100644
--- a/extras/pgicxx.py
+++ b/waflib/extras/pgicxx.py
diff --git a/extras/proc.py b/waflib/extras/proc.py
index 764abec..764abec 100644
--- a/extras/proc.py
+++ b/waflib/extras/proc.py
diff --git a/extras/protoc.py b/waflib/extras/protoc.py
index f3cb4d8..f3cb4d8 100644
--- a/extras/protoc.py
+++ b/waflib/extras/protoc.py
diff --git a/extras/pyqt5.py b/waflib/extras/pyqt5.py
index c21dfa7..c21dfa7 100644
--- a/extras/pyqt5.py
+++ b/waflib/extras/pyqt5.py
diff --git a/extras/pytest.py b/waflib/extras/pytest.py
index 7dd5a1a..7dd5a1a 100644
--- a/extras/pytest.py
+++ b/waflib/extras/pytest.py
diff --git a/extras/qnxnto.py b/waflib/extras/qnxnto.py
index 1158124..1158124 100644
--- a/extras/qnxnto.py
+++ b/waflib/extras/qnxnto.py
diff --git a/extras/qt4.py b/waflib/extras/qt4.py
index 90cae7e..90cae7e 100644
--- a/extras/qt4.py
+++ b/waflib/extras/qt4.py
diff --git a/extras/relocation.py b/waflib/extras/relocation.py
index 7e821f4..7e821f4 100644
--- a/extras/relocation.py
+++ b/waflib/extras/relocation.py
diff --git a/extras/remote.py b/waflib/extras/remote.py
index 3b038f7..3b038f7 100644
--- a/extras/remote.py
+++ b/waflib/extras/remote.py
diff --git a/extras/resx.py b/waflib/extras/resx.py
index caf4d31..caf4d31 100644
--- a/extras/resx.py
+++ b/waflib/extras/resx.py
diff --git a/extras/review.py b/waflib/extras/review.py
index 561e062..561e062 100644
--- a/extras/review.py
+++ b/waflib/extras/review.py
diff --git a/extras/rst.py b/waflib/extras/rst.py
index f3c3a5e..f3c3a5e 100644
--- a/extras/rst.py
+++ b/waflib/extras/rst.py
diff --git a/extras/run_do_script.py b/waflib/extras/run_do_script.py
index f3c5812..f3c5812 100644
--- a/extras/run_do_script.py
+++ b/waflib/extras/run_do_script.py
diff --git a/extras/run_m_script.py b/waflib/extras/run_m_script.py
index b5f27eb..b5f27eb 100644
--- a/extras/run_m_script.py
+++ b/waflib/extras/run_m_script.py
diff --git a/extras/run_py_script.py b/waflib/extras/run_py_script.py
index 3670381..3670381 100644
--- a/extras/run_py_script.py
+++ b/waflib/extras/run_py_script.py
diff --git a/extras/run_r_script.py b/waflib/extras/run_r_script.py
index b0d8f2b..b0d8f2b 100644
--- a/extras/run_r_script.py
+++ b/waflib/extras/run_r_script.py
diff --git a/extras/sas.py b/waflib/extras/sas.py
index 754c614..754c614 100644
--- a/extras/sas.py
+++ b/waflib/extras/sas.py
diff --git a/extras/satellite_assembly.py b/waflib/extras/satellite_assembly.py
index 005eb07..005eb07 100644
--- a/extras/satellite_assembly.py
+++ b/waflib/extras/satellite_assembly.py
diff --git a/extras/scala.py b/waflib/extras/scala.py
index a9880f0..a9880f0 100644
--- a/extras/scala.py
+++ b/waflib/extras/scala.py
diff --git a/extras/slow_qt4.py b/waflib/extras/slow_qt4.py
index ec7880b..ec7880b 100644
--- a/extras/slow_qt4.py
+++ b/waflib/extras/slow_qt4.py
diff --git a/extras/softlink_libs.py b/waflib/extras/softlink_libs.py
index 50c777f..50c777f 100644
--- a/extras/softlink_libs.py
+++ b/waflib/extras/softlink_libs.py
diff --git a/extras/stale.py b/waflib/extras/stale.py
index cac3f46..cac3f46 100644
--- a/extras/stale.py
+++ b/waflib/extras/stale.py
diff --git a/extras/stracedeps.py b/waflib/extras/stracedeps.py
index 37d82cb..37d82cb 100644
--- a/extras/stracedeps.py
+++ b/waflib/extras/stracedeps.py
diff --git a/extras/swig.py b/waflib/extras/swig.py
index fd3d6d2..fd3d6d2 100644
--- a/extras/swig.py
+++ b/waflib/extras/swig.py
diff --git a/extras/syms.py b/waflib/extras/syms.py
index dfa0059..dfa0059 100644
--- a/extras/syms.py
+++ b/waflib/extras/syms.py
diff --git a/extras/ticgt.py b/waflib/extras/ticgt.py
index f43a7ea..f43a7ea 100644
--- a/extras/ticgt.py
+++ b/waflib/extras/ticgt.py
diff --git a/extras/unity.py b/waflib/extras/unity.py
index 78128ed..78128ed 100644
--- a/extras/unity.py
+++ b/waflib/extras/unity.py
diff --git a/extras/use_config.py b/waflib/extras/use_config.py
index ef5129f..ef5129f 100644
--- a/extras/use_config.py
+++ b/waflib/extras/use_config.py
diff --git a/extras/valadoc.py b/waflib/extras/valadoc.py
index c50f69e..c50f69e 100644
--- a/extras/valadoc.py
+++ b/waflib/extras/valadoc.py
diff --git a/extras/waf_xattr.py b/waflib/extras/waf_xattr.py
index 351dd63..351dd63 100644
--- a/extras/waf_xattr.py
+++ b/waflib/extras/waf_xattr.py
diff --git a/extras/why.py b/waflib/extras/why.py
index 1bb941f..1bb941f 100644
--- a/extras/why.py
+++ b/waflib/extras/why.py
diff --git a/extras/win32_opts.py b/waflib/extras/win32_opts.py
index 9f7443c..9f7443c 100644
--- a/extras/win32_opts.py
+++ b/waflib/extras/win32_opts.py
diff --git a/extras/wix.py b/waflib/extras/wix.py
index d87bfbb..d87bfbb 100644
--- a/extras/wix.py
+++ b/waflib/extras/wix.py
diff --git a/extras/xcode6.py b/waflib/extras/xcode6.py
index 91bbff1..91bbff1 100644
--- a/extras/xcode6.py
+++ b/waflib/extras/xcode6.py
diff --git a/fixpy2.py b/waflib/fixpy2.py
index 24176e0..24176e0 100644
--- a/fixpy2.py
+++ b/waflib/fixpy2.py
diff --git a/processor.py b/waflib/processor.py
index 2eecf3b..2eecf3b 100755
--- a/processor.py
+++ b/waflib/processor.py
diff --git a/waflib/waf b/waflib/waf
new file mode 100755
index 0000000..e22930a
--- /dev/null
+++ b/waflib/waf
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+# Minimal waf script for projects that include waflib directly
+
+from waflib import Context, Scripting
+
+import inspect
+import os
+
+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/wscript b/wscript
new file mode 100644
index 0000000..a6d9347
--- /dev/null
+++ b/wscript
@@ -0,0 +1,495 @@
+#!/usr/bin/env python
+
+import os
+import shutil
+import subprocess
+import sys
+
+from waflib import 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.5'
+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
+
+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')
+ autowaf.set_options(ctx, test=True)
+ opt = ctx.get_option_group('Configuration options')
+ autowaf.add_flags(
+ opt,
+ {'no-utils': 'do not build command line utilities',
+ 'bindings': '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):
+ autowaf.display_header('Lilv Configuration')
+ conf.load('compiler_c', cache=True)
+ try:
+ conf.load('compiler_cxx', cache=True)
+ except:
+ pass
+
+ if Options.options.bindings:
+ try:
+ conf.load('python', cache=True)
+ conf.check_python_headers()
+ autowaf.define(conf, 'LILV_PYTHON', 1);
+ except:
+ Logs.warn('Failed to configure Python (%s)\n' % sys.exc_info()[1])
+
+ conf.load('autowaf', cache=True)
+ autowaf.set_c_lang(conf, 'c99')
+
+ 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')
+
+ autowaf.check_pkg(conf, 'lv2', uselib_store='LV2',
+ atleast_version='1.14.0', mandatory=True)
+ autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD',
+ atleast_version='0.18.0', mandatory=True)
+ autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD',
+ atleast_version='0.14.0', mandatory=True)
+ autowaf.check_pkg(conf, 'sratom-0', uselib_store='SRATOM',
+ atleast_version='0.4.0', mandatory=True)
+ autowaf.check_pkg(conf, 'sndfile', uselib_store='SNDFILE',
+ atleast_version='1.0.0', 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 = []
+
+ autowaf.check_function(conf, 'c', 'lstat',
+ header_name = ['sys/stat.h'],
+ defines = defines,
+ define_name = 'HAVE_LSTAT',
+ mandatory = False)
+
+ autowaf.check_function(conf, 'c', 'flock',
+ header_name = 'sys/file.h',
+ defines = defines,
+ define_name = 'HAVE_FLOCK',
+ mandatory = False)
+
+ autowaf.check_function(conf, 'c', 'fileno',
+ header_name = 'stdio.h',
+ defines = defines,
+ define_name = 'HAVE_FILENO',
+ mandatory = False)
+
+ autowaf.check_function(conf, '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,
+ mandatory = False)
+
+ conf.check_cc(define_name = 'HAVE_LIBDL',
+ lib = 'dl',
+ mandatory = False)
+
+ if Options.options.dyn_manifest:
+ autowaf.define(conf, 'LILV_DYN_MANIFEST', 1)
+
+ lilv_path_sep = ':'
+ lilv_dir_sep = '/'
+ if conf.env.DEST_OS == 'win32':
+ lilv_path_sep = ';'
+ lilv_dir_sep = '\\\\'
+
+ autowaf.define(conf, 'LILV_PATH_SEP', lilv_path_sep)
+ autowaf.define(conf, '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])
+ autowaf.define(conf, 'LILV_DEFAULT_LV2_PATH', lv2_path)
+
+ autowaf.set_lib_env(conf, 'lilv', LILV_VERSION)
+ conf.write_config_header('lilv_config.h', remove=False)
+
+ autowaf.display_summary(
+ conf,
+ {'Default LV2_PATH': conf.env.LILV_DEFAULT_LV2_PATH,
+ 'Utilities': bool(conf.env.BUILD_UTILS),
+ 'Unit tests': bool(conf.env.BUILD_TESTS),
+ 'Dynamic manifest support': bool(conf.env.LILV_DYN_MANIFEST),
+ 'Python bindings': conf.is_defined('LILV_PYTHON')})
+
+ conf.undefine('LILV_DEFAULT_LV2_PATH') # Cmd line errors with VC++
+
+def build_util(bld, name, defines, libs=''):
+ obj = bld(features = 'c cprogram',
+ source = name + '.c',
+ includes = ['.', './src', './utils'],
+ use = 'liblilv',
+ target = name,
+ defines = defines,
+ install_path = '${BINDIR}')
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2 ' + libs)
+ 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('lilv/*.h'))
+ bld.install_files(includedir, bld.path.ant_glob('lilv/*.hpp'))
+
+ lib_source = '''
+ src/collections.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 = ['.'],
+ source = lib_source,
+ includes = ['.', './src'],
+ name = 'liblilv',
+ target = 'lilv-%s' % LILV_MAJOR_VERSION,
+ vnum = LILV_VERSION,
+ install_path = '${LIBDIR}',
+ defines = ['LILV_SHARED', 'LILV_INTERNAL'],
+ cflags = libflags,
+ lib = lib)
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ # Static library
+ if bld.env.BUILD_STATIC:
+ obj = bld(features = 'c cstlib',
+ export_includes = ['.'],
+ source = lib_source,
+ includes = ['.', './src'],
+ name = 'liblilv_static',
+ target = 'lilv-%s' % LILV_MAJOR_VERSION,
+ vnum = LILV_VERSION,
+ install_path = '${LIBDIR}',
+ defines = defines + ['LILV_INTERNAL'])
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ # Python bindings
+ if bld.is_defined('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']
+
+ # 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,
+ 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 = ['.', './src'],
+ use = 'liblilv_profiled',
+ install_path = None,
+ defines = defines,
+ cflags = test_cflags,
+ linkflags = test_linkflags,
+ lib = test_libs,
+ uselib = 'LV2')
+ autowaf.use_lib(bld, obj, '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 = ['.', './src'],
+ name = 'liblilv_profiled',
+ target = 'lilv_profiled',
+ install_path = None,
+ defines = defines + ['LILV_INTERNAL'],
+ cflags = test_cflags,
+ linkflags = test_linkflags,
+ lib = test_libs)
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ # Unit test program
+ testdir = os.path.abspath(autowaf.build_dir(APPNAME, 'test'))
+ bpath = os.path.join(testdir, 'test.lv2')
+ bpath = bpath.replace('\\', '/')
+ testdir = testdir.replace('\\', '/')
+ obj = bld(features = 'c cprogram',
+ source = 'test/lilv_test.c',
+ includes = ['.', './src'],
+ use = 'liblilv_profiled',
+ lib = test_libs,
+ target = 'test/lilv_test',
+ install_path = None,
+ defines = (defines + ['LILV_TEST_BUNDLE=\"%s/\"' % bpath] +
+ ['LILV_TEST_DIR=\"%s/\"' % testdir]),
+ cflags = test_cflags,
+ linkflags = test_linkflags)
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ # C++ API test
+ if 'COMPILER_CXX' in bld.env:
+ obj = bld(features = 'cxx cxxprogram',
+ source = 'test/lilv_cxx_test.cpp',
+ includes = ['.', './src'],
+ use = 'liblilv_profiled',
+ lib = test_libs,
+ target = 'test/lilv_cxx_test',
+ install_path = None,
+ cxxflags = test_cflags,
+ linkflags = test_linkflags)
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ if bld.is_defined('LILV_PYTHON'):
+ # 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 = 'bindings/bindings_test_plugin.lv2/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 = 'bindings/bindings_test_plugin.lv2/' + 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.is_defined('HAVE_CLOCK_GETTIME') and not bld.env.STATIC_PROGS:
+ obj = build_util(bld, 'utils/lv2bench', defines)
+ if bld.env.DEST_OS != 'win32' and bld.env.DEST_OS != 'darwin':
+ obj.lib = ['rt']
+
+ # Documentation
+ autowaf.build_dox(bld, 'LILV', LILV_VERSION, top, out)
+
+ # 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)
+ if bld.env.DOCS:
+ bld.add_post_fun(fix_docs)
+
+def fix_docs(ctx):
+ if ctx.cmd == 'build':
+ autowaf.make_simple_dox(APPNAME)
+
+def upload_docs(ctx):
+ import glob
+ os.system('rsync -ravz --delete -e ssh build/doc/html/ drobilla@drobilla.net:~/drobilla.net/docs/lilv/')
+ for page in glob.glob('doc/*.[1-8]'):
+ os.system('soelim %s | pre-grohtml troff -man -wall -Thtml | post-grohtml > build/%s.html' % (page, page))
+ os.system('rsync -avz --delete -e ssh build/%s.html drobilla@drobilla.net:~/drobilla.net/man/' % page)
+
+def test(ctx):
+ assert ctx.env.BUILD_TESTS, "You have run waf configure without the --test flag. No tests were run."
+ autowaf.pre_test(ctx, APPNAME)
+ if ctx.is_defined('LILV_PYTHON'):
+ os.environ['LD_LIBRARY_PATH'] = os.getcwd()
+ autowaf.run_tests(ctx, APPNAME, ['python -m unittest discover bindings/'])
+ os.environ['PATH'] = 'test' + os.pathsep + os.getenv('PATH')
+
+ Logs.pprint('GREEN', '')
+ autowaf.run_test(ctx, APPNAME, 'lilv_test', dirs=['./src','./test'], name='lilv_test')
+
+ for p in test_plugins:
+ test_prog = 'test_' + p + ' ' + ('test/%s.lv2/' % p)
+ if os.path.exists('test/test_' + p):
+ autowaf.run_test(ctx, APPNAME, test_prog, 0,
+ dirs=['./src','./test','./test/%s.lv2' % p])
+
+ autowaf.post_test(ctx, APPNAME)
+ try:
+ shutil.rmtree('state')
+ except:
+ pass
+
+def lint(ctx):
+ "checks code for style issues"
+ import subprocess
+ cmd = ("clang-tidy -p=. -header-filter=.* -checks=\"*," +
+ "-clang-analyzer-alpha.*," +
+ "-google-readability-todo," +
+ "-llvm-header-guard," +
+ "-llvm-include-order," +
+ "-misc-unused-parameters," +
+ "-readability-else-after-return\" " +
+ "$(find .. -name '*.c')")
+ subprocess.call(cmd, cwd='build', shell=True)
+
+def posts(ctx):
+ path = str(ctx.path.abspath())
+ autowaf.news_to_posts(
+ os.path.join(path, 'NEWS'),
+ {'title' : 'Lilv',
+ 'description' : autowaf.get_blurb(os.path.join(path, 'README')),
+ 'dist_pattern' : 'http://download.drobilla.net/lilv-%s.tar.bz2'},
+ { 'Author' : 'drobilla',
+ 'Tags' : 'Hacking, LAD, LV2, Lilv' },
+ os.path.join(out, 'posts'))