diff options
-rw-r--r-- | .gitattributes | 1 | ||||
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | AUTHORS | 11 | ||||
-rw-r--r-- | COPYING | 38 | ||||
-rw-r--r-- | INSTALL | 66 | ||||
-rw-r--r-- | NEWS | 231 | ||||
-rw-r--r-- | README | 8 | ||||
-rw-r--r-- | bindings/lilv.i | 66 | ||||
-rw-r--r-- | bindings/numpy.i | 1746 | ||||
-rw-r--r-- | bindings/python/Makefile | 186 | ||||
-rw-r--r-- | bindings/python/conf.py | 263 | ||||
-rw-r--r-- | bindings/python/index.rst | 9 | ||||
-rw-r--r-- | bindings/python/lilv.py | 1775 | ||||
-rwxr-xr-x | bindings/python/lv2_apply.py | 159 | ||||
-rwxr-xr-x | bindings/python/lv2_list.py | 9 | ||||
-rw-r--r-- | bindings/test/bindings_test_plugin.c | 196 | ||||
-rw-r--r-- | bindings/test/bindings_test_plugin.ttl.in | 62 | ||||
-rw-r--r-- | bindings/test/manifest.ttl.in | 7 | ||||
-rw-r--r-- | bindings/test/python/test_api.py | 290 | ||||
-rw-r--r-- | doc/layout.xml | 187 | ||||
-rw-r--r-- | doc/lv2apply.1 | 34 | ||||
-rw-r--r-- | doc/lv2info.1 | 33 | ||||
-rw-r--r-- | doc/lv2ls.1 | 30 | ||||
-rw-r--r-- | doc/reference.doxygen.in | 2415 | ||||
-rw-r--r-- | doc/style.css | 691 | ||||
-rw-r--r-- | lilv.pc.in | 11 | ||||
-rw-r--r-- | lilv.ttl | 29 | ||||
-rw-r--r-- | lilv/lilv.h | 1844 | ||||
-rw-r--r-- | lilv/lilvmm.hpp | 345 | ||||
-rw-r--r-- | src/collections.c | 224 | ||||
-rw-r--r-- | src/instance.c | 110 | ||||
-rw-r--r-- | src/lib.c | 112 | ||||
-rw-r--r-- | src/lilv_internal.h | 445 | ||||
-rw-r--r-- | src/node.c | 397 | ||||
-rw-r--r-- | src/plugin.c | 1138 | ||||
-rw-r--r-- | src/pluginclass.c | 88 | ||||
-rw-r--r-- | src/port.c | 268 | ||||
-rw-r--r-- | src/query.c | 139 | ||||
-rw-r--r-- | src/scalepoint.c | 49 | ||||
-rw-r--r-- | src/state.c | 1352 | ||||
-rw-r--r-- | src/ui.c | 111 | ||||
-rw-r--r-- | src/util.c | 659 | ||||
-rw-r--r-- | src/world.c | 1212 | ||||
-rw-r--r-- | src/zix/common.h | 88 | ||||
-rw-r--r-- | src/zix/tree.c | 716 | ||||
-rw-r--r-- | src/zix/tree.h | 148 | ||||
-rw-r--r-- | test/bad_syntax.lv2/bad_syntax.c | 93 | ||||
-rw-r--r-- | test/bad_syntax.lv2/bad_syntax.ttl.in | 22 | ||||
-rw-r--r-- | test/bad_syntax.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/bad_syntax.lv2/test_bad_syntax.c | 45 | ||||
-rw-r--r-- | test/failed_instantiation.lv2/failed_instantiation.c | 70 | ||||
-rw-r--r-- | test/failed_instantiation.lv2/failed_instantiation.ttl.in | 40 | ||||
-rw-r--r-- | test/failed_instantiation.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/failed_instantiation.lv2/test_failed_instantiation.c | 45 | ||||
-rw-r--r-- | test/failed_lib_descriptor.lv2/failed_lib_descriptor.c | 30 | ||||
-rw-r--r-- | test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in | 38 | ||||
-rw-r--r-- | test/failed_lib_descriptor.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c | 46 | ||||
-rw-r--r-- | test/lib_descriptor.lv2/lib_descriptor.c | 112 | ||||
-rw-r--r-- | test/lib_descriptor.lv2/lib_descriptor.ttl.in | 41 | ||||
-rw-r--r-- | test/lib_descriptor.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/lib_descriptor.lv2/test_lib_descriptor.c | 59 | ||||
-rw-r--r-- | test/lilv_cxx_test.cpp | 23 | ||||
-rw-r--r-- | test/lilv_test.c | 2299 | ||||
-rw-r--r-- | test/missing_descriptor.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_descriptor.lv2/missing_descriptor.c | 21 | ||||
-rw-r--r-- | test/missing_descriptor.lv2/missing_descriptor.ttl.in | 38 | ||||
-rw-r--r-- | test/missing_descriptor.lv2/test_missing_descriptor.c | 46 | ||||
-rw-r--r-- | test/missing_name.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_name.lv2/missing_name.c | 93 | ||||
-rw-r--r-- | test/missing_name.lv2/missing_name.ttl.in | 37 | ||||
-rw-r--r-- | test/missing_name.lv2/test_missing_name.c | 47 | ||||
-rw-r--r-- | test/missing_plugin.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_plugin.lv2/missing_plugin.c | 43 | ||||
-rw-r--r-- | test/missing_plugin.lv2/missing_plugin.ttl.in | 38 | ||||
-rw-r--r-- | test/missing_plugin.lv2/test_missing_plugin.c | 46 | ||||
-rw-r--r-- | test/missing_port.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_port.lv2/missing_port.c | 93 | ||||
-rw-r--r-- | test/missing_port.lv2/missing_port.ttl.in | 31 | ||||
-rw-r--r-- | test/missing_port.lv2/test_missing_port.c | 45 | ||||
-rw-r--r-- | test/missing_port_name.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_port_name.lv2/missing_port_name.c | 93 | ||||
-rw-r--r-- | test/missing_port_name.lv2/missing_port_name.ttl.in | 30 | ||||
-rw-r--r-- | test/missing_port_name.lv2/test_missing_port_name.c | 49 | ||||
-rw-r--r-- | test/new_version.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/new_version.lv2/new_version.c | 93 | ||||
-rw-r--r-- | test/new_version.lv2/new_version.ttl.in | 40 | ||||
-rw-r--r-- | test/old_version.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/old_version.lv2/old_version.c | 93 | ||||
-rw-r--r-- | test/old_version.lv2/old_version.ttl.in | 40 | ||||
-rw-r--r-- | test/test.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/test.lv2/test.c | 394 | ||||
-rw-r--r-- | test/test.lv2/test.ttl.in | 46 | ||||
-rw-r--r-- | utils/bench.h | 52 | ||||
-rw-r--r-- | utils/lilv-bench.c | 38 | ||||
-rw-r--r-- | utils/lilv.bash_completion | 59 | ||||
-rw-r--r-- | utils/lv2apply.c | 354 | ||||
-rw-r--r-- | utils/lv2bench.c | 268 | ||||
-rw-r--r-- | utils/lv2info.c | 460 | ||||
-rw-r--r-- | utils/lv2ls.c | 93 | ||||
-rw-r--r-- | utils/uri_table.h | 73 | ||||
-rw-r--r-- | waflib/.gitignore | 2 | ||||
-rw-r--r-- | waflib/Build.py (renamed from Build.py) | 0 | ||||
-rw-r--r-- | waflib/COPYING | 25 | ||||
-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-x | waflib/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-x | waflib/processor.py (renamed from processor.py) | 0 | ||||
-rwxr-xr-x | waflib/waf | 16 | ||||
-rw-r--r-- | wscript | 495 |
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 @@ -1,2 +1,3 @@ -__pycache__ -*.pyc +build/** +.waf-* +.lock-waf* @@ -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 @@ -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 @@ -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 @@ -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 @@ -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><http://example.org/foo ></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><plugin-uri> 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 ∈ + } 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/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/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 feaae3c..feaae3c 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() @@ -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')) |