diff options
198 files changed, 1298 insertions, 7172 deletions
diff --git a/.clang-format b/.clang-format index 8ee2675..c40ad9c 100644 --- a/.clang-format +++ b/.clang-format @@ -1,37 +1,38 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> +# Copyright 2020-2024 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC --- AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: true -AlignEscapedNewlinesLeft: true +AlignEscapedNewlines: Left +AttributeMacros: + - LV2_DEPRECATED + - LV2_SYMBOL_EXPORT BasedOnStyle: Mozilla BraceWrapping: - AfterNamespace: false AfterClass: true AfterEnum: false AfterExternBlock: false AfterFunction: true + AfterNamespace: false AfterStruct: false SplitEmptyFunction: false SplitEmptyRecord: false BreakBeforeBraces: Custom Cpp11BracedListStyle: true +ForEachMacros: + - LV2_ATOM_OBJECT_BODY_FOREACH + - LV2_ATOM_OBJECT_FOREACH + - LV2_ATOM_SEQUENCE_BODY_FOREACH + - LV2_ATOM_SEQUENCE_FOREACH + - LV2_ATOM_TUPLE_BODY_FOREACH + - LV2_ATOM_TUPLE_FOREACH IndentCaseLabels: false IndentPPDirectives: AfterHash KeepEmptyLinesAtTheStartOfBlocks: false SpacesInContainerLiterals: false StatementMacros: - - LV2_DEPRECATED - LV2_DISABLE_DEPRECATION_WARNINGS - LV2_RESTORE_WARNINGS - - LV2_SYMBOL_EXPORT - _Pragma -ForEachMacros: - - LV2_ATOM_OBJECT_BODY_FOREACH - - LV2_ATOM_OBJECT_FOREACH - - LV2_ATOM_SEQUENCE_BODY_FOREACH - - LV2_ATOM_SEQUENCE_FOREACH - - LV2_ATOM_TUPLE_BODY_FOREACH - - LV2_ATOM_TUPLE_FOREACH ... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..f8c3249 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,19 @@ +# Copyright 2020-2025 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +Checks: > + *, + -*-macro-to-enum, + -*-magic-numbers, + -altera-*, + -bugprone-easily-swappable-parameters, + -llvmlibc-*, + -readability-identifier-length, +CheckOptions: + - key: hicpp-uppercase-literal-suffix.NewSuffixes + value: L;U;f + - key: readability-uppercase-literal-suffix.NewSuffixes + value: L;U;f +FormatStyle: file +HeaderFilterRegex: 'lv2/.*\.h$' +WarningsAsErrors: '*' diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..6f45828 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,5 @@ +# Copyright 2025 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +# Format all code with clang-format +882b9446cbf7316345de391188e68c2a7333da5b @@ -10,3 +10,6 @@ *.swp cscope.* tags + +# cached files when used as a meson subproject +.meson-subproject-wrap-hash.txt diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 82be218..a42bbeb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,168 +1,141 @@ -# Copyright 2018-2022 David Robillard <d@drobilla.net> +# Copyright 2018-2023 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC -arm32_dbg: - image: lv2plugin/debian-arm32 - script: - - meson setup build --cross-file=/usr/share/meson/cross/arm-linux-gnueabihf.ini -Dbuildtype=debug -Dstrict=true -Dwerror=true -Ddocs=disabled - - ninja -C build test - -arm32_rel: - image: lv2plugin/debian-arm32 - script: - - meson setup build --cross-file=/usr/share/meson/cross/arm-linux-gnueabihf.ini -Dbuildtype=release -Dstrict=true -Dwerror=true -Ddocs=disabled - - ninja -C build test - - -arm64_dbg: - image: lv2plugin/debian-arm64 - script: - - meson setup build --cross-file=/usr/share/meson/cross/aarch64-linux-gnu.ini -Dbuildtype=debug -Dstrict=true -Dwerror=true -Ddocs=disabled - - ninja -C build test - -arm64_rel: - image: lv2plugin/debian-arm64 - script: - - meson setup build --cross-file=/usr/share/meson/cross/aarch64-linux-gnu.ini -Dbuildtype=release -Dstrict=true -Dwerror=true -Ddocs=disabled - - ninja -C build test - - -x32_dbg: - image: lv2plugin/debian-x32 - script: - - meson setup build --cross-file=/usr/share/meson/cross/i686-linux-gnu.ini -Dbuildtype=debug -Dstrict=true -Dwerror=true -Ddocs=disabled - - ninja -C build test - -x32_rel: - image: lv2plugin/debian-x32 +default: + image: lv2plugin/debian-x64 script: - - meson setup build --cross-file=/usr/share/meson/cross/i686-linux-gnu.ini -Dbuildtype=release -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson setup build -Dwerror=true - ninja -C build test - -x64_dbg: - image: lv2plugin/debian-x64 +dev: + image: lv2plugin/debian-x64-big script: - - meson setup build -Dbuildtype=debug -Dstrict=true -Dwerror=true -Db_coverage=true + - meson setup build -Dbuildtype=debug -Dwarning_level=3 -Dwerror=true -Db_coverage=true -Dlint=true - ninja -C build test - ninja -C build coverage-html + - meson configure -Dbuildtype=release -Db_coverage=false build + - ninja -C build test coverage: '/ *lines\.*: \d+\.\d+.*/' artifacts: paths: - build/meson-logs/coveragereport -x64_rel: +static: image: lv2plugin/debian-x64 script: - - meson setup build -Dbuildtype=release -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson setup build -Ddefault_library=static -Dwarning_level=3 -Dwerror=true -Ddocs=disabled - ninja -C build test - -x64_static: - image: lv2plugin/debian-x64 - script: - - meson setup build -Ddefault_library=static -Dstrict=true -Dwerror=true -Ddocs=disabled - - ninja -C build test - - -x64_sanitize: +sanitize: image: lv2plugin/debian-x64-clang script: - - meson setup build -Db_lundef=false -Dbuildtype=plain -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson setup build -Db_lundef=false -Dbuildtype=plain -Dwarning_level=3 -Dwerror=true -Ddocs=disabled - ninja -C build test variables: CC: "clang" CFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" LDFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" +# Linux Distributions -freebsd_dbg: - tags: [freebsd,meson] +fedora: + image: lv2plugin/fedora-big script: - - meson setup build -Dbuildtype=debug -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson setup build -Dbuildtype=plain -Dwarning_level=3 -Ddocs=enabled -Dwerror=true - ninja -C build test + variables: + CFLAGS: -O2 -D_FORTIFY_SOURCE=2 + artifacts: + paths: + - build/doc -freebsd_rel: - tags: [freebsd,meson] +# Linux Platforms + +arm32: + image: lv2plugin/debian-arm32 script: - - meson setup build -Dbuildtype=release -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson setup build --cross-file=/usr/share/meson/cross/arm-linux-gnueabihf.ini -Dbuildtype=debug -Dwarning_level=3 -Dwerror=true -Ddocs=disabled + - ninja -C build test + - meson configure -Dbuildtype=release build - ninja -C build test - -mingw32_dbg: - image: lv2plugin/debian-mingw32 +arm64: + image: lv2plugin/debian-arm64 script: - - meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=debug -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson setup build --cross-file=/usr/share/meson/cross/aarch64-linux-gnu.ini -Dbuildtype=debug -Dwarning_level=3 -Dwerror=true -Ddocs=disabled + - ninja -C build test + - meson configure -Dbuildtype=release build - ninja -C build test -mingw32_rel: +mingw32: image: lv2plugin/debian-mingw32 script: - - meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=release -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=debug -Dwarning_level=everything -Dwerror=true -Ddocs=disabled - ninja -C build test - - -mingw64_dbg: - image: lv2plugin/debian-mingw64 - script: - - meson setup build --cross-file=/usr/share/meson/cross/x86_64-w64-mingw32.ini -Dbuildtype=debug -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson configure -Dbuildtype=release build - ninja -C build test -mingw64_rel: +mingw64: image: lv2plugin/debian-mingw64 script: - - meson setup build --cross-file=/usr/share/meson/cross/x86_64-w64-mingw32.ini -Dbuildtype=release -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson setup build --cross-file=/usr/share/meson/cross/x86_64-w64-mingw32.ini -Dbuildtype=debug -Dwarning_level=3 -Dwerror=true -Ddocs=disabled + - ninja -C build test + - meson configure -Dbuildtype=release build - ninja -C build test - -mac_dbg: - tags: [macos] +wasm: + image: lv2plugin/debian-wasm script: - - meson setup build -Dbuildtype=debug -Dstrict=true -Dwerror=true + - meson setup build --cross-file=/usr/share/meson/cross/wasm.ini -Dbuildtype=debug -Dwarning_level=3 -Dwerror=true -Ddefault_library=static -Ddocs=disabled + - ninja -C build test + - meson configure -Dbuildtype=release build - ninja -C build test -mac_rel: - tags: [macos] +x32: + image: lv2plugin/debian-x32 script: - - meson setup build -Dbuildtype=release -Dstrict=true -Dwerror=true + - meson setup build --cross-file=/usr/share/meson/cross/i686-linux-gnu.ini -Dbuildtype=debug -Dwarning_level=3 -Dwerror=true -Ddocs=disabled + - ninja -C build test + - meson configure -Dbuildtype=release build - ninja -C build test +# Non-Linux/Docker rows (not hosted) -win_dbg: - tags: [windows,meson] +freebsd: + tags: [freebsd,meson] script: - - meson setup build -Dbuildtype=debug -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson setup build -Dbuildtype=debug -Dwarning_level=3 -Dwerror=true -Ddocs=disabled - ninja -C build test - -win_rel: - tags: [windows,meson] - script: - - meson setup build -Dbuildtype=release -Dstrict=true -Dwerror=true -Ddocs=disabled + - meson configure -Dbuildtype=release build - ninja -C build test - -wasm_dbg: - image: lv2plugin/debian-wasm +mac: + tags: [macos] script: - - meson setup build --cross-file=/usr/share/meson/cross/wasm.ini -Dbuildtype=debug -Dstrict=true -Dwerror=true -Ddefault_library=static -Ddocs=disabled -Dplugins=disabled + - meson setup build -Dbuildtype=debug -Dwarning_level=everything -Dwerror=true + - ninja -C build test + - meson configure -Dbuildtype=release build - ninja -C build test -wasm_rel: - image: lv2plugin/debian-wasm +win: + tags: [windows,meson] script: - - meson setup build --cross-file=/usr/share/meson/cross/wasm.ini -Dbuildtype=release -Dstrict=true -Dwerror=true -Ddefault_library=static -Ddocs=disabled -Dplugins=disabled + - meson setup build -Dbuildtype=debug -Dwarning_level=everything -Dwerror=true -Ddocs=disabled + - ninja -C build test + - meson configure -Dbuildtype=release build - ninja -C build test +# Documentation pages: stage: deploy script: - mkdir -p public - mv build/meson-logs/coveragereport/ public/coverage - dependencies: - - x64_dbg + needs: + - dev artifacts: paths: - public only: - - master + - main diff --git a/.reuse/dep5 b/.reuse/dep5 index 78cd3aa..5f805a7 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -26,14 +26,10 @@ Files: **/README.txt doc/index.html.in doc/c/*.html lv2specgen/template.html res Copyright: Copyright 2010-2022 David Robillard <d@drobilla.net> License: ISC -Files: plugins/eg-sampler.lv2/click.wav -Copyright: Copyright 2012 David Robillard <d@drobilla.net> -License: ISC - -Files: lv2/**/*.ttl plugins/**/*.ttl plugins/**/*.ttl.in schemas.lv2/xsd.ttl +Files: lv2/**/*.ttl schemas.lv2/xsd.ttl Copyright: Copyright 2011-2022 David Robillard <d@drobilla.net> -Copyright: Copyright 2013 Robin Gareus <robin@gareus.org> -Copyright: Copyright 2006 Steve Harris <steve@plugin.org.uk> + Copyright 2013 Robin Gareus <robin@gareus.org> + Copyright 2006 Steve Harris <steve@plugin.org.uk> License: ISC Files: lv2specgen/DTD/* @@ -47,12 +43,12 @@ License: 0BSD OR ISC Files: schemas.lv2/doap.ttl Copyright: Copyright 2004-2016 Edd Dumbill <edd@usefulinc.com> -Copyright: Copyright 2016-2017 Edd Wilder-James <edd@ewj.me> + Copyright 2016-2017 Edd Wilder-James <edd@ewj.me> License: Apache-2.0 Files: schemas.lv2/foaf.ttl -Copyright: Copyright 2000-2014 Dan Brickley <danbri@danbri.org> -Copyright: Copyright 2000-2014 Libby Miller <libby@nicecupoftea.org> +Copyright: 2000-2014 Dan Brickley <danbri@danbri.org> + 2000-2014 Libby Miller <libby@nicecupoftea.org> License: CC-BY-1.0 Files: schemas.lv2/rdf.ttl @@ -1,11 +1,26 @@ -lv2 (1.18.9) unstable; urgency=medium +lv2 (1.18.11) unstable; urgency=medium + + * Add configuration options to bundle, header, and tool installation + * Add lv2dir and lv2specdatadir package variables + * Allow LV2_SYMBOL_EXPORT to be overridden + * Avoid over-use of yielding meson options + * Fix pylint warning in test script + * Move example plugins to a separate project + * Override pkg-config dependency within meson + * Remove troublesome lv2_atom_assert_double_fits_in_64_bits + * eg-metro: Fix memory leak + * ui: Add types for Gtk4UI and Qt6UI + + -- David Robillard <d@drobilla.net> Thu, 13 Nov 2025 22:54:05 +0000 + +lv2 (1.18.10) stable; urgency=medium * Fix includedir in pkg-config file * Fix missing example plugin UI binaries * Only install lv2specgen when required Python modules are found * Replace change history data with a plain text NEWS file - -- David Robillard <d@drobilla.net> Wed, 07 Sep 2022 00:19:12 +0000 + -- David Robillard <d@drobilla.net> Fri, 09 Sep 2022 17:26:51 +0000 lv2 (1.18.8) stable; urgency=medium @@ -37,12 +37,12 @@ Other projects may extend LV2, but must place their headers elsewhere. Headers are installed to `includedir` with paths like: - #include "lv2/urid/urid.h" + #include <lv2/urid/urid.h> For backwards compatibility, if the `old_headers` option is set, then headers are also installed to the older URI-based paths: - #include "lv2/lv2plug.in/ns/ext/urid/urid.h" + #include <lv2/lv2plug.in/ns/ext/urid/urid.h> Projects still using this style are encourated to migrate to the shorter style above. diff --git a/doc/c/doxy-style.css b/doc/c/doxy-style.css index b44675e..6f15ee4 100644 --- a/doc/c/doxy-style.css +++ b/doc/c/doxy-style.css @@ -208,6 +208,10 @@ dl.el { font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed; } +.ttc { + display: none; +} + pre.fragment { border: 1px solid #C4C4C4; background-color: #F9F9F9; diff --git a/doc/c/meson.build b/doc/c/meson.build index 7285c60..2642c3a 100644 --- a/doc/c/meson.build +++ b/doc/c/meson.build @@ -1,8 +1,6 @@ # Copyright 2022 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC -lv2_source_doc = meson.current_source_dir() - if doxygen.found() reference_doxygen_in = files('reference.doxygen.in') @@ -11,7 +9,7 @@ if doxygen.found() 'LV2_SRCDIR': lv2_source_root, 'LV2_BUILDDIR': lv2_build_root, 'LV2_VERSION': meson.project_version(), - } + }, ) reference_doxygen = configure_file( diff --git a/doc/c/reference.doxygen.in b/doc/c/reference.doxygen.in index 0b87d49..333fe1c 100644 --- a/doc/c/reference.doxygen.in +++ b/doc/c/reference.doxygen.in @@ -805,6 +805,7 @@ INPUT = @LV2_SRCDIR@/doc/c/mainpage.md \ @LV2_SRCDIR@/include/lv2/atom/util.h \ @LV2_SRCDIR@/include/lv2/buf-size/buf-size.h \ @LV2_SRCDIR@/include/lv2/core/lv2.h \ + @LV2_SRCDIR@/include/lv2/core/lv2_util.h \ @LV2_SRCDIR@/include/lv2/data-access/data-access.h \ @LV2_SRCDIR@/include/lv2/dynmanifest/dynmanifest.h \ @LV2_SRCDIR@/include/lv2/event/event-helpers.h \ @@ -1206,15 +1207,6 @@ HTML_COLORSTYLE_SAT = 30 HTML_COLORSTYLE_GAMMA = 100 -# 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_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will @@ -1502,17 +1494,6 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT 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 # https://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 @@ -1822,14 +1803,6 @@ LATEX_HIDE_INDICES = NO 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 #--------------------------------------------------------------------------- @@ -2185,23 +2158,6 @@ HAVE_DOT = NO 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. @@ -2417,18 +2373,6 @@ DOT_GRAPH_MAX_NODES = 50 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 diff --git a/doc/ns/ext/meson.build b/doc/ns/ext/meson.build index c76f1a2..58aa40d 100644 --- a/doc/ns/ext/meson.build +++ b/doc/ns/ext/meson.build @@ -1,17 +1,8 @@ -# Copyright 2022 David Robillard <d@drobilla.net> +# Copyright 2022-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC config = configuration_data({'BASE': '/ns/ext'}) -if get_option('online_docs') - htaccess = configure_file( - configuration: config, - input: files('..' / '..' / 'htaccess.in'), - install_dir: lv2_docdir / 'ns' / 'ext', - output: '.htaccess', - ) -endif - spec_names = [ 'atom', 'buf-size', @@ -37,6 +28,15 @@ spec_names = [ ] if build_docs + if get_option('online_docs') + htaccess = configure_file( + configuration: config, + input: files('..' / '..' / 'htaccess.in'), + install_dir: lv2_docdir / 'ns' / 'ext', + output: '.htaccess', + ) + endif + foreach name : spec_names spec_file = files(lv2_source_root / 'lv2' / name + '.lv2' / name + '.ttl') @@ -45,6 +45,7 @@ if build_docs command: lv2specgen_command_prefix + [ '--docdir=../../c/html', '--style-uri=../../style/style.css', + ] + [ '@INPUT@', '@OUTPUT@', ], diff --git a/doc/ns/extensions/meson.build b/doc/ns/extensions/meson.build index 599deb8..3bf66b4 100644 --- a/doc/ns/extensions/meson.build +++ b/doc/ns/extensions/meson.build @@ -1,23 +1,23 @@ -# Copyright 2022 David Robillard <d@drobilla.net> +# Copyright 2022-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC config = configuration_data({'BASE': '/ns/extensions'}) -if get_option('online_docs') - htaccess = configure_file( - configuration: config, - input: files('..' / '..' / 'htaccess.in'), - install_dir: lv2_docdir / 'ns' / 'extensions', - output: '.htaccess', - ) -endif - spec_names = [ 'ui', 'units', ] if build_docs + if get_option('online_docs') + htaccess = configure_file( + configuration: config, + input: files('..' / '..' / 'htaccess.in'), + install_dir: lv2_docdir / 'ns' / 'extensions', + output: '.htaccess', + ) + endif + foreach name : spec_names spec_file = files(lv2_source_root / 'lv2' / name + '.lv2' / name + '.ttl') @@ -26,6 +26,7 @@ if build_docs command: lv2specgen_command_prefix + [ '--docdir=../../c/html', '--style-uri=../../style/style.css', + ] + [ '@INPUT@', '@OUTPUT@', ], diff --git a/doc/ns/meson.build b/doc/ns/meson.build index cda9ba0..3189cc2 100644 --- a/doc/ns/meson.build +++ b/doc/ns/meson.build @@ -3,15 +3,6 @@ config = configuration_data({'BASE': '/ns'}) -if get_option('online_docs') - htaccess = configure_file( - configuration: config, - input: files('..' / 'htaccess.in'), - install_dir: lv2_docdir / 'ns', - output: '.htaccess', - ) -endif - ###################### # Core Documentation # ###################### @@ -24,6 +15,7 @@ if build_docs command: lv2specgen_command_prefix + [ '--docdir=../c/html', '--style-uri=../style/style.css', + ] + [ '@INPUT@', '@OUTPUT@', ], @@ -35,6 +27,13 @@ if build_docs ) if get_option('online_docs') + htaccess = configure_file( + configuration: config, + input: files('..' / 'htaccess.in'), + install_dir: lv2_docdir / 'ns', + output: '.htaccess', + ) + configure_file( copy: true, input: spec_file, @@ -55,12 +54,14 @@ subdir('extensions') # Index # ######### -lv2_build_index = find_program(lv2_source_root / 'scripts' / 'lv2_build_index.py') +lv2_build_index = find_program( + lv2_source_root / 'scripts' / 'lv2_build_index.py', +) lv2_build_index_command = [ lv2_build_index, - '--lv2-version', meson.project_version(), - '--lv2-source-root', lv2_source_root, + ['--lv2-version', meson.project_version()], + ['--lv2-source-root', lv2_source_root], ] if get_option('online_docs') diff --git a/doc/style/meson.build b/doc/style/meson.build index 501c0fa..39e9242 100644 --- a/doc/style/meson.build +++ b/doc/style/meson.build @@ -1,10 +1,7 @@ # Copyright 2022 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC -style_files = files( - 'pygments.css', - 'style.css' -) +style_files = files('pygments.css', 'style.css') foreach file : style_files configure_file( diff --git a/include/lv2/atom/atom.h b/include/lv2/atom/atom.h index a6178cf..41af21a 100644 --- a/include/lv2/atom/atom.h +++ b/include/lv2/atom/atom.h @@ -1,8 +1,8 @@ // Copyright 2008-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_ATOM_H -#define LV2_ATOM_H +#ifndef LV2_ATOM_ATOM_H +#define LV2_ATOM_ATOM_H /** @defgroup atom Atom @@ -62,25 +62,19 @@ extern "C" { #endif -/** @cond */ -/** This expression will fail to compile if double does not fit in 64 bits. */ -typedef char lv2_atom_assert_double_fits_in_64_bits - [((sizeof(double) <= sizeof(uint64_t)) * 2) - 1]; -/** @endcond */ - /** Return a pointer to the contents of an Atom. The "contents" of an atom is the data past the complete type-specific header. @param type The type of the atom, for example LV2_Atom_String. @param atom A variable-sized atom. */ -#define LV2_ATOM_CONTENTS(type, atom) ((void*)((uint8_t*)(atom) + sizeof(type))) +#define LV2_ATOM_CONTENTS(type, atom) ((void*)((type*)(atom) + 1U)) /** Const version of LV2_ATOM_CONTENTS. */ #define LV2_ATOM_CONTENTS_CONST(type, atom) \ - ((const void*)((const uint8_t*)(atom) + sizeof(type))) + ((const void*)((const type*)(atom) + 1U)) /** Return a pointer to the body of an Atom. The "body" of an atom is the @@ -221,7 +215,7 @@ typedef struct { | Event 1 (size 6) | Event 2 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - |FRAMES |TYPE |SIZE |DATADATADATAPAD|FRAMES |... + |FRAMES |SIZE |TYPE |DATADATADATAPAD|FRAMES |... </pre> */ typedef struct { @@ -244,4 +238,4 @@ typedef struct { @} */ -#endif /* LV2_ATOM_H */ +#endif // LV2_ATOM_ATOM_H diff --git a/include/lv2/atom/forge.h b/include/lv2/atom/forge.h index 5e9bef8..58b7512 100644 --- a/include/lv2/atom/forge.h +++ b/include/lv2/atom/forge.h @@ -38,10 +38,10 @@ @{ */ -#include "lv2/atom/atom.h" -#include "lv2/atom/util.h" -#include "lv2/core/attributes.h" -#include "lv2/urid/urid.h" +#include <lv2/atom/atom.h> +#include <lv2/atom/util.h> +#include <lv2/core/attributes.h> +#include <lv2/urid/urid.h> #include <assert.h> #include <stdbool.h> @@ -90,24 +90,24 @@ typedef struct { LV2_Atom_Forge_Frame* stack; - LV2_URID Blank LV2_DEPRECATED; - LV2_URID Bool; - LV2_URID Chunk; - LV2_URID Double; - LV2_URID Float; - LV2_URID Int; - LV2_URID Long; - LV2_URID Literal; - LV2_URID Object; - LV2_URID Path; - LV2_URID Property; + LV2_URID Blank LV2_DEPRECATED; + LV2_URID Bool; + LV2_URID Chunk; + LV2_URID Double; + LV2_URID Float; + LV2_URID Int; + LV2_URID Long; + LV2_URID Literal; + LV2_URID Object; + LV2_URID Path; + LV2_URID Property; LV2_URID Resource LV2_DEPRECATED; - LV2_URID Sequence; - LV2_URID String; - LV2_URID Tuple; - LV2_URID URI; - LV2_URID URID; - LV2_URID Vector; + LV2_URID Sequence; + LV2_URID String; + LV2_URID Tuple; + LV2_URID URI; + LV2_URID URID; + LV2_URID Vector; } LV2_Atom_Forge; static inline void @@ -147,6 +147,7 @@ lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map) static inline LV2_Atom* lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref) { + // NOLINTNEXTLINE(performance-no-int-to-ptr) return forge->buf ? (LV2_Atom*)ref : forge->deref(forge->handle, ref); } @@ -305,7 +306,7 @@ lv2_atom_forge_pad(LV2_Atom_Forge* forge, uint32_t written) static inline LV2_Atom_Forge_Ref lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size) { - LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size); + const LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size); if (out) { lv2_atom_forge_pad(forge, size); } @@ -316,11 +317,18 @@ lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size) static inline LV2_Atom_Forge_Ref lv2_atom_forge_string_body(LV2_Atom_Forge* forge, const char* str, uint32_t len) { - LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, str, len); - if (out && (out = lv2_atom_forge_raw(forge, "", 1))) { - lv2_atom_forge_pad(forge, len + 1); + const LV2_Atom_Forge_Ref s = lv2_atom_forge_raw(forge, str, len); + if (!s) { + return s; } - return out; + + const LV2_Atom_Forge_Ref t = lv2_atom_forge_raw(forge, "", 1); + if (!t) { + return t; + } + + lv2_atom_forge_pad(forge, len + 1U); + return t; } /** @@ -484,10 +492,10 @@ lv2_atom_forge_vector(LV2_Atom_Forge* forge, const void* elems) { const LV2_Atom_Vector a = { - {(uint32_t)sizeof(LV2_Atom_Vector_Body) + n_elems * child_size, + {(uint32_t)sizeof(LV2_Atom_Vector_Body) + (n_elems * child_size), forge->Vector}, {child_size, child_type}}; - LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); + const LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); if (out) { lv2_atom_forge_write(forge, elems, child_size * n_elems); } @@ -561,8 +569,7 @@ lv2_atom_forge_object(LV2_Atom_Forge* forge, This function is deprecated and should not be used in new code. Use lv2_atom_forge_object() directly instead. */ -LV2_DEPRECATED -static inline LV2_Atom_Forge_Ref +LV2_DEPRECATED static inline LV2_Atom_Forge_Ref lv2_atom_forge_resource(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame, LV2_URID id, @@ -580,8 +587,7 @@ lv2_atom_forge_resource(LV2_Atom_Forge* forge, This function is deprecated and should not be used in new code. Use lv2_atom_forge_object() directly instead. */ -LV2_DEPRECATED -static inline LV2_Atom_Forge_Ref +LV2_DEPRECATED static inline LV2_Atom_Forge_Ref lv2_atom_forge_blank(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame, uint32_t id, @@ -667,4 +673,4 @@ LV2_RESTORE_WARNINGS @} */ -#endif /* LV2_ATOM_FORGE_H */ +#endif // LV2_ATOM_FORGE_H diff --git a/include/lv2/atom/util.h b/include/lv2/atom/util.h index f585f82..32e8b6a 100644 --- a/include/lv2/atom/util.h +++ b/include/lv2/atom/util.h @@ -1,4 +1,4 @@ -// Copyright 2008-2015 David Robillard <d@drobilla.net> +// Copyright 2008-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC #ifndef LV2_ATOM_UTIL_H @@ -13,7 +13,7 @@ */ /** - @defgroup util Utilities + @defgroup atom_util Utilities @ingroup atom Utilities for working with atoms. @@ -21,13 +21,15 @@ @{ */ -#include "lv2/atom/atom.h" +#include <lv2/atom/atom.h> #include <stdarg.h> #include <stdbool.h> #include <stdint.h> #include <string.h> +// NOLINTBEGIN(bugprone-macro-parentheses) + #ifdef __cplusplus extern "C" { #endif @@ -36,7 +38,7 @@ extern "C" { static inline uint32_t lv2_atom_pad_size(uint32_t size) { - return (size + 7U) & (~7U); + return (size + 7U) & ~(7U); } /** Return the total size of `atom`, including the header. */ @@ -57,8 +59,7 @@ lv2_atom_is_null(const LV2_Atom* atom) static inline bool lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b) { - return (a == b) || ((a->type == b->type) && (a->size == b->size) && - !memcmp(a + 1, b + 1, a->size)); + return (a == b) || !memcmp(a, b, sizeof(LV2_Atom) + a->size); } /** @@ -112,14 +113,14 @@ lv2_atom_sequence_next(const LV2_Atom_Event* i) @endcode */ #define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \ - for (LV2_Atom_Event * iter = lv2_atom_sequence_begin(&(seq)->body); \ + for (LV2_Atom_Event* iter = lv2_atom_sequence_begin(&(seq)->body); \ !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \ (iter) = lv2_atom_sequence_next(iter)) /** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ -#define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ - for (LV2_Atom_Event * iter = lv2_atom_sequence_begin(body); \ - !lv2_atom_sequence_is_end(body, size, (iter)); \ +#define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ + for (LV2_Atom_Event* iter = lv2_atom_sequence_begin(body); \ + !lv2_atom_sequence_is_end(body, size, (iter)); \ (iter) = lv2_atom_sequence_next(iter)) /** @@ -178,7 +179,7 @@ lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, static inline LV2_Atom* lv2_atom_tuple_begin(const LV2_Atom_Tuple* tup) { - return (LV2_Atom*)(LV2_ATOM_BODY(tup)); + return (LV2_Atom*)tup + 1U; } /** Return true iff `i` has reached the end of `body`. */ @@ -210,15 +211,14 @@ lv2_atom_tuple_next(const LV2_Atom* i) } @endcode */ -#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ - for (LV2_Atom * iter = lv2_atom_tuple_begin(tuple); \ - !lv2_atom_tuple_is_end( \ - LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \ +#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ + for (LV2_Atom* iter = lv2_atom_tuple_begin(tuple); !lv2_atom_tuple_is_end( \ + LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \ (iter) = lv2_atom_tuple_next(iter)) /** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ #define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \ - for (LV2_Atom * iter = (LV2_Atom*)(body); \ + for (LV2_Atom* iter = (LV2_Atom*)(body); \ !lv2_atom_tuple_is_end(body, size, (iter)); \ (iter) = lv2_atom_tuple_next(iter)) @@ -249,7 +249,7 @@ static inline LV2_Atom_Property_Body* lv2_atom_object_next(const LV2_Atom_Property_Body* i) { const LV2_Atom* const value = - (const LV2_Atom*)((const uint8_t*)i + 2 * sizeof(uint32_t)); + (const LV2_Atom*)((const uint8_t*)i + (2 * sizeof(uint32_t))); return (LV2_Atom_Property_Body*)((const uint8_t*)i + lv2_atom_pad_size( (uint32_t)sizeof(LV2_Atom_Property_Body) + @@ -270,15 +270,15 @@ lv2_atom_object_next(const LV2_Atom_Property_Body* i) } @endcode */ -#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ - for (LV2_Atom_Property_Body * iter = lv2_atom_object_begin(&(obj)->body); \ - !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ +#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ + for (LV2_Atom_Property_Body* iter = lv2_atom_object_begin(&(obj)->body); \ + !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ (iter) = lv2_atom_object_next(iter)) /** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ -#define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ - for (LV2_Atom_Property_Body * iter = lv2_atom_object_begin(body); \ - !lv2_atom_object_is_end(body, size, (iter)); \ +#define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ + for (LV2_Atom_Property_Body* iter = lv2_atom_object_begin(body); \ + !lv2_atom_object_is_end(body, size, (iter)); \ (iter) = lv2_atom_object_next(iter)) /** @@ -357,7 +357,7 @@ lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...) int n_queries = 0; /* Count number of keys so we can short-circuit when done */ - va_list args; + va_list args; // NOLINT(cppcoreguidelines-init-variables) va_start(args, body); for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { if (!va_arg(args, const LV2_Atom**)) { @@ -370,7 +370,7 @@ lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...) LV2_ATOM_OBJECT_BODY_FOREACH (body, size, prop) { va_start(args, body); for (int i = 0; i < n_queries; ++i) { - uint32_t qkey = va_arg(args, uint32_t); + const uint32_t qkey = va_arg(args, uint32_t); const LV2_Atom** qval = va_arg(args, const LV2_Atom**); if (qkey == prop->key && !*qval) { *qval = &prop->value; @@ -412,7 +412,7 @@ lv2_atom_object_get(const LV2_Atom_Object* object, ...) int n_queries = 0; /* Count number of keys so we can short-circuit when done */ - va_list args; + va_list args; // NOLINT(cppcoreguidelines-init-variables) va_start(args, object); for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { if (!va_arg(args, const LV2_Atom**)) { @@ -425,7 +425,7 @@ lv2_atom_object_get(const LV2_Atom_Object* object, ...) LV2_ATOM_OBJECT_FOREACH (object, prop) { va_start(args, object); for (int i = 0; i < n_queries; ++i) { - uint32_t qkey = va_arg(args, uint32_t); + const uint32_t qkey = va_arg(args, uint32_t); const LV2_Atom** qval = va_arg(args, const LV2_Atom**); if (qkey == prop->key && !*qval) { *qval = &prop->value; @@ -468,7 +468,7 @@ lv2_atom_object_get_typed(const LV2_Atom_Object* object, ...) int n_queries = 0; /* Count number of keys so we can short-circuit when done */ - va_list args; + va_list args; // NOLINT(cppcoreguidelines-init-variables) va_start(args, object); for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { if (!va_arg(args, const LV2_Atom**) || !va_arg(args, uint32_t)) { @@ -502,9 +502,11 @@ lv2_atom_object_get_typed(const LV2_Atom_Object* object, ...) } /* extern "C" */ #endif +// NOLINTEND(bugprone-macro-parentheses) + /** @} @} */ -#endif /* LV2_ATOM_UTIL_H */ +#endif // LV2_ATOM_UTIL_H diff --git a/include/lv2/buf-size/buf-size.h b/include/lv2/buf-size/buf-size.h index 17be7bc..c1a00e1 100644 --- a/include/lv2/buf-size/buf-size.h +++ b/include/lv2/buf-size/buf-size.h @@ -1,8 +1,8 @@ // Copyright 2007-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_BUF_SIZE_H -#define LV2_BUF_SIZE_H +#ifndef LV2_BUF_SIZE_BUF_SIZE_H +#define LV2_BUF_SIZE_BUF_SIZE_H /** @defgroup buf-size Buffer Size @@ -35,4 +35,4 @@ @} */ -#endif /* LV2_BUF_SIZE_H */ +#endif // LV2_BUF_SIZE_BUF_SIZE_H diff --git a/include/lv2/core/attributes.h b/include/lv2/core/attributes.h index ab46931..2db876f 100644 --- a/include/lv2/core/attributes.h +++ b/include/lv2/core/attributes.h @@ -43,4 +43,4 @@ @} */ -#endif /* LV2_CORE_ATTRIBUTES_H */ +#endif // LV2_CORE_ATTRIBUTES_H diff --git a/include/lv2/core/lv2.h b/include/lv2/core/lv2.h index 06adea0..e4b90fd 100644 --- a/include/lv2/core/lv2.h +++ b/include/lv2/core/lv2.h @@ -3,8 +3,8 @@ // Copyright 2000-2002 Richard W.E. Furse, Paul Barton-Davis, Stefan Westerfeld. // SPDX-License-Identifier: ISC -#ifndef LV2_H_INCLUDED -#define LV2_H_INCLUDED +#ifndef LV2_CORE_LV2_H +#define LV2_CORE_LV2_H /** @defgroup lv2 LV2 @@ -36,6 +36,7 @@ #define LV2_CORE__AnalyserPlugin LV2_CORE_PREFIX "AnalyserPlugin" ///< http://lv2plug.in/ns/lv2core#AnalyserPlugin #define LV2_CORE__AudioPort LV2_CORE_PREFIX "AudioPort" ///< http://lv2plug.in/ns/lv2core#AudioPort #define LV2_CORE__BandpassPlugin LV2_CORE_PREFIX "BandpassPlugin" ///< http://lv2plug.in/ns/lv2core#BandpassPlugin +#define LV2_CORE__BandstopPlugin LV2_CORE_PREFIX "BandstopPlugin" ///< http://lv2plug.in/ns/lv2core#BandstopPlugin #define LV2_CORE__CVPort LV2_CORE_PREFIX "CVPort" ///< http://lv2plug.in/ns/lv2core#CVPort #define LV2_CORE__ChorusPlugin LV2_CORE_PREFIX "ChorusPlugin" ///< http://lv2plug.in/ns/lv2core#ChorusPlugin #define LV2_CORE__CombPlugin LV2_CORE_PREFIX "CombPlugin" ///< http://lv2plug.in/ns/lv2core#CombPlugin @@ -99,6 +100,7 @@ #define LV2_CORE__index LV2_CORE_PREFIX "index" ///< http://lv2plug.in/ns/lv2core#index #define LV2_CORE__integer LV2_CORE_PREFIX "integer" ///< http://lv2plug.in/ns/lv2core#integer #define LV2_CORE__isLive LV2_CORE_PREFIX "isLive" ///< http://lv2plug.in/ns/lv2core#isLive +#define LV2_CORE__isSideChain LV2_CORE_PREFIX "isSideChain" ///< http://lv2plug.in/ns/lv2core#isSideChain #define LV2_CORE__latency LV2_CORE_PREFIX "latency" ///< http://lv2plug.in/ns/lv2core#latency #define LV2_CORE__maximum LV2_CORE_PREFIX "maximum" ///< http://lv2plug.in/ns/lv2core#maximum #define LV2_CORE__microVersion LV2_CORE_PREFIX "microVersion" ///< http://lv2plug.in/ns/lv2core#microVersion @@ -351,11 +353,13 @@ typedef struct LV2_Descriptor { Put this (LV2_SYMBOL_EXPORT) before any functions that are to be loaded by the host as a symbol from the dynamic library. */ -#ifdef _WIN32 -# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __declspec(dllexport) -#else -# define LV2_SYMBOL_EXPORT \ - LV2_SYMBOL_EXTERN __attribute__((visibility("default"))) +#ifndef LV2_SYMBOL_EXPORT +# ifdef _WIN32 +# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __declspec(dllexport) +# else +# define LV2_SYMBOL_EXPORT \ + LV2_SYMBOL_EXTERN __attribute__((visibility("default"))) +# endif #endif /** @@ -381,8 +385,7 @@ typedef struct LV2_Descriptor { Note that `index` has no meaning, hosts MUST NOT depend on it remaining consistent between loads of the plugin library. */ -LV2_SYMBOL_EXPORT -const LV2_Descriptor* +LV2_SYMBOL_EXPORT const LV2_Descriptor* lv2_descriptor(uint32_t index); /** @@ -447,8 +450,7 @@ typedef struct { be destroyed (using LV2_Lib_Descriptor::cleanup()) until all plugins loaded from that library have been destroyed. */ -LV2_SYMBOL_EXPORT -const LV2_Lib_Descriptor* +LV2_SYMBOL_EXPORT const LV2_Lib_Descriptor* lv2_lib_descriptor(const char* bundle_path, const LV2_Feature* const* features); /** @@ -467,4 +469,4 @@ typedef const LV2_Lib_Descriptor* (*LV2_Lib_Descriptor_Function)( @} */ -#endif /* LV2_H_INCLUDED */ +#endif // LV2_CORE_LV2_H diff --git a/include/lv2/core/lv2_util.h b/include/lv2/core/lv2_util.h index 8cd2872..82c5e1e 100644 --- a/include/lv2/core/lv2_util.h +++ b/include/lv2/core/lv2_util.h @@ -1,13 +1,16 @@ // Copyright 2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC +#ifndef LV2_CORE_LV2_UTIL_H +#define LV2_CORE_LV2_UTIL_H + /** - @defgroup util Utilities + @defgroup lv2_util Utilities @ingroup lv2core @{ */ -#include "lv2/core/lv2.h" +#include <lv2/core/lv2.h> #include <stdarg.h> #include <stdbool.h> @@ -62,13 +65,13 @@ lv2_features_data(const LV2_Feature* const* features, const char* const uri) static inline const char* lv2_features_query(const LV2_Feature* const* features, ...) { - va_list args; + va_list args; // NOLINT(cppcoreguidelines-init-variables) va_start(args, features); const char* uri = NULL; while ((uri = va_arg(args, const char*))) { - void** data = va_arg(args, void**); - bool required = (bool)va_arg(args, int); + void** data = va_arg(args, void**); + const bool required = (bool)va_arg(args, int); *data = lv2_features_data(features, uri); if (required && !*data) { @@ -88,3 +91,5 @@ lv2_features_query(const LV2_Feature* const* features, ...) /** @} */ + +#endif // LV2_CORE_LV2_UTIL_H diff --git a/include/lv2/data-access/data-access.h b/include/lv2/data-access/data-access.h index 2809d93..4586273 100644 --- a/include/lv2/data-access/data-access.h +++ b/include/lv2/data-access/data-access.h @@ -1,8 +1,8 @@ // Copyright 2008-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_DATA_ACCESS_H -#define LV2_DATA_ACCESS_H +#ifndef LV2_DATA_ACCESS_DATA_ACCESS_H +#define LV2_DATA_ACCESS_DATA_ACCESS_H /** @defgroup data-access Data Access @@ -57,4 +57,4 @@ typedef struct { @} */ -#endif /* LV2_DATA_ACCESS_H */ +#endif // LV2_DATA_ACCESS_DATA_ACCESS_H diff --git a/include/lv2/dynmanifest/dynmanifest.h b/include/lv2/dynmanifest/dynmanifest.h index d67c750..7a6854d 100644 --- a/include/lv2/dynmanifest/dynmanifest.h +++ b/include/lv2/dynmanifest/dynmanifest.h @@ -1,8 +1,8 @@ // Copyright 2008-2011 Stefano D'Angelo <zanga.mail@gmail.com> // SPDX-License-Identifier: ISC -#ifndef LV2_DYN_MANIFEST_H_INCLUDED -#define LV2_DYN_MANIFEST_H_INCLUDED +#ifndef LV2_DYNMANIFEST_DYNMANIFEST_H +#define LV2_DYNMANIFEST_DYNMANIFEST_H /** @defgroup dynmanifest Dynamic Manifest @@ -15,7 +15,7 @@ @{ */ -#include "lv2/core/lv2.h" +#include <lv2/core/lv2.h> #include <stdio.h> @@ -56,7 +56,7 @@ typedef void* LV2_Dyn_Manifest_Handle; evaluate the result of the operation by examining the returned value and MUST NOT try to interpret the value of handle. */ -int +LV2_SYMBOL_EXPORT int lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle* handle, const LV2_Feature* const* features); @@ -83,7 +83,7 @@ lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle* handle, @return 0 on success, otherwise a non-zero error code. */ -int +LV2_SYMBOL_EXPORT int lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle, FILE* fp); /** @@ -116,7 +116,7 @@ lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle, FILE* fp); @return 0 on success, otherwise a non-zero error code. */ -int +LV2_SYMBOL_EXPORT int lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle, FILE* fp, const char* uri); @@ -132,7 +132,7 @@ lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle, @param handle Dynamic manifest generator handle. */ -void +LV2_SYMBOL_EXPORT void lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle); #ifdef __cplusplus @@ -143,4 +143,4 @@ lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle); @} */ -#endif /* LV2_DYN_MANIFEST_H_INCLUDED */ +#endif // LV2_DYNMANIFEST_DYNMANIFEST_H diff --git a/include/lv2/event/event-helpers.h b/include/lv2/event/event-helpers.h index 0bf352f..de386ca 100644 --- a/include/lv2/event/event-helpers.h +++ b/include/lv2/event/event-helpers.h @@ -1,16 +1,16 @@ // Copyright 2008-2015 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_EVENT_HELPERS_H -#define LV2_EVENT_HELPERS_H +#ifndef LV2_EVENT_EVENT_HELPERS_H +#define LV2_EVENT_EVENT_HELPERS_H /** @file event-helpers.h Helper functions for the LV2 Event extension <http://lv2plug.in/ns/ext/event>. */ -#include "lv2/core/attributes.h" -#include "lv2/event/event.h" +#include <lv2/core/attributes.h> +#include <lv2/event/event.h> #include <stdbool.h> #include <stdint.h> @@ -107,7 +107,8 @@ lv2_event_increment(LV2_Event_Iterator* iter) return false; } - LV2_Event* const ev = (LV2_Event*)(iter->buf->data + iter->offset); + const LV2_Event* const ev = + (const LV2_Event*)(iter->buf->data + iter->offset); iter->offset += lv2_event_pad_size((uint16_t)((uint16_t)sizeof(LV2_Event) + ev->size)); @@ -239,4 +240,4 @@ LV2_RESTORE_WARNINGS } /* extern "C" */ #endif -#endif /* LV2_EVENT_HELPERS_H */ +#endif // LV2_EVENT_EVENT_HELPERS_H diff --git a/include/lv2/event/event.h b/include/lv2/event/event.h index b880de1..42ba54f 100644 --- a/include/lv2/event/event.h +++ b/include/lv2/event/event.h @@ -2,8 +2,8 @@ // Copyright 2006-2007 Lars Luthman <lars.luthman@gmail.com> // SPDX-License-Identifier: ISC -#ifndef LV2_EVENT_H -#define LV2_EVENT_H +#ifndef LV2_EVENT_EVENT_H +#define LV2_EVENT_EVENT_H /** @defgroup event Event @@ -36,7 +36,7 @@ #define LV2_EVENT_AUDIO_STAMP 0 ///< Special timestamp type for audio frames -#include "lv2/core/attributes.h" +#include <lv2/core/attributes.h> #include <stdint.h> @@ -51,8 +51,7 @@ LV2_DISABLE_DEPRECATION_WARNINGS Equal to 2^12 * 5 * 7 * 9 * 11 * 13 * 17, which is evenly divisible by all integers from 1 through 18 inclusive, and powers of 2 up to 2^12. */ -LV2_DEPRECATED -static const uint32_t LV2_EVENT_PPQN = 3136573440U; +LV2_DEPRECATED static const uint32_t LV2_EVENT_PPQN = 3136573440U; /** An LV2 event (header only). @@ -67,8 +66,7 @@ static const uint32_t LV2_EVENT_PPQN = 3136573440U; memcpy(ev_copy, ev, sizeof(LV2_Event) + ev->size); (or equivalent) */ -LV2_DEPRECATED -typedef struct { +LV2_DEPRECATED typedef struct { /** The frames portion of timestamp. The units used here can optionally be set for a port (with the lv2ev:timeUnits property), otherwise this is @@ -127,8 +125,7 @@ typedef struct { | | | | | | | | | | | | | | | | | | | | | | | | | |FRAMES |SUBFRMS|TYP|LEN|DATA..DATA..PAD|FRAMES | ... */ -LV2_DEPRECATED -typedef struct { +LV2_DEPRECATED typedef struct { /** The contents of the event buffer. This may or may not reside in the same block of memory as this header, plugins must not assume either. @@ -210,8 +207,7 @@ typedef struct { /** Opaque pointer to host data. */ -LV2_DEPRECATED -typedef void* LV2_Event_Callback_Data; +LV2_DEPRECATED typedef void* LV2_Event_Callback_Data; /** Non-POD events feature. @@ -221,8 +217,7 @@ typedef void* LV2_Event_Callback_Data; and data pointed to an instance of this struct. Note this feature is not mandatory to support the event extension. */ -LV2_DEPRECATED -typedef struct { +LV2_DEPRECATED typedef struct { /** Opaque pointer to host data. @@ -282,4 +277,4 @@ LV2_RESTORE_WARNINGS @} */ -#endif /* LV2_EVENT_H */ +#endif // LV2_EVENT_EVENT_H diff --git a/include/lv2/instance-access/instance-access.h b/include/lv2/instance-access/instance-access.h index a87ecaf..70c2789 100644 --- a/include/lv2/instance-access/instance-access.h +++ b/include/lv2/instance-access/instance-access.h @@ -1,8 +1,8 @@ // Copyright 2008-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_INSTANCE_ACCESS_H -#define LV2_INSTANCE_ACCESS_H +#ifndef LV2_INSTANCE_ACCESS_INSTANCE_ACCESS_H +#define LV2_INSTANCE_ACCESS_INSTANCE_ACCESS_H /** @defgroup instance-access Instance Access @@ -25,4 +25,4 @@ @} */ -#endif /* LV2_INSTANCE_ACCESS_H */ +#endif // LV2_INSTANCE_ACCESS_INSTANCE_ACCESS_H diff --git a/include/lv2/log/log.h b/include/lv2/log/log.h index cdaa6d3..2616ffe 100644 --- a/include/lv2/log/log.h +++ b/include/lv2/log/log.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_LOG_H -#define LV2_LOG_H +#ifndef LV2_LOG_LOG_H +#define LV2_LOG_LOG_H /** @defgroup log Log @@ -29,7 +29,7 @@ // clang-format on -#include "lv2/urid/urid.h" +#include <lv2/urid/urid.h> #include <stdarg.h> @@ -97,4 +97,4 @@ typedef struct { @} */ -#endif /* LV2_LOG_H */ +#endif // LV2_LOG_LOG_H diff --git a/include/lv2/log/logger.h b/include/lv2/log/logger.h index b7d2856..83e2132 100644 --- a/include/lv2/log/logger.h +++ b/include/lv2/log/logger.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_ATOM_LOGGER_H -#define LV2_ATOM_LOGGER_H +#ifndef LV2_LOG_LOGGER_H +#define LV2_LOG_LOGGER_H /** @defgroup logger Logger @@ -15,8 +15,8 @@ @{ */ -#include "lv2/log/log.h" -#include "lv2/urid/urid.h" +#include <lv2/log/log.h> +#include <lv2/urid/urid.h> #include <stdarg.h> #include <stdio.h> @@ -90,7 +90,7 @@ LV2_LOG_FUNC(2, 3) static inline int lv2_log_error(LV2_Log_Logger* logger, const char* fmt, ...) { - va_list args; + va_list args; // NOLINT(cppcoreguidelines-init-variables) va_start(args, fmt); const int ret = lv2_log_vprintf(logger, logger->Error, fmt, args); va_end(args); @@ -102,7 +102,7 @@ LV2_LOG_FUNC(2, 3) static inline int lv2_log_note(LV2_Log_Logger* logger, const char* fmt, ...) { - va_list args; + va_list args; // NOLINT(cppcoreguidelines-init-variables) va_start(args, fmt); const int ret = lv2_log_vprintf(logger, logger->Note, fmt, args); va_end(args); @@ -114,7 +114,7 @@ LV2_LOG_FUNC(2, 3) static inline int lv2_log_trace(LV2_Log_Logger* logger, const char* fmt, ...) { - va_list args; + va_list args; // NOLINT(cppcoreguidelines-init-variables) va_start(args, fmt); const int ret = lv2_log_vprintf(logger, logger->Trace, fmt, args); va_end(args); @@ -126,7 +126,7 @@ LV2_LOG_FUNC(2, 3) static inline int lv2_log_warning(LV2_Log_Logger* logger, const char* fmt, ...) { - va_list args; + va_list args; // NOLINT(cppcoreguidelines-init-variables) va_start(args, fmt); const int ret = lv2_log_vprintf(logger, logger->Warning, fmt, args); va_end(args); @@ -141,4 +141,4 @@ lv2_log_warning(LV2_Log_Logger* logger, const char* fmt, ...) @} */ -#endif /* LV2_LOG_LOGGER_H */ +#endif // LV2_LOG_LOGGER_H diff --git a/include/lv2/midi/midi.h b/include/lv2/midi/midi.h index cef7bc8..2a687fb 100644 --- a/include/lv2/midi/midi.h +++ b/include/lv2/midi/midi.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_MIDI_H -#define LV2_MIDI_H +#ifndef LV2_MIDI_MIDI_H +#define LV2_MIDI_MIDI_H /** @defgroup midi MIDI @@ -232,4 +232,4 @@ lv2_midi_message_type(const uint8_t* msg) @} */ -#endif /* LV2_MIDI_H */ +#endif // LV2_MIDI_MIDI_H diff --git a/include/lv2/morph/morph.h b/include/lv2/morph/morph.h index e60a7d5..88bfe81 100644 --- a/include/lv2/morph/morph.h +++ b/include/lv2/morph/morph.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_MORPH_H -#define LV2_MORPH_H +#ifndef LV2_MORPH_MORPH_H +#define LV2_MORPH_MORPH_H /** @defgroup morph Morph @@ -32,4 +32,4 @@ @} */ -#endif /* LV2_MORPH_H */ +#endif // LV2_MORPH_MORPH_H diff --git a/include/lv2/options/options.h b/include/lv2/options/options.h index 48f7bfb..5dea9f3 100644 --- a/include/lv2/options/options.h +++ b/include/lv2/options/options.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_OPTIONS_H -#define LV2_OPTIONS_H +#ifndef LV2_OPTIONS_OPTIONS_H +#define LV2_OPTIONS_OPTIONS_H /** @defgroup options Options @@ -15,8 +15,8 @@ @{ */ -#include "lv2/core/lv2.h" -#include "lv2/urid/urid.h" +#include <lv2/core/lv2.h> +#include <lv2/urid/urid.h> #include <stdint.h> @@ -133,4 +133,4 @@ typedef struct { @} */ -#endif /* LV2_OPTIONS_H */ +#endif // LV2_OPTIONS_OPTIONS_H diff --git a/include/lv2/parameters/parameters.h b/include/lv2/parameters/parameters.h index 6c17a1c..b20c83e 100644 --- a/include/lv2/parameters/parameters.h +++ b/include/lv2/parameters/parameters.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_PARAMETERS_H -#define LV2_PARAMETERS_H +#ifndef LV2_PARAMETERS_PARAMETERS_H +#define LV2_PARAMETERS_PARAMETERS_H /** @defgroup parameters Parameters @@ -52,4 +52,4 @@ @} */ -#endif /* LV2_PARAMETERS_H */ +#endif // LV2_PARAMETERS_PARAMETERS_H diff --git a/include/lv2/patch/patch.h b/include/lv2/patch/patch.h index 16b7889..99fd8e9 100644 --- a/include/lv2/patch/patch.h +++ b/include/lv2/patch/patch.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_PATCH_H -#define LV2_PATCH_H +#ifndef LV2_PATCH_PATCH_H +#define LV2_PATCH_PATCH_H /** @defgroup patch Patch @@ -57,4 +57,4 @@ @} */ -#endif /* LV2_PATCH_H */ +#endif // LV2_PATCH_PATCH_H diff --git a/include/lv2/port-groups/port-groups.h b/include/lv2/port-groups/port-groups.h index 303890a..ff00533 100644 --- a/include/lv2/port-groups/port-groups.h +++ b/include/lv2/port-groups/port-groups.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_PORT_GROUPS_H -#define LV2_PORT_GROUPS_H +#ifndef LV2_PORT_GROUPS_PORT_GROUPS_H +#define LV2_PORT_GROUPS_PORT_GROUPS_H /** @defgroup port-groups Port Groups @@ -61,4 +61,4 @@ @} */ -#endif /* LV2_PORT_GROUPS_H */ +#endif // LV2_PORT_GROUPS_PORT_GROUPS_H diff --git a/include/lv2/port-props/port-props.h b/include/lv2/port-props/port-props.h index ff45140..6007f4b 100644 --- a/include/lv2/port-props/port-props.h +++ b/include/lv2/port-props/port-props.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_PORT_PROPS_H -#define LV2_PORT_PROPS_H +#ifndef LV2_PORT_PROPS_PORT_PROPS_H +#define LV2_PORT_PROPS_PORT_PROPS_H /** @defgroup port-props Port Properties @@ -37,4 +37,4 @@ @} */ -#endif /* LV2_PORT_PROPS_H */ +#endif // LV2_PORT_PROPS_PORT_PROPS_H diff --git a/include/lv2/presets/presets.h b/include/lv2/presets/presets.h index 7100605..ecd23a2 100644 --- a/include/lv2/presets/presets.h +++ b/include/lv2/presets/presets.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_PRESETS_H -#define LV2_PRESETS_H +#ifndef LV2_PRESETS_PRESETS_H +#define LV2_PRESETS_PRESETS_H /** @defgroup presets Presets @@ -32,4 +32,4 @@ @} */ -#endif /* LV2_PRESETS_H */ +#endif // LV2_PRESETS_PRESETS_H diff --git a/include/lv2/resize-port/resize-port.h b/include/lv2/resize-port/resize-port.h index cd4828e..7593a6d 100644 --- a/include/lv2/resize-port/resize-port.h +++ b/include/lv2/resize-port/resize-port.h @@ -1,8 +1,8 @@ // Copyright 2007-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_RESIZE_PORT_H -#define LV2_RESIZE_PORT_H +#ifndef LV2_RESIZE_PORT_RESIZE_PORT_H +#define LV2_RESIZE_PORT_RESIZE_PORT_H /** @defgroup resize-port Resize Port @@ -73,4 +73,4 @@ typedef struct { @} */ -#endif /* LV2_RESIZE_PORT_H */ +#endif // LV2_RESIZE_PORT_RESIZE_PORT_H diff --git a/include/lv2/state/state.h b/include/lv2/state/state.h index 9c5d9d8..c1a357a 100644 --- a/include/lv2/state/state.h +++ b/include/lv2/state/state.h @@ -2,8 +2,8 @@ // Copyright 2010 Leonard Ritter <paniq@paniq.org> // SPDX-License-Identifier: ISC -#ifndef LV2_STATE_H -#define LV2_STATE_H +#ifndef LV2_STATE_STATE_H +#define LV2_STATE_STATE_H /** @defgroup state State @@ -16,7 +16,7 @@ @{ */ -#include "lv2/core/lv2.h" +#include <lv2/core/lv2.h> #include <stddef.h> #include <stdint.h> @@ -376,4 +376,4 @@ typedef struct { @} */ -#endif /* LV2_STATE_H */ +#endif // LV2_STATE_STATE_H diff --git a/include/lv2/time/time.h b/include/lv2/time/time.h index 83408c9..2d81dc7 100644 --- a/include/lv2/time/time.h +++ b/include/lv2/time/time.h @@ -1,8 +1,8 @@ // Copyright 2011-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_TIME_H -#define LV2_TIME_H +#ifndef LV2_TIME_TIME_H +#define LV2_TIME_TIME_H /** @defgroup time Time @@ -43,4 +43,4 @@ @} */ -#endif /* LV2_TIME_H */ +#endif // LV2_TIME_TIME_H diff --git a/include/lv2/ui/ui.h b/include/lv2/ui/ui.h index 5ef75a3..408b570 100644 --- a/include/lv2/ui/ui.h +++ b/include/lv2/ui/ui.h @@ -2,8 +2,8 @@ // Copyright 2006-2011 Lars Luthman <lars.luthman@gmail.com> // SPDX-License-Identifier: ISC -#ifndef LV2_UI_H -#define LV2_UI_H +#ifndef LV2_UI_UI_H +#define LV2_UI_UI_H /** @defgroup ui User Interfaces @@ -16,8 +16,8 @@ @{ */ -#include "lv2/core/lv2.h" -#include "lv2/urid/urid.h" +#include <lv2/core/lv2.h> +#include <lv2/urid/urid.h> #include <stdbool.h> #include <stdint.h> @@ -29,11 +29,13 @@ #define LV2_UI__CocoaUI LV2_UI_PREFIX "CocoaUI" ///< http://lv2plug.in/ns/extensions/ui#CocoaUI #define LV2_UI__Gtk3UI LV2_UI_PREFIX "Gtk3UI" ///< http://lv2plug.in/ns/extensions/ui#Gtk3UI +#define LV2_UI__Gtk4UI LV2_UI_PREFIX "Gtk4UI" ///< http://lv2plug.in/ns/extensions/ui#Gtk4UI #define LV2_UI__GtkUI LV2_UI_PREFIX "GtkUI" ///< http://lv2plug.in/ns/extensions/ui#GtkUI #define LV2_UI__PortNotification LV2_UI_PREFIX "PortNotification" ///< http://lv2plug.in/ns/extensions/ui#PortNotification #define LV2_UI__PortProtocol LV2_UI_PREFIX "PortProtocol" ///< http://lv2plug.in/ns/extensions/ui#PortProtocol #define LV2_UI__Qt4UI LV2_UI_PREFIX "Qt4UI" ///< http://lv2plug.in/ns/extensions/ui#Qt4UI #define LV2_UI__Qt5UI LV2_UI_PREFIX "Qt5UI" ///< http://lv2plug.in/ns/extensions/ui#Qt5UI +#define LV2_UI__Qt6UI LV2_UI_PREFIX "Qt6UI" ///< http://lv2plug.in/ns/extensions/ui#Qt6UI #define LV2_UI__UI LV2_UI_PREFIX "UI" ///< http://lv2plug.in/ns/extensions/ui#UI #define LV2_UI__WindowsUI LV2_UI_PREFIX "WindowsUI" ///< http://lv2plug.in/ns/extensions/ui#WindowsUI #define LV2_UI__X11UI LV2_UI_PREFIX "X11UI" ///< http://lv2plug.in/ns/extensions/ui#X11UI @@ -67,7 +69,7 @@ /** The index returned by LV2UI_Port_Map::port_index() for unknown ports. */ -#define LV2UI_INVALID_PORT_INDEX ((uint32_t)-1) +#define LV2UI_INVALID_PORT_INDEX ((uint32_t)(-1)) #ifdef __cplusplus extern "C" { @@ -509,8 +511,7 @@ typedef struct { This is the entry point to a UI library, which works in the same way as lv2_descriptor() but for UIs rather than plugins. */ -LV2_SYMBOL_EXPORT -const LV2UI_Descriptor* +LV2_SYMBOL_EXPORT const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index); /** @@ -526,4 +527,4 @@ typedef const LV2UI_Descriptor* (*LV2UI_DescriptorFunction)(uint32_t index); @} */ -#endif /* LV2_UI_H */ +#endif // LV2_UI_UI_H diff --git a/include/lv2/units/units.h b/include/lv2/units/units.h index 0b8d416..a6ac4c5 100644 --- a/include/lv2/units/units.h +++ b/include/lv2/units/units.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_UNITS_H -#define LV2_UNITS_H +#ifndef LV2_UNITS_UNITS_H +#define LV2_UNITS_UNITS_H /** @defgroup units Units @@ -59,4 +59,4 @@ @} */ -#endif /* LV2_UNITS_H */ +#endif // LV2_UNITS_UNITS_H diff --git a/include/lv2/uri-map/uri-map.h b/include/lv2/uri-map/uri-map.h index 0c683d0..90bc9bd 100644 --- a/include/lv2/uri-map/uri-map.h +++ b/include/lv2/uri-map/uri-map.h @@ -1,8 +1,8 @@ // Copyright 2008-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_URI_MAP_H -#define LV2_URI_MAP_H +#ifndef LV2_URI_MAP_URI_MAP_H +#define LV2_URI_MAP_URI_MAP_H /** @defgroup uri-map URI Map @@ -30,7 +30,7 @@ // clang-format on -#include "lv2/core/attributes.h" +#include <lv2/core/attributes.h> #include <stdint.h> @@ -43,8 +43,7 @@ LV2_DISABLE_DEPRECATION_WARNINGS /** Opaque pointer to host data. */ -LV2_DEPRECATED -typedef void* LV2_URI_Map_Callback_Data; +LV2_DEPRECATED typedef void* LV2_URI_Map_Callback_Data; /** URI Map Feature. @@ -53,8 +52,7 @@ typedef void* LV2_URI_Map_Callback_Data; plugin's instantiate method with URI "http://lv2plug.in/ns/ext/uri-map" and data pointed to an instance of this struct. */ -LV2_DEPRECATED -typedef struct { +LV2_DEPRECATED typedef struct { /** Opaque pointer to host data. @@ -105,4 +103,4 @@ LV2_RESTORE_WARNINGS @} */ -#endif /* LV2_URI_MAP_H */ +#endif // LV2_URI_MAP_URI_MAP_H diff --git a/include/lv2/urid/urid.h b/include/lv2/urid/urid.h index 5890307..8dc4332 100644 --- a/include/lv2/urid/urid.h +++ b/include/lv2/urid/urid.h @@ -2,8 +2,8 @@ // Copyright 2011 Gabriel M. Beddingfield <gabrbedd@gmail.com> // SPDX-License-Identifier: ISC -#ifndef LV2_URID_H -#define LV2_URID_H +#ifndef LV2_URID_URID_H +#define LV2_URID_URID_H /** @defgroup urid URID @@ -124,4 +124,4 @@ typedef struct { @} */ -#endif /* LV2_URID_H */ +#endif // LV2_URID_URID_H diff --git a/include/lv2/worker/worker.h b/include/lv2/worker/worker.h index 0b0e792..2cfd052 100644 --- a/include/lv2/worker/worker.h +++ b/include/lv2/worker/worker.h @@ -1,8 +1,8 @@ // Copyright 2012-2016 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef LV2_WORKER_H -#define LV2_WORKER_H +#ifndef LV2_WORKER_WORKER_H +#define LV2_WORKER_WORKER_H /** @defgroup worker Worker @@ -15,7 +15,7 @@ @{ */ -#include "lv2/core/lv2.h" +#include <lv2/core/lv2.h> #include <stdint.h> @@ -167,4 +167,4 @@ typedef struct { @} */ -#endif /* LV2_WORKER_H */ +#endif // LV2_WORKER_WORKER_H diff --git a/lv2/atom.lv2/atom.meta.ttl b/lv2/atom.lv2/atom.meta.ttl index 81c3482..e2902c5 100644 --- a/lv2/atom.lv2/atom.meta.ttl +++ b/lv2/atom.lv2/atom.meta.ttl @@ -447,4 +447,3 @@ This protocol applies to atom ports. The host must transfer the complete atom contained in the port, including header. """^^lv2:Markdown . - diff --git a/lv2/atom.lv2/atom.ttl b/lv2/atom.lv2/atom.ttl index bdeaebf..064d274 100644 --- a/lv2/atom.lv2/atom.ttl +++ b/lv2/atom.lv2/atom.ttl @@ -161,7 +161,7 @@ atom:Resource rdfs:subClassOf atom:Object ; rdfs:label "Resource" ; rdfs:comment "A named collection of properties with a URI." ; - owl:deprecated "true"^^xsd:boolean ; + owl:deprecated true ; atom:cType "LV2_Atom_Object" . atom:Blank @@ -169,7 +169,7 @@ atom:Blank rdfs:subClassOf atom:Object ; rdfs:label "Blank" ; rdfs:comment "An anonymous collection of properties without a URI." ; - owl:deprecated "true"^^xsd:boolean ; + owl:deprecated true ; atom:cType "LV2_Atom_Object" . atom:Sound @@ -244,4 +244,3 @@ atom:atomTransfer a ui:PortProtocol ; rdfs:label "atom transfer" ; rdfs:comment "A port protocol for transferring atoms." . - diff --git a/lv2/atom.lv2/manifest.ttl b/lv2/atom.lv2/manifest.ttl index 3cb5134..8875a20 100644 --- a/lv2/atom.lv2/manifest.ttl +++ b/lv2/atom.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 2 ; lv2:microVersion 4 ; rdfs:seeAlso <atom.ttl> . - diff --git a/lv2/buf-size.lv2/buf-size.meta.ttl b/lv2/buf-size.lv2/buf-size.meta.ttl index ba08b29..dbce35b 100644 --- a/lv2/buf-size.lv2/buf-size.meta.ttl +++ b/lv2/buf-size.lv2/buf-size.meta.ttl @@ -120,4 +120,3 @@ This should be provided as an option by hosts that support event ports auxiliary buffers large enough to copy the input. """^^lv2:Markdown . - diff --git a/lv2/buf-size.lv2/buf-size.ttl b/lv2/buf-size.lv2/buf-size.ttl index 4f6bd52..be18fbe 100644 --- a/lv2/buf-size.lv2/buf-size.ttl +++ b/lv2/buf-size.lv2/buf-size.ttl @@ -65,4 +65,3 @@ bufsz:sequenceSize rdfs:label "sequence size" ; rdfs:comment "The maximum size of a sequence, in bytes." ; rdfs:range xsd:nonNegativeInteger . - diff --git a/lv2/buf-size.lv2/manifest.ttl b/lv2/buf-size.lv2/manifest.ttl index d242f97..b9cd1ec 100644 --- a/lv2/buf-size.lv2/manifest.ttl +++ b/lv2/buf-size.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 4 ; rdfs:seeAlso <buf-size.ttl> . - diff --git a/lv2/core.lv2/lv2core.meta.ttl b/lv2/core.lv2/lv2core.meta.ttl index 9844b29..77f2c67 100644 --- a/lv2/core.lv2/lv2core.meta.ttl +++ b/lv2/core.lv2/lv2core.meta.ttl @@ -126,7 +126,7 @@ If the value has no explicit datatype, it is assumed to be a valid XHTML Basic 1.1 fragment suitable for use as the content of the `body` element of a page. XHTML Basic is a W3C Recommendation which defines a simplified subset of XHTML -intended to be reasonable to implement with limited resources, for exampe on +intended to be reasonable to implement with limited resources, for example on embedded devices. See [XHTML Basic, Section 3](http://www.w3.org/TR/xhtml-basic/#s_xhtmlmodules) for a list of valid tags. @@ -702,4 +702,3 @@ but should not be considered a part of the main signal chain. Sidechain ports SHOULD be lv2:connectionOptional, and may be ignored by hosts. """^^lv2:Markdown . - diff --git a/lv2/core.lv2/lv2core.ttl b/lv2/core.lv2/lv2core.ttl index 7722cac..c7dcf50 100644 --- a/lv2/core.lv2/lv2core.ttl +++ b/lv2/core.lv2/lv2core.ttl @@ -17,7 +17,7 @@ lv2:Specification owl:Class ; rdfs:subClassOf doap:Project ; rdfs:label "Specification" ; - rdfs:comment "An LV2 specifiation." . + rdfs:comment "An LV2 specification." . lv2:Markdown a rdfs:Datatype ; @@ -374,7 +374,7 @@ lv2:connectionOptional lv2:reportsLatency a lv2:PortProperty ; - owl:deprecated "true"^^xsd:boolean ; + owl:deprecated true ; rdfs:label "reports latency" ; rdfs:comment "Control port value is the plugin latency in frames." . @@ -524,6 +524,13 @@ lv2:BandpassPlugin rdfs:label "Bandpass Filter Plugin" ; rdfs:comment "A filter that attenuates frequencies outside of some band." . +lv2:BandstopPlugin + a rdfs:Class , + owl:Class ; + rdfs:subClassOf lv2:FilterPlugin ; + rdfs:label "Bandstop Filter Plugin" ; + rdfs:comment "A filter that attenuates frequencies inside of some band." . + lv2:HighpassPlugin a rdfs:Class , owl:Class ; @@ -670,4 +677,3 @@ lv2:MIDIPlugin rdfs:subClassOf lv2:Plugin ; rdfs:label "MIDI Plugin" ; rdfs:comment "A plugin that primarily processes MIDI messages." . - diff --git a/lv2/core.lv2/manifest.ttl b/lv2/core.lv2/manifest.ttl index 7f5e37e..0072346 100644 --- a/lv2/core.lv2/manifest.ttl +++ b/lv2/core.lv2/manifest.ttl @@ -4,12 +4,11 @@ <http://lv2plug.in/ns/lv2core> a lv2:Specification ; - lv2:minorVersion 18 ; - lv2:microVersion 6 ; + lv2:minorVersion 19 ; + lv2:microVersion 0 ; rdfs:seeAlso <lv2core.ttl> . <http://lv2plug.in/ns/lv2> a doap:Project ; rdfs:seeAlso <meta.ttl> , <people.ttl> . - diff --git a/lv2/core.lv2/meta.ttl b/lv2/core.lv2/meta.ttl index 75a13ac..014285c 100644 --- a/lv2/core.lv2/meta.ttl +++ b/lv2/core.lv2/meta.ttl @@ -32,4 +32,3 @@ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH R meta:daste , meta:kfoltman , meta:paniq . - diff --git a/lv2/core.lv2/people.ttl b/lv2/core.lv2/people.ttl index 52d0384..c735af6 100644 --- a/lv2/core.lv2/people.ttl +++ b/lv2/core.lv2/people.ttl @@ -48,4 +48,3 @@ meta:bmwiedemann a foaf:Person ; foaf:name "Bernhard M. Wiedemann" ; foaf:mbox <bwiedemann@suse.de> . - diff --git a/lv2/data-access.lv2/data-access.meta.ttl b/lv2/data-access.lv2/data-access.meta.ttl index 7d0e622..735bf0b 100644 --- a/lv2/data-access.lv2/data-access.meta.ttl +++ b/lv2/data-access.lv2/data-access.meta.ttl @@ -27,4 +27,3 @@ LV2_Descriptor::extension_data() with URI LV2_DATA_ACCESS_URI and data pointed to an instance of LV2_Extension_Data_Feature. """^^lv2:Markdown . - diff --git a/lv2/data-access.lv2/data-access.ttl b/lv2/data-access.lv2/data-access.ttl index b0dc6f4..b75cc81 100644 --- a/lv2/data-access.lv2/data-access.ttl +++ b/lv2/data-access.lv2/data-access.ttl @@ -7,4 +7,3 @@ rdfs:label "data access" ; rdfs:comment "A feature that provides access to plugin extension data." ; rdfs:seeAlso <data-access.meta.ttl> . - diff --git a/lv2/data-access.lv2/manifest.ttl b/lv2/data-access.lv2/manifest.ttl index 9585a5e..54fcace 100644 --- a/lv2/data-access.lv2/manifest.ttl +++ b/lv2/data-access.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 6 ; rdfs:seeAlso <data-access.ttl> . - diff --git a/lv2/dynmanifest.lv2/dynmanifest.meta.ttl b/lv2/dynmanifest.lv2/dynmanifest.meta.ttl index 5d365c6..9c2919d 100644 --- a/lv2/dynmanifest.lv2/dynmanifest.meta.ttl +++ b/lv2/dynmanifest.lv2/dynmanifest.meta.ttl @@ -86,4 +86,3 @@ All relative URIs in the generated data MUST be relative to the base path that would be used to parse a normal LV2 manifest (the bundle path). """^^lv2:Markdown . - diff --git a/lv2/dynmanifest.lv2/dynmanifest.ttl b/lv2/dynmanifest.lv2/dynmanifest.ttl index b46d694..12be4dc 100644 --- a/lv2/dynmanifest.lv2/dynmanifest.ttl +++ b/lv2/dynmanifest.lv2/dynmanifest.ttl @@ -21,4 +21,3 @@ dman:DynManifest rdfs:comment "A DynManifest MUST have at least one lv2:binary." ] ; rdfs:comment "Dynamic manifest for an LV2 binary." . - diff --git a/lv2/dynmanifest.lv2/manifest.ttl b/lv2/dynmanifest.lv2/manifest.ttl index db27a73..2c072a3 100644 --- a/lv2/dynmanifest.lv2/manifest.ttl +++ b/lv2/dynmanifest.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 6 ; rdfs:seeAlso <dynmanifest.ttl> . - diff --git a/lv2/event.lv2/event.meta.ttl b/lv2/event.lv2/event.meta.ttl index f573a15..4492d2b 100644 --- a/lv2/event.lv2/event.meta.ttl +++ b/lv2/event.lv2/event.meta.ttl @@ -157,4 +157,3 @@ their output type on an input port so the host can make more sense of the plugin and provide a more sensible interface. """^^lv2:Markdown . - diff --git a/lv2/event.lv2/event.ttl b/lv2/event.lv2/event.ttl index 2d871f6..ebcbd3a 100644 --- a/lv2/event.lv2/event.ttl +++ b/lv2/event.lv2/event.ttl @@ -82,4 +82,3 @@ ev:inheritsTimeStamp rdfs:range lv2:Port ; rdfs:label "inherits time stamp type" ; rdfs:comment "Output port inherits time stamp types from an input port." . - diff --git a/lv2/event.lv2/manifest.ttl b/lv2/event.lv2/manifest.ttl index 230fe73..15b095b 100644 --- a/lv2/event.lv2/manifest.ttl +++ b/lv2/event.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 14 ; rdfs:seeAlso <event.ttl> . - diff --git a/lv2/instance-access.lv2/instance-access.meta.ttl b/lv2/instance-access.lv2/instance-access.meta.ttl index 8d33100..b9cdfc4 100644 --- a/lv2/instance-access.lv2/instance-access.meta.ttl +++ b/lv2/instance-access.lv2/instance-access.meta.ttl @@ -26,4 +26,3 @@ instantiate method with URI LV2_INSTANCE_ACCESS_URI and data pointed directly to the LV2_Handle of the plugin instance. """^^lv2:Markdown . - diff --git a/lv2/instance-access.lv2/instance-access.ttl b/lv2/instance-access.lv2/instance-access.ttl index 637f4e0..068ef4f 100644 --- a/lv2/instance-access.lv2/instance-access.ttl +++ b/lv2/instance-access.lv2/instance-access.ttl @@ -7,4 +7,3 @@ rdfs:label "instance access" ; rdfs:comment "A feature that provides access to a plugin instance." ; rdfs:seeAlso <instance-access.meta.ttl> . - diff --git a/lv2/instance-access.lv2/manifest.ttl b/lv2/instance-access.lv2/manifest.ttl index e6c8810..3da863d 100644 --- a/lv2/instance-access.lv2/manifest.ttl +++ b/lv2/instance-access.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 6 ; rdfs:seeAlso <instance-access.ttl> . - diff --git a/lv2/log.lv2/log.meta.ttl b/lv2/log.lv2/log.meta.ttl index 06d9f63..0c74e6c 100644 --- a/lv2/log.lv2/log.meta.ttl +++ b/lv2/log.lv2/log.meta.ttl @@ -81,4 +81,3 @@ the host must pass an LV2_Feature to LV2_Descriptor::instantiate() with URI LV2_LOG__log and data pointed to an instance of LV2_Log_Log. """^^lv2:Markdown . - diff --git a/lv2/log.lv2/log.ttl b/lv2/log.lv2/log.ttl index 0b334ed..10f0acc 100644 --- a/lv2/log.lv2/log.ttl +++ b/lv2/log.lv2/log.ttl @@ -45,4 +45,3 @@ log:log a lv2:Feature ; rdfs:label "log" ; rdfs:comment "Logging feature." . - diff --git a/lv2/log.lv2/manifest.ttl b/lv2/log.lv2/manifest.ttl index bcaeff3..f448ffe 100644 --- a/lv2/log.lv2/manifest.ttl +++ b/lv2/log.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 2 ; lv2:microVersion 4 ; rdfs:seeAlso <log.ttl> . - diff --git a/lv2/midi.lv2/manifest.ttl b/lv2/midi.lv2/manifest.ttl index f141936..7c9efcd 100644 --- a/lv2/midi.lv2/manifest.ttl +++ b/lv2/midi.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 10 ; rdfs:seeAlso <midi.ttl> . - diff --git a/lv2/midi.lv2/midi.meta.ttl b/lv2/midi.lv2/midi.meta.ttl index b3dfd44..8afb944 100644 --- a/lv2/midi.lv2/midi.meta.ttl +++ b/lv2/midi.lv2/midi.meta.ttl @@ -70,4 +70,3 @@ midi:statusMask This is a status byte with the lower nibble set to zero. """^^lv2:Markdown . - diff --git a/lv2/midi.lv2/midi.ttl b/lv2/midi.lv2/midi.ttl index 4a0e8c9..5dfdbc7 100644 --- a/lv2/midi.lv2/midi.ttl +++ b/lv2/midi.lv2/midi.ttl @@ -362,4 +362,3 @@ midi:velocity rdfs:label "velocity" ; rdfs:range midi:HexByte ; rdfs:comment "The velocity of a note message (0 to 127)." . - diff --git a/lv2/morph.lv2/manifest.ttl b/lv2/morph.lv2/manifest.ttl index 7c85cfd..c63317b 100644 --- a/lv2/morph.lv2/manifest.ttl +++ b/lv2/morph.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 0 ; rdfs:seeAlso <morph.ttl> . - diff --git a/lv2/morph.lv2/morph.meta.ttl b/lv2/morph.lv2/morph.meta.ttl index f161557..09945c0 100644 --- a/lv2/morph.lv2/morph.meta.ttl +++ b/lv2/morph.lv2/morph.meta.ttl @@ -75,4 +75,3 @@ The currently active type of the port. This is for dynamic use as an option and SHOULD NOT be listed in the static plugin data. """^^lv2:Markdown . - diff --git a/lv2/morph.lv2/morph.ttl b/lv2/morph.lv2/morph.ttl index 9b8ef51..cd21dd9 100644 --- a/lv2/morph.lv2/morph.ttl +++ b/lv2/morph.lv2/morph.ttl @@ -43,4 +43,3 @@ morph:currentType rdfs:domain morph:MorphPort ; rdfs:label "current type" ; rdfs:comment "The currently active type of the port." . - diff --git a/lv2/options.lv2/manifest.ttl b/lv2/options.lv2/manifest.ttl index 18db448..c54456c 100644 --- a/lv2/options.lv2/manifest.ttl +++ b/lv2/options.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 4 ; rdfs:seeAlso <options.ttl> . - diff --git a/lv2/options.lv2/options.meta.ttl b/lv2/options.lv2/options.meta.ttl index 10ca296..33946f4 100644 --- a/lv2/options.lv2/options.meta.ttl +++ b/lv2/options.lv2/options.meta.ttl @@ -94,4 +94,3 @@ The host SHOULD provide a value for the specified option if one is known, or provide the user an opportunity to specify one if possible. """^^lv2:Markdown . - diff --git a/lv2/options.lv2/options.ttl b/lv2/options.lv2/options.ttl index 5f9fcc9..7233180 100644 --- a/lv2/options.lv2/options.ttl +++ b/lv2/options.lv2/options.ttl @@ -41,4 +41,3 @@ opts:supportedOption rdfs:range rdf:Property ; rdfs:label "supported option" ; rdfs:comment "An option supported or by the instance." . - diff --git a/lv2/parameters.lv2/manifest.ttl b/lv2/parameters.lv2/manifest.ttl index 57f5d2e..4889ae5 100644 --- a/lv2/parameters.lv2/manifest.ttl +++ b/lv2/parameters.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 4 ; rdfs:seeAlso <parameters.ttl> . - diff --git a/lv2/parameters.lv2/parameters.meta.ttl b/lv2/parameters.lv2/parameters.meta.ttl index ffb3507..653f715 100644 --- a/lv2/parameters.lv2/parameters.meta.ttl +++ b/lv2/parameters.lv2/parameters.meta.ttl @@ -36,4 +36,3 @@ and wet percentages can be calculated from the following equations: Typically, maximum value of 1 or 100 and minimum value of 0 should be used. """^^lv2:Markdown . - diff --git a/lv2/parameters.lv2/parameters.ttl b/lv2/parameters.lv2/parameters.ttl index 9987812..2e2c5df 100644 --- a/lv2/parameters.lv2/parameters.ttl +++ b/lv2/parameters.lv2/parameters.ttl @@ -202,4 +202,3 @@ param:CompressorControls ] , [ lv2:designation param:ratio ] . - diff --git a/lv2/patch.lv2/manifest.ttl b/lv2/patch.lv2/manifest.ttl index 4bf9cfb..fee2375 100644 --- a/lv2/patch.lv2/manifest.ttl +++ b/lv2/patch.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 2 ; lv2:microVersion 10 ; rdfs:seeAlso <patch.ttl> . - diff --git a/lv2/patch.lv2/patch.meta.ttl b/lv2/patch.lv2/patch.meta.ttl index 976034f..edbb5dc 100644 --- a/lv2/patch.lv2/patch.meta.ttl +++ b/lv2/patch.lv2/patch.meta.ttl @@ -303,4 +303,3 @@ interfaces to present appropriate controls. For example: patch:writable eg:title . """^^lv2:Markdown . - diff --git a/lv2/patch.lv2/patch.ttl b/lv2/patch.lv2/patch.ttl index 33d04d1..59ca118 100644 --- a/lv2/patch.lv2/patch.ttl +++ b/lv2/patch.lv2/patch.ttl @@ -245,4 +245,3 @@ patch:writable rdfs:label "writable" ; rdfs:range rdf:Property ; rdfs:comment "A property that can be set with a patch:Set or patch:Patch message." . - diff --git a/lv2/port-groups.lv2/manifest.ttl b/lv2/port-groups.lv2/manifest.ttl index a887cb0..7df99a8 100644 --- a/lv2/port-groups.lv2/manifest.ttl +++ b/lv2/port-groups.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 4 ; rdfs:seeAlso <port-groups.ttl> . - diff --git a/lv2/port-groups.lv2/port-groups.meta.ttl b/lv2/port-groups.lv2/port-groups.meta.ttl index 4e3a158..a76d783 100644 --- a/lv2/port-groups.lv2/port-groups.meta.ttl +++ b/lv2/port-groups.lv2/port-groups.meta.ttl @@ -107,4 +107,3 @@ does not depend on a particular speaker configuration; a decoder can be used to convert an ambisonic stream for any speaker configuration. """^^lv2:Markdown . - diff --git a/lv2/port-groups.lv2/port-groups.ttl b/lv2/port-groups.lv2/port-groups.ttl index 2806821..9fc5c34 100644 --- a/lv2/port-groups.lv2/port-groups.ttl +++ b/lv2/port-groups.lv2/port-groups.ttl @@ -805,4 +805,3 @@ pg:AmbisonicBH3P3Group lv2:index 15 ; lv2:designation pg:ACN15 ] . - diff --git a/lv2/port-props.lv2/manifest.ttl b/lv2/port-props.lv2/manifest.ttl index 45f598d..dd12797 100644 --- a/lv2/port-props.lv2/manifest.ttl +++ b/lv2/port-props.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 2 ; rdfs:seeAlso <port-props.ttl> . - diff --git a/lv2/port-props.lv2/port-props.meta.ttl b/lv2/port-props.lv2/port-props.meta.ttl index 6f65843..e97de45 100644 --- a/lv2/port-props.lv2/port-props.meta.ttl +++ b/lv2/port-props.lv2/port-props.meta.ttl @@ -177,4 +177,3 @@ where: * `lower` and <code>upper</code> are the bounds. """^^lv2:Markdown . - diff --git a/lv2/port-props.lv2/port-props.ttl b/lv2/port-props.lv2/port-props.ttl index ea25c6b..063996a 100644 --- a/lv2/port-props.lv2/port-props.ttl +++ b/lv2/port-props.lv2/port-props.ttl @@ -77,4 +77,3 @@ pprops:rangeSteps rdfs:range xsd:nonNegativeInteger ; rdfs:label "range steps" ; rdfs:comment "The number of even steps the range should be divided into." . - diff --git a/lv2/presets.lv2/manifest.ttl b/lv2/presets.lv2/manifest.ttl index b9cacf5..1508770 100644 --- a/lv2/presets.lv2/manifest.ttl +++ b/lv2/presets.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 2 ; lv2:microVersion 8 ; rdfs:seeAlso <presets.ttl> . - diff --git a/lv2/presets.lv2/presets.meta.ttl b/lv2/presets.lv2/presets.meta.ttl index 53b8ee9..d3e1ef9 100644 --- a/lv2/presets.lv2/presets.meta.ttl +++ b/lv2/presets.lv2/presets.meta.ttl @@ -79,4 +79,3 @@ be useful for saving state, or notifying a plugin instance at run-time about a preset change. """^^lv2:Markdown . - diff --git a/lv2/presets.lv2/presets.ttl b/lv2/presets.lv2/presets.ttl index 60189ea..8de997b 100644 --- a/lv2/presets.lv2/presets.ttl +++ b/lv2/presets.lv2/presets.ttl @@ -58,4 +58,3 @@ pset:preset rdfs:range pset:Preset ; rdfs:label "preset" ; rdfs:comment "The preset currently applied to a plugin instance." . - diff --git a/lv2/resize-port.lv2/manifest.ttl b/lv2/resize-port.lv2/manifest.ttl index 9fae8b8..fb8da8a 100644 --- a/lv2/resize-port.lv2/manifest.ttl +++ b/lv2/resize-port.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 0 ; rdfs:seeAlso <resize-port.ttl> . - diff --git a/lv2/resize-port.lv2/resize-port.meta.ttl b/lv2/resize-port.lv2/resize-port.meta.ttl index 74bd534..ffd1b9b 100644 --- a/lv2/resize-port.lv2/resize-port.meta.ttl +++ b/lv2/resize-port.lv2/resize-port.meta.ttl @@ -59,4 +59,3 @@ property. Any host, especially those that do NOT support dynamic port resizing, SHOULD do so or reduced functionality may result. """^^lv2:Markdown . - diff --git a/lv2/resize-port.lv2/resize-port.ttl b/lv2/resize-port.lv2/resize-port.ttl index 6f42c8f..ffdeda2 100644 --- a/lv2/resize-port.lv2/resize-port.ttl +++ b/lv2/resize-port.lv2/resize-port.ttl @@ -33,4 +33,3 @@ rsz:minimumSize rdfs:range xsd:nonNegativeInteger ; rdfs:label "minimum size" ; rdfs:comment "Minimum buffer size required by a port, in bytes." . - diff --git a/lv2/state.lv2/manifest.ttl b/lv2/state.lv2/manifest.ttl index e56c4e5..6b17b52 100644 --- a/lv2/state.lv2/manifest.ttl +++ b/lv2/state.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 2 ; lv2:microVersion 10 ; rdfs:seeAlso <state.ttl> . - diff --git a/lv2/state.lv2/state.meta.ttl b/lv2/state.lv2/state.meta.ttl index b7b6855..1cd0544 100644 --- a/lv2/state.lv2/state.meta.ttl +++ b/lv2/state.lv2/state.meta.ttl @@ -390,4 +390,3 @@ a change which it knows is likely to have that effect, such as changing a parameter. """^^lv2:Markdown . - diff --git a/lv2/state.lv2/state.ttl b/lv2/state.lv2/state.ttl index 463fdb9..9a5ddcb 100644 --- a/lv2/state.lv2/state.ttl +++ b/lv2/state.lv2/state.ttl @@ -57,4 +57,3 @@ state:StateChanged a rdfs:Class ; rdfs:label "State Changed" ; rdfs:comment "A notification that the internal state of the plugin has changed." . - diff --git a/lv2/time.lv2/manifest.ttl b/lv2/time.lv2/manifest.ttl index d80aa75..ece8679 100644 --- a/lv2/time.lv2/manifest.ttl +++ b/lv2/time.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 6 ; rdfs:seeAlso <time.ttl> . - diff --git a/lv2/time.lv2/time.meta.ttl b/lv2/time.lv2/time.meta.ttl index c0543c3..723a61b 100644 --- a/lv2/time.lv2/time.meta.ttl +++ b/lv2/time.lv2/time.meta.ttl @@ -67,4 +67,3 @@ rate of 0.0 is stopped, 1.0 is rolling at normal speed, 0.5 is rolling at half speed, -1.0 is reverse, and so on. """^^lv2:Markdown . - diff --git a/lv2/time.lv2/time.ttl b/lv2/time.lv2/time.ttl index f3da9b0..f9c65ee 100644 --- a/lv2/time.lv2/time.ttl +++ b/lv2/time.lv2/time.ttl @@ -118,4 +118,3 @@ time:speed rdfs:range xsd:float ; rdfs:label "speed" ; rdfs:comment "The rate of the progress of time as a fraction of normal speed." . - diff --git a/lv2/ui.lv2/manifest.ttl b/lv2/ui.lv2/manifest.ttl index d3b12b5..a77d6d0 100644 --- a/lv2/ui.lv2/manifest.ttl +++ b/lv2/ui.lv2/manifest.ttl @@ -3,7 +3,6 @@ <http://lv2plug.in/ns/extensions/ui> a lv2:Specification ; - lv2:minorVersion 2 ; - lv2:microVersion 24 ; + lv2:minorVersion 3 ; + lv2:microVersion 0 ; rdfs:seeAlso <ui.ttl> . - diff --git a/lv2/ui.lv2/ui.meta.ttl b/lv2/ui.lv2/ui.meta.ttl index fd4d11d..393d8d6 100644 --- a/lv2/ui.lv2/ui.meta.ttl +++ b/lv2/ui.lv2/ui.meta.ttl @@ -99,6 +99,16 @@ not be used in the same process. """^^lv2:Markdown . +ui:Gtk4UI + lv2:documentation """ + +The host must guarantee that the Gtk+ 4 library has been initialised and the +Glib main loop is running before the UI is instantiated. Note that this UI +type is not suitable for binary distribution since multiple versions of Gtk can +not be used in the same process. + +"""^^lv2:Markdown . + ui:Qt4UI lv2:documentation """ @@ -119,6 +129,16 @@ used in the same process. """^^lv2:Markdown . +ui:Qt6UI + lv2:documentation """ + +The host must guarantee that the Qt6 library has been initialised and the Qt6 +main loop is running before the UI is instantiated. Note that this UI type is +not suitable for binary distribution since multiple versions of Qt can not be +used in the same process. + +"""^^lv2:Markdown . + ui:X11UI lv2:documentation """ @@ -476,4 +496,3 @@ Write Effect : None. """^^lv2:Markdown . - diff --git a/lv2/ui.lv2/ui.ttl b/lv2/ui.lv2/ui.ttl index 1c2e455..c42992e 100644 --- a/lv2/ui.lv2/ui.ttl +++ b/lv2/ui.lv2/ui.ttl @@ -34,6 +34,13 @@ ui:Gtk3UI rdfs:label "GTK3 UI" ; rdfs:comment "A UI where the widget is a pointer to a Gtk+ 3.0 GtkWidget." . +ui:Gtk4UI + a rdfs:Class , + owl:Class ; + rdfs:subClassOf ui:UI ; + rdfs:label "GTK4 UI" ; + rdfs:comment "A UI where the widget is a pointer to a Gtk+ 4.0 GtkWidget." . + ui:Qt4UI a rdfs:Class , owl:Class ; @@ -48,6 +55,13 @@ ui:Qt5UI rdfs:label "Qt5 UI" ; rdfs:comment "A UI where the widget is a pointer to a Qt5 QWidget." . +ui:Qt6UI + a rdfs:Class , + owl:Class ; + rdfs:subClassOf ui:UI ; + rdfs:label "Qt6 UI" ; + rdfs:comment "A UI where the widget is a pointer to a Qt6 QWidget." . + ui:X11UI a rdfs:Class , owl:Class ; @@ -81,13 +95,13 @@ ui:binary a rdf:Property , owl:ObjectProperty ; owl:sameAs lv2:binary ; - owl:deprecated "true"^^xsd:boolean ; + owl:deprecated true ; rdfs:label "binary" ; rdfs:comment "The shared library that a UI resides in." . ui:makeSONameResident a lv2:Feature ; - owl:deprecated "true"^^xsd:boolean ; + owl:deprecated true ; rdfs:label "make SO name resident" ; rdfs:comment "UI binary must not be unloaded." . @@ -174,7 +188,7 @@ ui:notifyType ui:resize a lv2:Feature , lv2:ExtensionData ; - owl:deprecated "true"^^xsd:boolean ; + owl:deprecated true ; rdfs:label "resize" ; rdfs:comment """A feature that provides control of, and notifications about, a UI's size.""" . @@ -246,4 +260,3 @@ ui:peakProtocol a ui:PortProtocol ; rdfs:label "peak protocol" ; rdfs:comment "A protocol for sending continuous peak measurements of an audio signal." . - diff --git a/lv2/units.lv2/manifest.ttl b/lv2/units.lv2/manifest.ttl index c6c9286..8b84346 100644 --- a/lv2/units.lv2/manifest.ttl +++ b/lv2/units.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 5 ; lv2:microVersion 12 ; rdfs:seeAlso <units.ttl> . - diff --git a/lv2/units.lv2/units.meta.ttl b/lv2/units.lv2/units.meta.ttl index 910bf46..0e03b35 100644 --- a/lv2/units.lv2/units.meta.ttl +++ b/lv2/units.lv2/units.meta.ttl @@ -55,4 +55,3 @@ units defined in this extension include conversion definitions where it makes sense to do so. """^^lv2:Markdown . - diff --git a/lv2/units.lv2/units.ttl b/lv2/units.lv2/units.ttl index 21a1898..0f94cc3 100644 --- a/lv2/units.lv2/units.ttl +++ b/lv2/units.lv2/units.ttl @@ -375,4 +375,3 @@ units:midiNote rdfs:comment "A MIDI note number." ; units:render "MIDI note %d" ; units:symbol "note" . - diff --git a/lv2/uri-map.lv2/manifest.ttl b/lv2/uri-map.lv2/manifest.ttl index a64e4fb..3824435 100644 --- a/lv2/uri-map.lv2/manifest.ttl +++ b/lv2/uri-map.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 6 ; rdfs:seeAlso <uri-map.ttl> . - diff --git a/lv2/uri-map.lv2/uri-map.meta.ttl b/lv2/uri-map.lv2/uri-map.meta.ttl index d531899..9615f31 100644 --- a/lv2/uri-map.lv2/uri-map.meta.ttl +++ b/lv2/uri-map.lv2/uri-map.meta.ttl @@ -26,4 +26,3 @@ extensibility of RDF with the performance of integers (or centrally defined enumerations). """^^lv2:Markdown . - diff --git a/lv2/uri-map.lv2/uri-map.ttl b/lv2/uri-map.lv2/uri-map.ttl index 7a7d6e3..8a7a043 100644 --- a/lv2/uri-map.lv2/uri-map.ttl +++ b/lv2/uri-map.lv2/uri-map.ttl @@ -10,4 +10,3 @@ rdfs:label "LV2 URI Map" ; rdfs:comment "A feature for mapping URIs to integers." ; rdfs:seeAlso <uri-map.meta.ttl> . - diff --git a/lv2/urid.lv2/manifest.ttl b/lv2/urid.lv2/manifest.ttl index 772e2b6..7dc919f 100644 --- a/lv2/urid.lv2/manifest.ttl +++ b/lv2/urid.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 4 ; rdfs:seeAlso <urid.ttl> . - diff --git a/lv2/urid.lv2/urid.meta.ttl b/lv2/urid.lv2/urid.meta.ttl index 6f64e69..cbc5153 100644 --- a/lv2/urid.lv2/urid.meta.ttl +++ b/lv2/urid.lv2/urid.meta.ttl @@ -47,4 +47,3 @@ LV2_Descriptor::instantiate() with URI LV2_URID__unmap and data pointed to an instance of LV2_URID_Unmap. """^^lv2:Markdown . - diff --git a/lv2/urid.lv2/urid.ttl b/lv2/urid.lv2/urid.ttl index 6ce666a..e875fba 100644 --- a/lv2/urid.lv2/urid.ttl +++ b/lv2/urid.lv2/urid.ttl @@ -19,4 +19,3 @@ urid:unmap a lv2:Feature ; rdfs:label "unmap" ; rdfs:comment "A feature to unmap URIDs back to strings." . - diff --git a/lv2/worker.lv2/manifest.ttl b/lv2/worker.lv2/manifest.ttl index 692720d..cf4a83f 100644 --- a/lv2/worker.lv2/manifest.ttl +++ b/lv2/worker.lv2/manifest.ttl @@ -6,4 +6,3 @@ lv2:minorVersion 1 ; lv2:microVersion 2 ; rdfs:seeAlso <worker.ttl> . - diff --git a/lv2/worker.lv2/worker.meta.ttl b/lv2/worker.lv2/worker.meta.ttl index 0918446..b9d651a 100644 --- a/lv2/worker.lv2/worker.meta.ttl +++ b/lv2/worker.lv2/worker.meta.ttl @@ -57,4 +57,3 @@ the plugin MAY use it to schedule work in the calling context. The plugin MUST NOT assume any relationship between different schedule features. """^^lv2:Markdown . - diff --git a/lv2/worker.lv2/worker.ttl b/lv2/worker.lv2/worker.ttl index 581be71..be07105 100644 --- a/lv2/worker.lv2/worker.ttl +++ b/lv2/worker.lv2/worker.ttl @@ -21,4 +21,3 @@ work:schedule a lv2:Feature ; rdfs:label "work schedule" ; rdfs:comment "The work scheduling feature provided by a host." . - diff --git a/lv2specgen/lv2docgen.py b/lv2specgen/lv2docgen.py index 9b1685a..271e434 100755 --- a/lv2specgen/lv2docgen.py +++ b/lv2specgen/lv2docgen.py @@ -3,6 +3,13 @@ # Copyright 2012 David Robillard <d@drobilla.net> # SPDX-License-Identifier: ISC +"""LV2 plugin documentation generator.""" + +# pylint: disable=consider-using-f-string +# pylint: disable=missing-function-docstring +# pylint: disable=redefined-outer-name +# pylint: disable=invalid-name + import errno import os import sys @@ -93,10 +100,10 @@ def plugin_doc(model, plugin, style_uri): html += get_doc(model, plugin) ports_html = "" - for p in model.triples([plugin, lv2.port, None]): - ports_html += port_doc(model, p[2]) + for link in model.triples([plugin, lv2.port, None]): + ports_html += port_doc(model, link[2]) - if len(ports_html): + if ports_html: html += ( """ <h2 class="sec">Ports</h2> @@ -111,8 +118,6 @@ def plugin_doc(model, plugin, style_uri): if __name__ == "__main__": - "LV2 plugin documentation generator" - if len(sys.argv) < 2: print("Usage: %s OUTDIR FILE..." % sys.argv[0]) sys.exit(1) @@ -140,5 +145,5 @@ if __name__ == "__main__": raise print("Writing <%s> documentation to %s" % (plugin, outpath)) - with open(outpath, "w") as out: + with open(outpath, "w", encoding="utf-8") as out: out.write(html) diff --git a/lv2specgen/lv2specgen.py b/lv2specgen/lv2specgen.py index 95702a5..7927261 100755 --- a/lv2specgen/lv2specgen.py +++ b/lv2specgen/lv2specgen.py @@ -9,17 +9,39 @@ # Based on SpecGen: # <http://forge.morfeo-project.org/wiki_en/index.php/SpecGen> +"""Ontology specification generator tool.""" + +# pylint: disable=broad-exception-caught +# pylint: disable=cell-var-from-loop +# pylint: disable=consider-using-f-string +# pylint: disable=deprecated-module +# pylint: disable=global-statement +# pylint: disable=global-variable-not-assigned +# pylint: disable=invalid-name +# pylint: disable=missing-function-docstring +# pylint: disable=no-member +# pylint: disable=redefined-outer-name +# pylint: disable=too-many-arguments +# pylint: disable=too-many-boolean-expressions +# pylint: disable=too-many-branches +# pylint: disable=too-many-lines +# pylint: disable=too-many-locals +# pylint: disable=too-many-positional-arguments +# pylint: disable=too-many-statements +# pylint: disable=unused-argument + import datetime -import markdown -import markdown.extensions import optparse import os import re import sys import time -import xml.sax.saxutils import xml.dom import xml.dom.minidom +import xml.sax.saxutils + +import markdown +import markdown.extensions __date__ = "2011-10-26" __version__ = __date__.replace("-", ".") @@ -111,33 +133,34 @@ def getLiteralString(s): def isResource(n): - return type(n) == rdflib.URIRef + return isinstance(n, rdflib.URIRef) def isBlank(n): - return type(n) == rdflib.BNode + return isinstance(n, rdflib.BNode) def isLiteral(n): - return type(n) == rdflib.Literal + return isinstance(n, rdflib.Literal) def niceName(uri): - global spec_bundle if uri.startswith(spec_ns_str): return uri.replace(spec_ns_str, "") - elif uri == str(rdfs.seeAlso): + + if uri == str(rdfs.seeAlso): return "See also" regexp = re.compile("^(.*[/#])([^/#]+)$") rez = regexp.search(uri) if not rez: return uri + pref = rez.group(1) if pref in ns_list: return ns_list.get(pref, pref) + ":" + rez.group(2) - else: - return uri + + return uri def termName(m, urinode): @@ -149,17 +172,17 @@ def getLabel(m, urinode): statement = findOne(m, urinode, rdfs.label, None) if statement: return getLiteralString(getObject(statement)) - else: - return "" + + return "" def linkifyCodeIdentifiers(string): "Add links to code documentation for identifiers like LV2_Type" - if linkmap == {}: + if not linkmap: return string - if string in linkmap.keys(): + if string in linkmap: # Exact match for complete string return linkmap[string] @@ -194,13 +217,14 @@ def linkifyVocabIdentifiers(m, string, classlist, proplist, instalist): ): print("warning: Link to undefined resource <%s>\n" % text) return '<a href="#%s">%s</a>' % (name, name) - elif prefix in namespaces: + + if prefix in namespaces: return '<a href="%s">%s</a>' % ( namespaces[match.group(1)] + match.group(2), match.group(0), ) - else: - return text + + return text return rgx.sub(translateLink, string) @@ -256,15 +280,17 @@ def prettifyHtml(m, markup, subject, classlist, proplist, instalist): or (proplist and uri in proplist) ): return '%s<a href="#%s">%s</a>' % (space, name, name) - else: - print("warning: Link to undefined resource <%s>\n" % name) - return text + + print("warning: Link to undefined resource <%s>\n" % name) + return text markup = rgx.sub(translateLocalLink, markup) if not have_lxml: print("warning: No Python lxml module found, output may be invalid") else: + oldcwd = os.getcwd() + try: # Parse and validate documentation as XHTML Basic 1.1 doc = ( @@ -283,7 +309,6 @@ def prettifyHtml(m, markup, subject, classlist, proplist, instalist): </html>""" ) - oldcwd = os.getcwd() os.chdir(specgendir) parser = etree.XMLParser(dtd_validation=True, no_network=True) etree.fromstring(doc.encode("utf-8"), parser) @@ -317,11 +342,11 @@ def formatDoc(m, urinode, literal, classlist, proplist, instalist): doc = doc.replace("</%s>\n" % tag, "") return prettifyHtml(m, doc, urinode, classlist, proplist, instalist) - else: - doc = xml.sax.saxutils.escape(string) - doc = linkifyCodeIdentifiers(doc) - doc = linkifyVocabIdentifiers(m, doc, classlist, proplist, instalist) - return "<p>%s</p>" % doc + + doc = xml.sax.saxutils.escape(string) + doc = linkifyCodeIdentifiers(doc) + doc = linkifyVocabIdentifiers(m, doc, classlist, proplist, instalist) + return "<p>%s</p>" % doc def getComment(m, subject, classlist, proplist, instalist): @@ -374,16 +399,11 @@ def getProperty(val, first=True): def endProperties(first): - if first: - return "</tr>" - else: - return "" + return "</tr>" if first else "" def rdfsPropertyInfo(term, m): """Generate HTML for properties: Domain, range""" - global classranges - global classdomains doc = "" label = getLabel(m, term) @@ -478,8 +498,8 @@ def getTermLink(uri, subject=None, predicate=None): extra, niceName(uri), ) - else: - return '<a href="%s" %s>%s</a>' % (uri, extra, niceName(uri)) + + return '<a href="%s" %s>%s</a>' % (uri, extra, niceName(uri)) def owlRestrictionInfo(term, m): @@ -537,8 +557,6 @@ def owlRestrictionInfo(term, m): def rdfsClassInfo(term, m): """Generate rdfs-type information for Classes: ranges, and domains.""" - global classranges - global classdomains doc = "" label = getLabel(m, term) @@ -707,8 +725,8 @@ def owlInfo(term, m): def owlTypeInfo(term, propertyType, name): if findOne(m, term, rdf.type, propertyType): return "<tr><th>Type</th><td>%s</td></tr>\n" % name - else: - return "" + + return "" res += owlTypeInfo(term, owl.DatatypeProperty, "Datatype Property") res += owlTypeInfo(term, owl.ObjectProperty, "Object Property") @@ -726,14 +744,14 @@ def isDeprecated(m, subject): return deprecated and (str(deprecated[2]).find("true") >= 0) -def docTerms(category, list, m, classlist, proplist, instalist): +def docTerms(category, termlist, m, classlist, proplist, instalist): """ A wrapper class for listing all the terms in a specific class (either Properties, or Classes. Category is 'Property' or 'Class', list is a list of term URI strings, return value is a chunk of HTML. """ doc = "" - for term in list: + for term in termlist: if not term.startswith(spec_ns_str): continue @@ -792,17 +810,17 @@ def docTerms(category, list, m, classlist, proplist, instalist): def getShortName(uri): uri = str(uri) if "#" in uri: - return uri.split("#")[-1] - else: - return uri.split("/")[-1] + return uri.split("#", maxsplit=1)[-1] + + return uri.split("/", maxsplit=1)[-1] def getAnchor(uri): uri = str(uri) if uri.startswith(spec_ns_str): return uri.replace(spec_ns_str, "").replace("/", "_") - else: - return getShortName(uri) + + return getShortName(uri) def buildIndex(m, classlist, proplist, instalist=None): @@ -816,8 +834,8 @@ def buildIndex(m, classlist, proplist, instalist=None): if str(t).startswith(spec_ns_str): name = termName(m, t) return '<a href="#%s">%s</a>' % (name, name) - else: - return '<a href="%s">%s</a>' % (str(t), str(t)) + + return '<a href="%s">%s</a>' % (str(t), str(t)) if len(classlist) > 0: head += '<th><a href="#ref-classes" />Classes</th>' @@ -900,22 +918,20 @@ def specInformation(m, ns): and proplist. Global variables classranges and classdomains are also filled as appropriate. """ - global classranges - global classdomains # Find the class information: Ranges, domains, and list of all names. classtypes = [rdfs.Class, owl.Class, rdfs.Datatype] classlist = [] for onetype in classtypes: for classStatement in findStatements(m, None, rdf.type, onetype): - for range in findStatements( + for rangelink in findStatements( m, None, rdfs.range, getSubject(classStatement) ): if not isBlank(getSubject(classStatement)): add( classranges, str(getSubject(classStatement)), - str(getSubject(range)), + str(getSubject(rangelink)), ) for domain in findStatements( m, None, rdfs.domain, getSubject(classStatement) @@ -1002,15 +1018,16 @@ def specAuthors(m, subject): maintdoc = "" first = True - for m in sorted(maint): + for name in sorted(maint): if not first: maintdoc += ", " - maintdoc += ( - f'<span class="author" property="doap:maintainer">{m}</span>' - ) + maintdoc += '<span class="author" property="doap:maintainer">' + maintdoc += name + maintdoc += "</span>" first = False - if len(maint): + + if maint: label = "Maintainer" if len(maint) == 1 else "Maintainers" doc += f'<tr><th class="metahead">{label}</th><td>{maintdoc}</td></tr>' @@ -1096,12 +1113,12 @@ def load_tags(path, docdir): anchor, sym, ) - else: - return '<span><a href="%s/%s">%s</a></span>' % ( - docdir, - filename, - sym, - ) + + return '<span><a href="%s/%s">%s</a></span>' % ( + docdir, + filename, + sym, + ) tagdoc = xml.dom.minidom.parse(path) root = tagdoc.documentElement @@ -1112,7 +1129,6 @@ def load_tags(path, docdir): and cn.tagName == "compound" and cn.getAttribute("kind") != "page" ): - name = getChildText(cn, "name") filename = getChildText(cn, "filename") anchor = getChildText(cn, "anchor") @@ -1152,14 +1168,12 @@ def specgen( global spec_ns_str global spec_ns global spec_pre - global ns_list - global specgendir global linkmap spec_bundle = "file://%s/" % os.path.abspath(os.path.dirname(specloc)) # Template - with open(template_path, "r") as f: + with open(template_path, "r", encoding="utf-8") as f: template = f.read() # Load code documentation link map from tags file @@ -1244,6 +1258,7 @@ def specgen( proplist = docTerms( "Property", proplist, m, classlist, proplist, instalist ) + instlist = "" if instances: instlist = docTerms( "Instance", instalist, m, classlist, proplist, instalist @@ -1328,13 +1343,13 @@ def specgen( template = template.replace("@DESCRIPTION@", docs) now = int(os.environ.get("SOURCE_DATE_EPOCH", time.time())) - build_date = datetime.datetime.utcfromtimestamp(now) + build_date = datetime.datetime.fromtimestamp(now, datetime.timezone.utc) template = template.replace("@DATE@", build_date.strftime("%F")) template = template.replace("@TIME@", build_date.strftime("%F %H:%M UTC")) # Validate complete output page + oldcwd = os.getcwd() try: - oldcwd = os.getcwd() os.chdir(specgendir) etree.fromstring( template.replace( @@ -1355,7 +1370,7 @@ def specgen( def save(path, text): try: - with open(path, "w") as f: + with open(path, "w", encoding="utf-8") as f: f.write(text) f.flush() except Exception: @@ -1414,8 +1429,6 @@ def _data_dirs(): if __name__ == "__main__": - """Ontology specification generator tool""" - data_dir = None for d in _data_dirs(): path = os.path.join(d, "lv2specgen") diff --git a/lv2specgen/meson.build b/lv2specgen/meson.build index 5407d69..bc3a616 100644 --- a/lv2specgen/meson.build +++ b/lv2specgen/meson.build @@ -1,4 +1,4 @@ -# Copyright 2022 David Robillard <d@drobilla.net> +# Copyright 2022-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC lv2specgen_py = files('lv2specgen.py') @@ -16,28 +16,30 @@ lv2specgen_command_prefix = [ if is_variable('lv2_tags') lv2specgen_command_prefix += [ - '--tags', lv2_tags.full_path(), # TODO: Remove full_path() in meson 0.60.0 + ['--tags', lv2_tags.full_path()], # TODO: Remove full_path() in meson 0.60.0 ] endif -install_data( - files('lv2specgen.py'), - install_dir: get_option('bindir'), - install_mode: 'rwxr-xr-x', -) - meson.override_find_program('lv2specgen.py', lv2specgen_py) -install_data( - files( - '../doc/style/pygments.css', - '../doc/style/style.css', - 'template.html', - ), - install_dir: get_option('datadir') / 'lv2specgen', -) - -install_subdir( - 'DTD', - install_dir: get_option('datadir') / 'lv2specgen', -) +if not get_option('tools').disabled() + install_data( + files('lv2specgen.py'), + install_dir: get_option('bindir'), + install_mode: 'rwxr-xr-x', + ) + + install_data( + files( + '../doc/style/pygments.css', + '../doc/style/style.css', + 'template.html', + ), + install_dir: get_option('datadir') / 'lv2specgen', + ) + + install_subdir( + 'DTD', + install_dir: get_option('datadir') / 'lv2specgen', + ) +endif diff --git a/meson.build b/meson.build index 6c10fad..669c139 100644 --- a/meson.build +++ b/meson.build @@ -1,41 +1,43 @@ -# Copyright 2021-2022 David Robillard <d@drobilla.net> +# Copyright 2021-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC -project('lv2', ['c'], - version: '1.18.9', - license: 'ISC', - meson_version: '>= 0.56.0', - default_options: [ - 'b_ndebug=if-release', - 'buildtype=release', - 'c_std=c99', - ]) +project( + 'lv2', + ['c'], + default_options: [ + 'b_ndebug=if-release', + 'buildtype=release', + 'c_std=c99', + 'c_winlibs=', + 'cpp_winlibs=', + ], + license: 'ISC', + meson_version: '>= 0.56.0', + version: '1.18.11', +) lv2_docdir = get_option('datadir') / 'doc' / 'lv2' lv2_source_root = meson.current_source_dir() lv2_build_root = meson.current_build_dir() -####################### -# Compilers and Flags # -####################### - -# Required tools -pkg = import('pkgconfig') -pymod = import('python') -cc = meson.get_compiler('c') +installing_anything = ( + get_option('bundles') or + get_option('headers') or + get_option('old_headers') +) -# Optional C++ compiler and Python tools for tests -if not get_option('tests').disabled() - if add_languages(['cpp'], native: false, required: get_option('tests')) - cpp = meson.get_compiler('cpp') - endif -endif +############### +# Build Tools # +############### -# Set global warning flags -if get_option('strict') and not meson.is_subproject() - subdir('meson/warnings') +pymod = disabler() +if ( + get_option('bundles') + or get_option('lint') + or not get_option('docs').disabled() +) + pymod = import('python') endif -subdir('meson/suppressions') ########################## # LV2 Path Configuration # @@ -59,28 +61,34 @@ endif # Package/Dependency # ###################### -# Generage pkg-config file for external dependants -pkg.generate( - description: 'Plugin standard for audio systems', - filebase: 'lv2', - name: 'LV2', - variables: [ - 'lv2dir=' + lv2dir, - 'plugindir=' + lv2dir, - ], - version: meson.project_version(), -) +# Generate pkg-config file for external dependants +if installing_anything + pkg = import('pkgconfig') + pkg.generate( + description: 'Plugin standard for audio systems', + filebase: 'lv2', + name: 'LV2', + variables: [ + 'lv2dir=' + lv2dir, + 'lv2specdatadir=' + lv2dir, + ], + version: meson.project_version(), + ) +endif # Declare dependency for internal meson dependants lv2_dep = declare_dependency( include_directories: include_directories('include'), variables: [ - 'lv2dir=' + lv2_source_root / 'lv2', - 'plugindir=' + lv2_build_root / 'plugins', + 'lv2dir=' + lv2dir, + 'lv2specdatadir=' + lv2_source_root / 'lv2', ], version: meson.project_version(), ) +# Override pkg-config dependency for internal meson dependants +meson.override_dependency('lv2', lv2_dep) + ################## # Specifications # ################## @@ -129,7 +137,9 @@ all_spec_names = ['core'] + ext_names + extensions_names prefix = get_option('prefix') includedir = get_option('includedir') -install_subdir('include/lv2', install_dir: includedir) +if get_option('headers') + install_subdir('include/lv2', install_dir: includedir) +endif if get_option('old_headers') uri_include_dir = prefix / includedir / 'lv2' / 'lv2plug.in' / 'ns' @@ -179,34 +189,41 @@ subdir('schemas.lv2') lv2_check_specification = files('scripts' / 'lv2_check_specification.py') -check_python = pymod.find_installation( - 'python3', - modules: ['rdflib'], - required: get_option('tests'), -) - -if (check_python.found() and - check_python.language_version().version_compare('<3.7')) - warning('Python 3.7 is required for tests') +if get_option('bundles') check_python = disabler() -endif - -foreach bundle_name : all_spec_names - bundle = 'lv2' / bundle_name + '.lv2' + if not get_option('tests').disabled() + check_python = pymod.find_installation( + 'python3', + modules: ['rdflib'], + required: get_option('tests'), + ) - # Check specification - if check_python.found() - test( - bundle_name, - lv2_check_specification, - args: files(bundle / 'manifest.ttl'), - suite: ['spec'], + if ( + check_python.found() + and check_python.language_version().version_compare('<3.7') ) + warning('Python 3.7 is required for tests') + check_python = disabler() + endif endif - # Install specification bundle - install_subdir(bundle, install_dir: lv2dir) -endforeach + foreach bundle_name : all_spec_names + bundle = 'lv2' / bundle_name + '.lv2' + + # Check specification + if check_python.found() + test( + bundle_name, + lv2_check_specification, + args: files(bundle / 'manifest.ttl'), + suite: ['spec'], + ) + endif + + # Install specification bundle + install_subdir(bundle, install_dir: lv2dir) + endforeach +endif spec_files = files( 'lv2/atom.lv2/atom.meta.ttl', @@ -290,49 +307,34 @@ spec_files = files( ################# # Determine if all the dependencies for building documentation are present -doxygen = find_program('doxygen', required: get_option('docs')) build_docs = false -build_lv2specgen = false -doc_deps = [] if not get_option('docs').disabled() - doc_python_modules = ['lxml', 'markdown', 'pygments', 'rdflib'] - - python = pymod.find_installation( + doxygen = find_program('doxygen', required: get_option('docs')) + doc_python = pymod.find_installation( 'python3', - modules: doc_python_modules, + modules: ['lxml', 'markdown', 'pygments', 'rdflib'], required: get_option('docs'), ) - if python.found() and python.language_version().version_compare('<3.7') + build_docs = doxygen.found() and doc_python.found() + if doc_python.found() and doc_python.language_version().version_compare('<3.7') warning('Python 3.7 is required for documentation') build_docs = false endif - - build_docs = doxygen.found() and python.found() - build_lv2specgen = python.found() -endif - -# Run Doxygen first to generate tags -subdir('doc/c') - -# Set up lv2specgen and lv2specgen_command_prefix (which references tags) -if build_lv2specgen - subdir('lv2specgen') endif -# Generate specification documentation if build_docs - subdir('doc/style') - subdir('doc/ns') -endif + # Run Doxygen first to generate tags + subdir('doc/c') -########### -# Plugins # -########### + # Set up lv2specgen and lv2specgen_command_prefix (which references tags) + subdir('lv2specgen') -# Example plugins and "Programming LV2 Plugins" book -if not get_option('plugins').disabled() - subdir('plugins') + # Generate specification documentation + if build_docs + subdir('doc/style') + subdir('doc/ns') + endif endif ############ @@ -340,15 +342,31 @@ endif ############ # Command-line utilities -subdir('util') +if not get_option('tools').disabled() + subdir('util') +endif # Data and build tests -subdir('test') +if not get_option('tests').disabled() + subdir('test') +endif if not meson.is_subproject() - summary('Tests', not get_option('tests').disabled(), bool_yn: true) - summary('Documentation', build_docs, bool_yn: true) - summary('Prefix', get_option('prefix'), section: 'Paths') - summary('LV2 bundles', lv2dir, section: 'Paths') - summary('Headers', get_option('prefix') / get_option('includedir'), section: 'Paths') + summary( + { + 'Tests': not get_option('tests').disabled(), + 'Documentation': build_docs, + }, + bool_yn: true, + section: 'Components', + ) + + summary( + { + 'Install prefix': get_option('prefix'), + 'Headers': get_option('prefix') / get_option('includedir'), + 'LV2 bundles': lv2dir, + }, + section: 'Directories', + ) endif diff --git a/meson/library/meson.build b/meson/library/meson.build deleted file mode 100644 index 81aab96..0000000 --- a/meson/library/meson.build +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -# General definitions for building libraries. -# -# These are essentially workarounds for meson and/or MSVC. Unfortunately, -# meson's default_library option doesn't support shared and static builds very -# well. In particular, it's often necessary to define different symbols for -# static and shared builds of libraries so that symbols can be exported. To -# work around this, we do not support default_library=both on Windows. On -# other platforms with GCC-like compilers, we can support both because symbols -# can safely be exported in the same way (giving them default visibility) in -# both static and shared builds. - -# Abort on Windows with default_library=both -if get_option('default_library') == 'both' - if host_machine.system() == 'windows' - error('default_library=both is not supported on Windows') - endif -endif - -# Set library_suffix to the suffix for libraries -if cc.get_id() == 'msvc' - # Meson appends a version to the name only on MS, which leads to inconsistent - # library names, like `mylib-1-1`. So, provide no suffix to ultimately get - # the same name as on other platforms, like `mylib-1`. - library_suffix = '' -else - library_suffix = '-@0@'.format(meson.project_version().split('.')[0]) -endif diff --git a/meson/suppressions/meson.build b/meson/suppressions/meson.build deleted file mode 100644 index 66510f9..0000000 --- a/meson/suppressions/meson.build +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -# Project-specific warning suppressions. -# -# This should be used in conjunction with the generic "warnings" sibling that -# enables all reasonable warnings for the compiler. It lives here just to keep -# the top-level meson.build more readable. - -##### -# C # -##### - -if is_variable('cc') - c_suppressions = [] - - if get_option('strict') - if cc.get_id() in ['clang', 'emscripten'] - c_suppressions += [ - '-Wno-bad-function-cast', - '-Wno-cast-align', - '-Wno-cast-qual', - '-Wno-declaration-after-statement', - '-Wno-documentation-unknown-command', - '-Wno-double-promotion', - '-Wno-float-conversion', - '-Wno-float-equal', - '-Wno-implicit-float-conversion', - '-Wno-padded', - '-Wno-reserved-id-macro', - '-Wno-shorten-64-to-32', - '-Wno-sign-conversion', - '-Wno-switch-enum', - '-Wno-unused-parameter', - ] - elif cc.get_id() == 'gcc' - c_suppressions += [ - '-Wno-bad-function-cast', - '-Wno-cast-align', - '-Wno-cast-qual', - '-Wno-conversion', - '-Wno-double-promotion', - '-Wno-float-equal', - '-Wno-inline', - '-Wno-padded', - '-Wno-suggest-attribute=const', - '-Wno-suggest-attribute=malloc', - '-Wno-suggest-attribute=pure', - '-Wno-switch-default', - '-Wno-switch-enum', - '-Wno-unsuffixed-float-constants', - '-Wno-unused-const-variable', - '-Wno-unused-parameter', - ] - - if target_machine.system() == 'windows' - c_suppressions += [ - '-Wno-suggest-attribute=format', - ] - endif - - elif cc.get_id() == 'msvc' - c_suppressions += [ - '/wd4061', # enumerator in switch is not explicitly handled - '/wd4100', # unreferenced formal parameter - '/wd4244', # conversion with possible loss of data - '/wd4267', # conversion from size_t to a smaller type - '/wd4310', # cast truncates constant value - '/wd4365', # signed/unsigned mismatch - '/wd4464', # relative include path contains ".." - '/wd4514', # unreferenced inline function has been removed - '/wd4514', # unreferenced inline function has been removed - '/wd4706', # assignment within conditional expression - '/wd4710', # function not inlined - '/wd4711', # function selected for automatic inline expansion - '/wd4820', # padding added after construct - '/wd5045', # will insert Spectre mitigation for memory load - ] - endif - endif - - c_suppressions = cc.get_supported_arguments(c_suppressions) -endif - -####### -# C++ # -####### - -if is_variable('cpp') - cpp_suppressions = [] - - if get_option('strict') - if cpp.get_id() in ['clang', 'emscripten'] - cpp_suppressions = [ - '-Wno-cast-align', - '-Wno-cast-qual', - '-Wno-documentation-unknown-command', - '-Wno-nullability-extension', - '-Wno-padded', - '-Wno-reserved-id-macro', - ] - - elif cpp.get_id() == 'gcc' - cpp_suppressions = [ - '-Wno-cast-align', - '-Wno-cast-qual', - '-Wno-inline', - '-Wno-padded', - '-Wno-unused-const-variable', - '-Wno-useless-cast', - ] - - if target_machine.system() == 'windows' - cpp_suppressions += [ - '-Wno-suggest-attribute=format', - ] - endif - - elif cpp.get_id() == 'msvc' - cpp_suppressions = [ - '/wd4514', # unreferenced inline function has been removed - '/wd4706', # assignment within conditional expression - '/wd4710', # function not inlined - '/wd4711', # function selected for automatic inline expansion - '/wd4820', # padding added after data member - '/wd5045', # will insert Spectre mitigation - ] - endif - endif - - cpp_suppressions = cpp.get_supported_arguments(cpp_suppressions) -endif diff --git a/meson/warnings/meson.build b/meson/warnings/meson.build deleted file mode 100644 index 089f292..0000000 --- a/meson/warnings/meson.build +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -# General code to enable approximately all warnings in GCC 12, clang, and MSVC. -# -# This is trivial for clang and MSVC, but GCC doesn't have an "everything" -# option, so we need to enable everything we want explicitly. Wall is assumed, -# but Wextra is not, for stability. -# -# These are collected from common.opt and c.opt in the GCC source, and manually -# curated with the help of the GCC documentation. Warnings that are -# application-specific, historical, or about compatibility between specific -# language revisions are omitted. The intent here is to have roughly the same -# meaning as clang's Weverything: extremely strict, but general. Specifically -# omitted are: -# -# General: -# -# Wabi= -# Waggregate-return -# Walloc-size-larger-than=BYTES -# Walloca-larger-than=BYTES -# Wframe-larger-than=BYTES -# Wlarger-than=BYTES -# Wstack-usage=BYTES -# Wsystem-headers -# Wtraditional -# Wtraditional-conversion -# Wtrampolines -# Wvla-larger-than=BYTES -# -# Build specific: -# -# Wpoison-system-directories -# -# C Specific: -# -# Wc11-c2x-compat -# Wc90-c99-compat -# Wc99-c11-compat -# Wdeclaration-after-statement -# Wtraditional -# Wtraditional-conversion -# -# C++ Specific: -# -# Wc++0x-compat -# Wc++1z-compat -# Wc++2a-compat -# Wctad-maybe-unsupported -# Wnamespaces -# Wtemplates - -# GCC warnings that apply to all C-family languages -gcc_common_warnings = [ - '-Walloc-zero', - '-Walloca', - '-Wanalyzer-too-complex', - '-Warith-conversion', - '-Warray-bounds=2', - '-Wattribute-alias=2', - '-Wbidi-chars=ucn', - '-Wcast-align=strict', - '-Wcast-function-type', - '-Wcast-qual', - '-Wclobbered', - '-Wconversion', - '-Wdate-time', - '-Wdisabled-optimization', - '-Wdouble-promotion', - '-Wduplicated-branches', - '-Wduplicated-cond', - '-Wempty-body', - '-Wendif-labels', - '-Wfloat-equal', - '-Wformat-overflow=2', - '-Wformat-signedness', - '-Wformat-truncation=2', - '-Wformat=2', - '-Wignored-qualifiers', - '-Wimplicit-fallthrough=3', - '-Winit-self', - '-Winline', - '-Winvalid-pch', - '-Wlogical-op', - '-Wmissing-declarations', - '-Wmissing-field-initializers', - '-Wmissing-include-dirs', - '-Wmultichar', - '-Wnormalized=nfc', - '-Wnull-dereference', - '-Wopenacc-parallelism', - '-Woverlength-strings', - '-Wpacked', - '-Wpacked-bitfield-compat', - '-Wpadded', - '-Wpointer-arith', - '-Wredundant-decls', - '-Wshadow', - '-Wshift-negative-value', - '-Wshift-overflow=2', - '-Wstack-protector', - '-Wstrict-aliasing=3', - '-Wstrict-overflow=5', - '-Wstring-compare', - '-Wstringop-overflow=3', - '-Wsuggest-attribute=cold', - '-Wsuggest-attribute=const', - '-Wsuggest-attribute=format', - '-Wsuggest-attribute=malloc', - '-Wsuggest-attribute=noreturn', - '-Wsuggest-attribute=pure', - '-Wswitch-default', - '-Wswitch-enum', - '-Wtrampolines', - '-Wtrivial-auto-var-init', - '-Wtype-limits', - '-Wundef', - '-Wuninitialized', - '-Wunsafe-loop-optimizations', - '-Wunused', - '-Wunused-const-variable=2', - '-Wunused-macros', - '-Wvector-operation-performance', - '-Wvla', - '-Wwrite-strings', -] - -##### -# C # -##### - -if is_variable('cc') and not is_variable('all_c_warnings') - # Set all_c_warnings for the current C compiler - all_c_warnings = [] - - if get_option('strict') - if cc.get_id() == 'clang' - all_c_warnings += ['-Weverything'] - - if not meson.is_cross_build() - all_c_warnings += [ - '-Wno-poison-system-directories', - ] - endif - - elif cc.get_id() == 'gcc' - all_c_warnings += gcc_common_warnings + [ - '-Wabsolute-value', - '-Wbad-function-cast', - '-Wc++-compat', - '-Wenum-conversion', - '-Wjump-misses-init', - '-Wmissing-parameter-type', - '-Wmissing-prototypes', - '-Wnested-externs', - '-Wold-style-declaration', - '-Wold-style-definition', - '-Woverride-init', - '-Wsign-compare', - '-Wstrict-prototypes', - '-Wunsuffixed-float-constants', - ] - - elif cc.get_id() == 'msvc' - all_c_warnings += [ - '/Wall', - '/experimental:external', - '/external:W0', - '/external:anglebrackets', - ] - endif - endif - - all_c_warnings = cc.get_supported_arguments(all_c_warnings) - add_global_arguments(all_c_warnings, language: ['c']) -endif - -####### -# C++ # -####### - -if is_variable('cpp') and not is_variable('all_cpp_warnings') - # Set all_cpp_warnings for the current C++ compiler - all_cpp_warnings = [] - - if get_option('strict') - if cpp.get_id() == 'clang' - all_cpp_warnings += [ - '-Weverything', - '-Wno-c++98-compat', - '-Wno-c++98-compat-pedantic', - ] - - if not meson.is_cross_build() - all_cpp_warnings += [ - '-Wno-poison-system-directories', - ] - endif - - elif cpp.get_id() == 'gcc' - all_cpp_warnings += gcc_common_warnings + [ - '-Wabi-tag', - '-Waligned-new=all', - '-Wcatch-value=3', - '-Wcomma-subscript', - '-Wconditionally-supported', - '-Wctor-dtor-privacy', - '-Wdelete-non-virtual-dtor', - '-Wdeprecated', - '-Wdeprecated-copy', - '-Wdeprecated-copy-dtor', - '-Wdeprecated-enum-enum-conversion', - '-Wdeprecated-enum-float-conversion', - '-Weffc++', - '-Wexpansion-to-defined', - '-Wextra-semi', - '-Wimport', - '-Winvalid-imported-macros', - '-Wmismatched-tags', - '-Wmultiple-inheritance', - '-Wnoexcept', - '-Wnoexcept-type', - '-Wnon-virtual-dtor', - '-Wold-style-cast', - '-Woverloaded-virtual', - '-Wplacement-new=2', - '-Wredundant-move', - '-Wredundant-tags', - '-Wregister', - '-Wsign-compare', - '-Wsign-promo', - '-Wsized-deallocation', - '-Wstrict-null-sentinel', - '-Wsuggest-final-methods', - '-Wsuggest-final-types', - '-Wsuggest-override', - '-Wuseless-cast', - '-Wvirtual-inheritance', - '-Wvolatile', - '-Wzero-as-null-pointer-constant', - ] - - elif cpp.get_id() == 'msvc' - all_cpp_warnings += [ - '/Wall', - '/experimental:external', - '/external:W0', - '/external:anglebrackets', - ] - endif - endif - - all_cpp_warnings = cpp.get_supported_arguments(all_cpp_warnings) - add_global_arguments(all_cpp_warnings, language: ['cpp']) -endif diff --git a/meson_options.txt b/meson_options.txt index 278cf12..365f916 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,26 +1,32 @@ -# Copyright 2021-2022 David Robillard <d@drobilla.net> +# Copyright 2021-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC -option('docs', type: 'feature', value: 'auto', yield: true, +option('docs', type: 'feature', description: 'Build documentation') -option('lv2dir', type: 'string', value: '', yield: true, +option('bundles', type: 'boolean', + description: 'Install specification data bundles') + +option('headers', type: 'boolean', + description: 'Install new-style headers at simplified paths') + +option('lint', type: 'boolean', value: false, + description: 'Run code quality checks') + +option('lv2dir', type: 'string', value: '', description: 'LV2 bundle installation directory') -option('old_headers', type: 'boolean', value: true, yield: true, +option('old_headers', type: 'boolean', description: 'Install backwards compatible headers at URI-style paths') -option('online_docs', type: 'boolean', value: 'false', yield: true, +option('online_docs', type: 'boolean', value: false, description: 'Build documentation for online hosting') -option('plugins', type: 'feature', value: 'auto', yield: true, - description: 'Build example plugins') - -option('strict', type: 'boolean', value: false, yield: true, - description: 'Enable ultra-strict warnings') - -option('tests', type: 'feature', value: 'auto', yield: true, +option('tests', type: 'feature', description: 'Build tests') +option('tools', type: 'feature', + description: 'Build and install command-line utilities') + option('title', type: 'string', value: 'LV2', description: 'Project title') diff --git a/plugins/README.txt b/plugins/README.txt deleted file mode 100644 index 361460d..0000000 --- a/plugins/README.txt +++ /dev/null @@ -1,26 +0,0 @@ -= Programming LV2 Plugins = -David Robillard <d@drobilla.net> -:Author Initials: DER -:toc: -:website: http://lv2plug.in/ -:doctype: book - -== Introduction == - -This is a series of well-documented example plugins that demonstrate the various features of LV2. -Starting with the most basic plugin possible, -each adds new functionality and explains the features used from a high level perspective. - -API and vocabulary reference documentation explains details, -but not the ``big picture''. -This book is intended to complement the reference documentation by providing good reference implementations of plugins, -while also conveying a higher-level understanding of LV2. - -The chapters/plugins are arranged so that each builds incrementally on its predecessor. -Reading this book front to back is a good way to become familiar with modern LV2 programming. -The reader is expected to be familiar with C, but otherwise no special knowledge is required; -the first plugin describes the basics in detail. - -This book is compiled from plugin source code into a single document for pleasant reading and ease of reference. -Each chapter corresponds to executable plugin code which can be found in the +plugins+ directory of the LV2 distribution. -If you prefer to read actual source code, all the content here is also available in the source code as comments. diff --git a/plugins/eg-amp.lv2/README.txt b/plugins/eg-amp.lv2/README.txt deleted file mode 100644 index 41683d3..0000000 --- a/plugins/eg-amp.lv2/README.txt +++ /dev/null @@ -1,19 +0,0 @@ -== Simple Amplifier == - -This plugin is a simple example of a basic LV2 plugin with no additional features. -It has audio ports which contain an array of `float`, -and a control port which contains a single `float`. - -LV2 plugins are defined in two parts: code and data. -The code is written in C, or any C compatible language such as C++. -Static data is described separately in the human and machine friendly http://www.w3.org/TeamSubmission/turtle/[Turtle] syntax. - -Generally, the goal is to keep code minimal, -and describe as much as possible in the static data. -There are several advantages to this approach: - - * Hosts can discover and inspect plugins without loading or executing any plugin code. - * Plugin data can be used from a wide range of generic tools like scripting languages and command line utilities. - * The standard data model allows the use of existing vocabularies to describe plugins and related information. - * The language is extensible, so authors may describe any data without requiring changes to the LV2 specification. - * Labels and documentation are translatable, and available to hosts for display in user interfaces. diff --git a/plugins/eg-amp.lv2/amp.c b/plugins/eg-amp.lv2/amp.c deleted file mode 100644 index 90a5769..0000000 --- a/plugins/eg-amp.lv2/amp.c +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2006-2016 David Robillard <d@drobilla.net> -// Copyright 2006 Steve Harris <steve@plugin.org.uk> -// SPDX-License-Identifier: ISC - -/** - 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/core/lv2.h" - -/** Include standard C headers */ -#include <math.h> -#include <stdint.h> -#include <stdlib.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 AMP_URI "http://lv2plug.in/plugins/eg-amp" - -/** - In code, ports are referred to by index. An enumeration of port indices - should be defined for readability. -*/ -typedef enum { AMP_GAIN = 0, AMP_INPUT = 1, AMP_OUTPUT = 2 } 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 { - // Port buffers - const float* gain; - const float* input; - float* output; -} Amp; - -/** - 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) -{ - Amp* amp = (Amp*)calloc(1, sizeof(Amp)); - - return (LV2_Handle)amp; -} - -/** - 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) -{ - Amp* amp = (Amp*)instance; - - switch ((PortIndex)port) { - case AMP_GAIN: - amp->gain = (const float*)data; - break; - case AMP_INPUT: - amp->input = (const float*)data; - break; - case AMP_OUTPUT: - amp->output = (float*)data; - break; - } -} - -/** - 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) -{} - -/** Define a macro for converting a gain in dB to a coefficient. */ -#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g)*0.05f) : 0.0f) - -/** - The `run()` method is the main process function of the plugin. It processes - a block of audio in the audio context. Since this plugin is - `lv2:hardRTCapable`, `run()` must be real-time safe, so blocking (e.g. with - a mutex) or memory allocation are not allowed. -*/ -static void -run(LV2_Handle instance, uint32_t n_samples) -{ - const Amp* amp = (const Amp*)instance; - - const float gain = *(amp->gain); - const float* const input = amp->input; - float* const output = amp->output; - - const float coef = DB_CO(gain); - - for (uint32_t pos = 0; pos < n_samples; pos++) { - output[pos] = input[pos] * coef; - } -} - -/** - The `deactivate()` method is the counterpart to `activate()`, and is 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; -} - -/** - Every plugin must define an `LV2_Descriptor`. 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 = {AMP_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 - identifier, 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) -{ - return index == 0 ? &descriptor : NULL; -} diff --git a/plugins/eg-amp.lv2/amp.ttl b/plugins/eg-amp.lv2/amp.ttl deleted file mode 100644 index 9f522a1..0000000 --- a/plugins/eg-amp.lv2/amp.ttl +++ /dev/null @@ -1,90 +0,0 @@ -# The full description of the plugin is in this file, which is linked to from -# `manifest.ttl`. This is done so the host only needs to scan the relatively -# small `manifest.ttl` files to quickly discover all plugins. - -@prefix doap: <http://usefulinc.com/ns/doap#> . -@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 units: <http://lv2plug.in/ns/extensions/units#> . - -# First the type of the plugin is described. All plugins must explicitly list -# `lv2:Plugin` as a type. A more specific type should also be given, where -# applicable, so hosts can present a nicer UI for loading plugins. Note that -# this URI is the identifier of the plugin, so if it does not match the one in -# `manifest.ttl`, the host will not discover the plugin data at all. -<http://lv2plug.in/plugins/eg-amp> - a lv2:Plugin , - lv2:AmplifierPlugin ; -# Plugins are associated with a project, where common information like -# developers, home page, and so on are described. This plugin is part of the -# LV2 project, which has URI <http://lv2plug.in/ns/lv2>, and is described -# elsewhere. Typical plugin collections will describe the project in -# manifest.ttl - lv2:project <http://lv2plug.in/ns/lv2> ; -# Every plugin must have a name, described with the doap:name property. -# Translations to various languages can be added by putting a language tag -# after strings as shown. - doap:name "Simple Amplifier" , - "简单放大器"@zh , - "Einfacher Verstärker"@de , - "Simple Amplifier"@en-gb , - "Amplificador Simple"@es , - "Amplificateur de Base"@fr , - "Amplificatore Semplice"@it , - "簡単なアンプ"@jp , - "Просто Усилитель"@ru ; - doap:license <http://opensource.org/licenses/isc> ; - lv2:optionalFeature lv2:hardRTCapable ; - lv2:port [ -# Every port must have at least two types, one that specifies direction -# (lv2:InputPort or lv2:OutputPort), and another to describe the data type. -# This port is a lv2:ControlPort, which means it contains a single float. - a lv2:InputPort , - lv2:ControlPort ; - lv2:index 0 ; - lv2:symbol "gain" ; - lv2:name "Gain" , - "收益"@zh , - "Verstärkung"@de , - "Gain"@en-gb , - "Aumento"@es , - "Gain"@fr , - "Guadagno"@it , - "利益"@jp , - "Увеличение"@ru ; -# An lv2:ControlPort should always describe its default value, and usually a -# minimum and maximum value. Defining a range is not strictly required, but -# should be done wherever possible to aid host support, particularly for UIs. - lv2:default 0.0 ; - lv2:minimum -90.0 ; - lv2:maximum 24.0 ; -# Ports can describe units and control detents to allow better UI generation -# and host automation. - units:unit units:db ; - lv2:scalePoint [ - rdfs:label "+5" ; - rdf:value 5.0 - ] , [ - rdfs:label "0" ; - rdf:value 0.0 - ] , [ - rdfs:label "-5" ; - rdf:value -5.0 - ] , [ - rdfs:label "-10" ; - rdf:value -10.0 - ] - ] , [ - a lv2:AudioPort , - lv2:InputPort ; - lv2:index 1 ; - lv2:symbol "in" ; - lv2:name "In" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 2 ; - lv2:symbol "out" ; - lv2:name "Out" - ] . diff --git a/plugins/eg-amp.lv2/manifest.ttl.in b/plugins/eg-amp.lv2/manifest.ttl.in deleted file mode 100644 index 4a22f95..0000000 --- a/plugins/eg-amp.lv2/manifest.ttl.in +++ /dev/null @@ -1,68 +0,0 @@ -# LV2 plugins are installed in a ``bundle'', a directory with a standard -# structure. Each bundle has a Turtle file named `manifest.ttl` which lists -# the contents of the bundle. -# -# Hosts typically read the manifest of every installed bundle to discover -# plugins on start-up, so it should be as small as possible for performance -# reasons. Details that are only useful if the host chooses to load the plugin -# are stored in other files and linked to from `manifest.ttl`. -# -# ==== URIs ==== -# -# LV2 makes use of URIs as globally-unique identifiers for resources. For -# example, the ID of the plugin described here is -# `<http://lv2plug.in/plugins/eg-amp>`. Note that URIs are only used as -# identifiers and don't necessarily imply that something can be accessed at -# that address on the web (though that may be the case). -# -# ==== Namespace Prefixes ==== -# -# Turtle files contain many URIs, but prefixes can be defined to improve -# readability. For example, with the `lv2:` prefix below, `lv2:Plugin` can be -# written instead of `<http://lv2plug.in/ns/lv2core#Plugin>`. - -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . - -# ==== Describing a Plugin ==== - -# Turtle files contain a set of ``statements'' which describe resources. -# This file contains 3 statements: -# [options="header"] -# |================================================================ -# | Subject | Predicate | Object -# | <http://lv2plug.in/plugins/eg-amp> | a | lv2:Plugin -# | <http://lv2plug.in/plugins/eg-amp> | lv2:binary | <amp.so> -# | <http://lv2plug.in/plugins/eg-amp> | rdfs:seeAlso | <amp.ttl> -# |================================================================ - -# Firstly, `<http://lv2plug.in/plugins/eg-amp>` is an LV2 plugin: -<http://lv2plug.in/plugins/eg-amp> a lv2:Plugin . - -# The predicate ```a`'' is a Turtle shorthand for `rdf:type`. - -# The binary of that plugin can be found at `<amp.ext>`: -<http://lv2plug.in/plugins/eg-amp> lv2:binary <amp@LIB_EXT@> . - -# This file is a template; the token `@LIB_EXT@` is replaced by the build -# system with the appropriate extension for the current platform before -# installation. For example, in the output `manifest.ttl`, the binary would be -# listed as `<amp.so>`. Relative URIs in manifests are relative to the bundle -# directory, so this refers to a binary with the given name in the same -# directory as this manifest. - -# Finally, more information about this plugin can be found in `<amp.ttl>`: -<http://lv2plug.in/plugins/eg-amp> rdfs:seeAlso <amp.ttl> . - -# ==== Abbreviation ==== -# -# This file shows these statements individually for instructive purposes, but -# the subject `<http://lv2plug.in/plugins/eg-amp>` is repetitive. Turtle -# allows the semicolon to be used as a delimiter that repeats the previous -# subject. For example, this manifest would more realistically be written like -# so: - -<http://lv2plug.in/plugins/eg-amp> - a lv2:Plugin ; - lv2:binary <amp@LIB_EXT@> ; - rdfs:seeAlso <amp.ttl> . diff --git a/plugins/eg-amp.lv2/meson.build b/plugins/eg-amp.lv2/meson.build deleted file mode 100644 index bc222d2..0000000 --- a/plugins/eg-amp.lv2/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -plugin_sources = files('amp.c') -bundle_name = 'eg-amp.lv2' -data_filenames = ['manifest.ttl.in', 'amp.ttl'] - -module = shared_library( - 'amp', - plugin_sources, - c_args: c_suppressions, - dependencies: [lv2_dep, m_dep], - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: lv2dir / bundle_name, - name_prefix: '', -) - -config = configuration_data( - { - 'LIB_EXT': '.' + module.full_path().split('.')[-1], - } -) - -foreach filename : data_filenames - if filename.endswith('.in') - configure_file( - configuration: config, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename.substring(0, -3), - ) - else - configure_file( - copy: true, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename, - ) - endif -endforeach diff --git a/plugins/eg-fifths.lv2/README.txt b/plugins/eg-fifths.lv2/README.txt deleted file mode 100644 index 2154321..0000000 --- a/plugins/eg-fifths.lv2/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -== Fifths == - -This plugin demonstrates simple MIDI event reading and writing. diff --git a/plugins/eg-fifths.lv2/fifths.c b/plugins/eg-fifths.lv2/fifths.c deleted file mode 100644 index 9f6a388..0000000 --- a/plugins/eg-fifths.lv2/fifths.c +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2014-2016 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "./uris.h" - -#include "lv2/atom/atom.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/urid/urid.h" - -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> - -enum { FIFTHS_IN = 0, FIFTHS_OUT = 1 }; - -typedef struct { - // Features - LV2_URID_Map* map; - LV2_Log_Logger logger; - - // Ports - const LV2_Atom_Sequence* in_port; - LV2_Atom_Sequence* out_port; - - // URIs - FifthsURIs uris; -} Fifths; - -static void -connect_port(LV2_Handle instance, uint32_t port, void* data) -{ - Fifths* self = (Fifths*)instance; - switch (port) { - case FIFTHS_IN: - self->in_port = (const LV2_Atom_Sequence*)data; - break; - case FIFTHS_OUT: - self->out_port = (LV2_Atom_Sequence*)data; - break; - default: - break; - } -} - -static LV2_Handle -instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* path, - const LV2_Feature* const* features) -{ - // Allocate and initialise instance structure. - Fifths* self = (Fifths*)calloc(1, sizeof(Fifths)); - if (!self) { - return NULL; - } - - // Scan host features for URID map - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - map_fifths_uris(self->map, &self->uris); - - return (LV2_Handle)self; -} - -static void -cleanup(LV2_Handle instance) -{ - free(instance); -} - -static void -run(LV2_Handle instance, uint32_t sample_count) -{ - Fifths* self = (Fifths*)instance; - FifthsURIs* uris = &self->uris; - - // Struct for a 3 byte MIDI event, used for writing notes - typedef struct { - LV2_Atom_Event event; - uint8_t msg[3]; - } MIDINoteEvent; - - // Initially self->out_port contains a Chunk with size set to capacity - - // Get the capacity - const uint32_t out_capacity = self->out_port->atom.size; - - // Write an empty Sequence header to the output - lv2_atom_sequence_clear(self->out_port); - self->out_port->atom.type = self->in_port->atom.type; - - // Read incoming events - LV2_ATOM_SEQUENCE_FOREACH (self->in_port, ev) { - if (ev->body.type == uris->midi_Event) { - const uint8_t* const msg = (const uint8_t*)(ev + 1); - switch (lv2_midi_message_type(msg)) { - case LV2_MIDI_MSG_NOTE_ON: - case LV2_MIDI_MSG_NOTE_OFF: - // Forward note to output - lv2_atom_sequence_append_event(self->out_port, out_capacity, ev); - - if (msg[1] <= 127 - 7) { - // Make a note one 5th (7 semitones) higher than input - MIDINoteEvent fifth; - - // Could simply do fifth.event = *ev here instead... - fifth.event.time.frames = ev->time.frames; // Same time - fifth.event.body.type = ev->body.type; // Same type - fifth.event.body.size = ev->body.size; // Same size - - fifth.msg[0] = msg[0]; // Same status - fifth.msg[1] = msg[1] + 7; // Pitch up 7 semitones - fifth.msg[2] = msg[2]; // Same velocity - - // Write 5th event - lv2_atom_sequence_append_event( - self->out_port, out_capacity, &fifth.event); - } - break; - default: - // Forward all other MIDI events directly - lv2_atom_sequence_append_event(self->out_port, out_capacity, ev); - break; - } - } - } -} - -static const void* -extension_data(const char* uri) -{ - return NULL; -} - -static const LV2_Descriptor descriptor = {EG_FIFTHS_URI, - instantiate, - connect_port, - NULL, // activate, - run, - NULL, // deactivate, - cleanup, - extension_data}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* -lv2_descriptor(uint32_t index) -{ - return index == 0 ? &descriptor : NULL; -} diff --git a/plugins/eg-fifths.lv2/fifths.ttl b/plugins/eg-fifths.lv2/fifths.ttl deleted file mode 100644 index 7f58a33..0000000 --- a/plugins/eg-fifths.lv2/fifths.ttl +++ /dev/null @@ -1,30 +0,0 @@ -@prefix atom: <http://lv2plug.in/ns/ext/atom#> . -@prefix doap: <http://usefulinc.com/ns/doap#> . -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix urid: <http://lv2plug.in/ns/ext/urid#> . -@prefix midi: <http://lv2plug.in/ns/ext/midi#> . - -<http://lv2plug.in/plugins/eg-fifths> - a lv2:Plugin ; - doap:name "Example Fifths" ; - doap:license <http://opensource.org/licenses/isc> ; - lv2:project <http://lv2plug.in/ns/lv2> ; - lv2:requiredFeature urid:map ; - lv2:optionalFeature lv2:hardRTCapable ; - lv2:port [ - a lv2:InputPort , - atom:AtomPort ; - atom:bufferType atom:Sequence ; - atom:supports midi:MidiEvent ; - lv2:index 0 ; - lv2:symbol "in" ; - lv2:name "In" - ] , [ - a lv2:OutputPort , - atom:AtomPort ; - atom:bufferType atom:Sequence ; - atom:supports midi:MidiEvent ; - lv2:index 1 ; - lv2:symbol "out" ; - lv2:name "Out" - ] . diff --git a/plugins/eg-fifths.lv2/manifest.ttl.in b/plugins/eg-fifths.lv2/manifest.ttl.in deleted file mode 100644 index f87f2c1..0000000 --- a/plugins/eg-fifths.lv2/manifest.ttl.in +++ /dev/null @@ -1,8 +0,0 @@ -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . - -<http://lv2plug.in/plugins/eg-fifths> - a lv2:Plugin ; - lv2:binary <fifths@LIB_EXT@> ; - rdfs:seeAlso <fifths.ttl> . diff --git a/plugins/eg-fifths.lv2/meson.build b/plugins/eg-fifths.lv2/meson.build deleted file mode 100644 index 0fa8525..0000000 --- a/plugins/eg-fifths.lv2/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -plugin_sources = files('fifths.c') -bundle_name = 'eg-fifths.lv2' -data_filenames = ['manifest.ttl.in', 'fifths.ttl'] - -module = shared_library( - 'fifths', - plugin_sources, - c_args: c_suppressions, - dependencies: [lv2_dep, m_dep], - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: lv2dir / bundle_name, - name_prefix: '', -) - -config = configuration_data( - { - 'LIB_EXT': '.' + module.full_path().split('.')[-1], - } -) - -foreach filename : data_filenames - if filename.endswith('.in') - configure_file( - configuration: config, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename.substring(0, -3), - ) - else - configure_file( - copy: true, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename, - ) - endif -endforeach diff --git a/plugins/eg-fifths.lv2/uris.h b/plugins/eg-fifths.lv2/uris.h deleted file mode 100644 index 0406937..0000000 --- a/plugins/eg-fifths.lv2/uris.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014-2015 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef FIFTHS_URIS_H -#define FIFTHS_URIS_H - -#include "lv2/atom/atom.h" -#include "lv2/midi/midi.h" -#include "lv2/patch/patch.h" -#include "lv2/urid/urid.h" - -#define EG_FIFTHS_URI "http://lv2plug.in/plugins/eg-fifths" - -typedef struct { - LV2_URID atom_Path; - LV2_URID atom_Resource; - LV2_URID atom_Sequence; - LV2_URID atom_URID; - LV2_URID atom_eventTransfer; - LV2_URID midi_Event; - LV2_URID patch_Set; - LV2_URID patch_property; - LV2_URID patch_value; -} FifthsURIs; - -static inline void -map_fifths_uris(LV2_URID_Map* map, FifthsURIs* uris) -{ - uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); - uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); - uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); - uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); - uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); - uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); - uris->patch_property = map->map(map->handle, LV2_PATCH__property); - uris->patch_value = map->map(map->handle, LV2_PATCH__value); -} - -#endif /* FIFTHS_URIS_H */ diff --git a/plugins/eg-metro.lv2/README.txt b/plugins/eg-metro.lv2/README.txt deleted file mode 100644 index 5e9a84a..0000000 --- a/plugins/eg-metro.lv2/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -== Metronome == - -This plugin demonstrates tempo synchronisation by clicking on every beat. The -host sends this information to the plugin as events, so an event with new time -and tempo information will be received whenever there is a change. - -Time is assumed to continue rolling at the tempo and speed defined by the last -received tempo event, even across cycles, until a new tempo event is received -or the plugin is deactivated. diff --git a/plugins/eg-metro.lv2/manifest.ttl.in b/plugins/eg-metro.lv2/manifest.ttl.in deleted file mode 100644 index bd93f66..0000000 --- a/plugins/eg-metro.lv2/manifest.ttl.in +++ /dev/null @@ -1,7 +0,0 @@ -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . - -<http://lv2plug.in/plugins/eg-metro> - a lv2:Plugin ; - lv2:binary <metro@LIB_EXT@> ; - rdfs:seeAlso <metro.ttl> . diff --git a/plugins/eg-metro.lv2/meson.build b/plugins/eg-metro.lv2/meson.build deleted file mode 100644 index def9e0b..0000000 --- a/plugins/eg-metro.lv2/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -plugin_sources = files('metro.c') -bundle_name = 'eg-metro.lv2' -data_filenames = ['manifest.ttl.in', 'metro.ttl'] - -module = shared_library( - 'metro', - plugin_sources, - c_args: c_suppressions, - dependencies: [lv2_dep, m_dep], - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: lv2dir / bundle_name, - name_prefix: '', -) - -config = configuration_data( - { - 'LIB_EXT': '.' + module.full_path().split('.')[-1], - } -) - -foreach filename : data_filenames - if filename.endswith('.in') - configure_file( - configuration: config, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename.substring(0, -3), - ) - else - configure_file( - copy: true, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename, - ) - endif -endforeach diff --git a/plugins/eg-metro.lv2/metro.c b/plugins/eg-metro.lv2/metro.c deleted file mode 100644 index f3fe164..0000000 --- a/plugins/eg-metro.lv2/metro.c +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2012-2016 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "lv2/atom/atom.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/time/time.h" -#include "lv2/urid/urid.h" - -#include <math.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#ifndef M_PI -# define M_PI 3.14159265 -#endif - -#define EG_METRO_URI "http://lv2plug.in/plugins/eg-metro" - -typedef struct { - LV2_URID atom_Blank; - LV2_URID atom_Float; - LV2_URID atom_Object; - LV2_URID atom_Path; - LV2_URID atom_Resource; - LV2_URID atom_Sequence; - LV2_URID time_Position; - LV2_URID time_barBeat; - LV2_URID time_beatsPerMinute; - LV2_URID time_speed; -} MetroURIs; - -static const double attack_s = 0.005; -static const double decay_s = 0.075; - -enum { METRO_CONTROL = 0, METRO_OUT = 1 }; - -/** During execution this plugin can be in one of 3 states: */ -typedef enum { - STATE_ATTACK, // Envelope rising - STATE_DECAY, // Envelope lowering - STATE_OFF // Silent -} State; - -/** - This plugin must keep track of more state than previous examples to be able - to render audio. The basic idea is to generate a single cycle of a sine - wave which is conceptually played continuously. The 'tick' is generated by - enveloping the amplitude so there is a short attack/decay peak around a - tick, and silence the rest of the time. - - This example uses a simple AD envelope with fixed parameters. A more - sophisticated implementation might use a more advanced envelope and allow - the user to modify these parameters, the frequency of the wave, and so on. -*/ -typedef struct { - LV2_URID_Map* map; // URID map feature - LV2_Log_Logger logger; // Logger API - MetroURIs uris; // Cache of mapped URIDs - - struct { - LV2_Atom_Sequence* control; - float* output; - } ports; - - // Variables to keep track of the tempo information sent by the host - double rate; // Sample rate - float bpm; // Beats per minute (tempo) - float speed; // Transport speed (usually 0=stop, 1=play) - - uint32_t elapsed_len; // Frames since the start of the last click - uint32_t wave_offset; // Current play offset in the wave - State state; // Current play state - - // One cycle of a sine wave - float* wave; - uint32_t wave_len; - - // Envelope parameters - uint32_t attack_len; - uint32_t decay_len; -} Metro; - -static void -connect_port(LV2_Handle instance, uint32_t port, void* data) -{ - Metro* self = (Metro*)instance; - - switch (port) { - case METRO_CONTROL: - self->ports.control = (LV2_Atom_Sequence*)data; - break; - case METRO_OUT: - self->ports.output = (float*)data; - break; - default: - break; - } -} - -/** - The activate() method resets the state completely, so the wave offset is - zero and the envelope is off. -*/ -static void -activate(LV2_Handle instance) -{ - Metro* self = (Metro*)instance; - - self->elapsed_len = 0; - self->wave_offset = 0; - self->state = STATE_OFF; -} - -/** - This plugin does a bit more work in instantiate() than the previous - examples. The tempo updates from the host contain several URIs, so those - are mapped, and the sine wave to be played needs to be generated based on - the current sample rate. -*/ -static LV2_Handle -instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* path, - const LV2_Feature* const* features) -{ - Metro* self = (Metro*)calloc(1, sizeof(Metro)); - if (!self) { - return NULL; - } - - // Scan host features for URID map - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - // Map URIS - MetroURIs* const uris = &self->uris; - LV2_URID_Map* const map = self->map; - uris->atom_Blank = map->map(map->handle, LV2_ATOM__Blank); - uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); - uris->atom_Object = map->map(map->handle, LV2_ATOM__Object); - uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); - uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); - uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); - uris->time_Position = map->map(map->handle, LV2_TIME__Position); - uris->time_barBeat = map->map(map->handle, LV2_TIME__barBeat); - uris->time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute); - uris->time_speed = map->map(map->handle, LV2_TIME__speed); - - // Initialise instance fields - self->rate = rate; - self->bpm = 120.0f; - self->attack_len = (uint32_t)(attack_s * rate); - self->decay_len = (uint32_t)(decay_s * rate); - self->state = STATE_OFF; - - // Generate one cycle of a sine wave at the desired frequency - const double freq = 440.0 * 2.0; - const double amp = 0.5; - self->wave_len = (uint32_t)(rate / freq); - self->wave = (float*)malloc(self->wave_len * sizeof(float)); - for (uint32_t i = 0; i < self->wave_len; ++i) { - self->wave[i] = (float)(sin(i * 2 * M_PI * freq / rate) * amp); - } - - return (LV2_Handle)self; -} - -static void -cleanup(LV2_Handle instance) -{ - free(instance); -} - -/** - Play back audio for the range [begin..end) relative to this cycle. This is - called by run() in-between events to output audio up until the current time. -*/ -static void -play(Metro* self, uint32_t begin, uint32_t end) -{ - float* const output = self->ports.output; - const uint32_t frames_per_beat = (uint32_t)(60.0f / self->bpm * self->rate); - - if (self->speed == 0.0f) { - memset(output, 0, (end - begin) * sizeof(float)); - return; - } - - for (uint32_t i = begin; i < end; ++i) { - switch (self->state) { - case STATE_ATTACK: - // Amplitude increases from 0..1 until attack_len - output[i] = self->wave[self->wave_offset] * (float)self->elapsed_len / - (float)self->attack_len; - if (self->elapsed_len >= self->attack_len) { - self->state = STATE_DECAY; - } - break; - case STATE_DECAY: - // Amplitude decreases from 1..0 until attack_len + decay_len - output[i] = 0.0f; - output[i] = self->wave[self->wave_offset] * - (1 - ((float)(self->elapsed_len - self->attack_len) / - (float)self->decay_len)); - if (self->elapsed_len >= self->attack_len + self->decay_len) { - self->state = STATE_OFF; - } - break; - case STATE_OFF: - output[i] = 0.0f; - } - - // We continuously play the sine wave regardless of envelope - self->wave_offset = (self->wave_offset + 1) % self->wave_len; - - // Update elapsed time and start attack if necessary - if (++self->elapsed_len == frames_per_beat) { - self->state = STATE_ATTACK; - self->elapsed_len = 0; - } - } -} - -/** - Update the current position based on a host message. This is called by - run() when a time:Position is received. -*/ -static void -update_position(Metro* self, const LV2_Atom_Object* obj) -{ - const MetroURIs* uris = &self->uris; - - // Received new transport position/speed - LV2_Atom* beat = NULL; - LV2_Atom* bpm = NULL; - LV2_Atom* speed = NULL; - // clang-format off - lv2_atom_object_get(obj, - uris->time_barBeat, &beat, - uris->time_beatsPerMinute, &bpm, - uris->time_speed, &speed, - NULL); - // clang-format on - - if (bpm && bpm->type == uris->atom_Float) { - // Tempo changed, update BPM - self->bpm = ((LV2_Atom_Float*)bpm)->body; - } - if (speed && speed->type == uris->atom_Float) { - // Speed changed, e.g. 0 (stop) to 1 (play) - self->speed = ((LV2_Atom_Float*)speed)->body; - } - if (beat && beat->type == uris->atom_Float) { - // Received a beat position, synchronise - // This hard sync may cause clicks, a real plugin would be more graceful - const float frames_per_beat = (float)(60.0 / self->bpm * self->rate); - const float bar_beats = ((LV2_Atom_Float*)beat)->body; - const float beat_beats = bar_beats - floorf(bar_beats); - self->elapsed_len = (uint32_t)(beat_beats * frames_per_beat); - if (self->elapsed_len < self->attack_len) { - self->state = STATE_ATTACK; - } else if (self->elapsed_len < self->attack_len + self->decay_len) { - self->state = STATE_DECAY; - } else { - self->state = STATE_OFF; - } - } -} - -static void -run(LV2_Handle instance, uint32_t sample_count) -{ - Metro* self = (Metro*)instance; - const MetroURIs* uris = &self->uris; - - // Work forwards in time frame by frame, handling events as we go - const LV2_Atom_Sequence* in = self->ports.control; - uint32_t last_t = 0; - for (const LV2_Atom_Event* ev = lv2_atom_sequence_begin(&in->body); - !lv2_atom_sequence_is_end(&in->body, in->atom.size, ev); - ev = lv2_atom_sequence_next(ev)) { - // Play the click for the time slice from last_t until now - play(self, last_t, (uint32_t)ev->time.frames); - - // Check if this event is an Object - // (or deprecated Blank to tolerate old hosts) - if (ev->body.type == uris->atom_Object || - ev->body.type == uris->atom_Blank) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == uris->time_Position) { - // Received position information, update - update_position(self, obj); - } - } - - // Update time for next iteration and move to next event - last_t = (uint32_t)ev->time.frames; - } - - // Play for remainder of cycle - play(self, last_t, sample_count); -} - -static const LV2_Descriptor descriptor = { - EG_METRO_URI, - instantiate, - connect_port, - 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/plugins/eg-metro.lv2/metro.ttl b/plugins/eg-metro.lv2/metro.ttl deleted file mode 100644 index 8b4af3d..0000000 --- a/plugins/eg-metro.lv2/metro.ttl +++ /dev/null @@ -1,30 +0,0 @@ -@prefix atom: <http://lv2plug.in/ns/ext/atom#> . -@prefix doap: <http://usefulinc.com/ns/doap#> . -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix time: <http://lv2plug.in/ns/ext/time#> . -@prefix urid: <http://lv2plug.in/ns/ext/urid#> . - -<http://lv2plug.in/plugins/eg-metro> - a lv2:Plugin ; - doap:name "Example Metronome" ; - doap:license <http://opensource.org/licenses/isc> ; - lv2:project <http://lv2plug.in/ns/lv2> ; - lv2:requiredFeature urid:map ; - lv2:optionalFeature lv2:hardRTCapable ; - lv2:port [ - a lv2:InputPort , - atom:AtomPort ; - atom:bufferType atom:Sequence ; -# Since this port supports time:Position, the host knows to deliver time and -# tempo information - atom:supports time:Position ; - lv2:index 0 ; - lv2:symbol "control" ; - lv2:name "Control" ; - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 1 ; - lv2:symbol "out" ; - lv2:name "Out" ; - ] . diff --git a/plugins/eg-midigate.lv2/README.txt b/plugins/eg-midigate.lv2/README.txt deleted file mode 100644 index 8f4a0f0..0000000 --- a/plugins/eg-midigate.lv2/README.txt +++ /dev/null @@ -1,10 +0,0 @@ -== MIDI Gate == - -This plugin demonstrates: - - * Receiving MIDI input - - * Processing audio based on MIDI events with sample accuracy - - * Supporting MIDI programs which the host can control/automate, or present a - user interface for with human readable labels diff --git a/plugins/eg-midigate.lv2/manifest.ttl.in b/plugins/eg-midigate.lv2/manifest.ttl.in deleted file mode 100644 index d32f1dc..0000000 --- a/plugins/eg-midigate.lv2/manifest.ttl.in +++ /dev/null @@ -1,10 +0,0 @@ -# The manifest.ttl file follows the same template as the previous example. - -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . - -<http://lv2plug.in/plugins/eg-midigate> - a lv2:Plugin ; - lv2:binary <midigate@LIB_EXT@> ; - rdfs:seeAlso <midigate.ttl> . diff --git a/plugins/eg-midigate.lv2/meson.build b/plugins/eg-midigate.lv2/meson.build deleted file mode 100644 index 609ee3a..0000000 --- a/plugins/eg-midigate.lv2/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -plugin_sources = files('midigate.c') -bundle_name = 'eg-midigate.lv2' -data_filenames = ['manifest.ttl.in', 'midigate.ttl'] - -module = shared_library( - 'midigate', - plugin_sources, - c_args: c_suppressions, - dependencies: [lv2_dep, m_dep], - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: lv2dir / bundle_name, - name_prefix: '', -) - -config = configuration_data( - { - 'LIB_EXT': '.' + module.full_path().split('.')[-1], - } -) - -foreach filename : data_filenames - if filename.endswith('.in') - configure_file( - configuration: config, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename.substring(0, -3), - ) - else - configure_file( - copy: true, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename, - ) - endif -endforeach diff --git a/plugins/eg-midigate.lv2/midigate.c b/plugins/eg-midigate.lv2/midigate.c deleted file mode 100644 index db2fdea..0000000 --- a/plugins/eg-midigate.lv2/midigate.c +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2013-2016 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "lv2/atom/atom.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/urid/urid.h" - -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define MIDIGATE_URI "http://lv2plug.in/plugins/eg-midigate" - -typedef enum { - MIDIGATE_CONTROL = 0, - MIDIGATE_IN = 1, - MIDIGATE_OUT = 2 -} PortIndex; - -typedef struct { - // Port buffers - const LV2_Atom_Sequence* control; - const float* in; - float* out; - - // Features - LV2_URID_Map* map; - LV2_Log_Logger logger; - - struct { - LV2_URID midi_MidiEvent; - } uris; - - unsigned n_active_notes; - unsigned program; // 0 = normal, 1 = inverted -} Midigate; - -static LV2_Handle -instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* bundle_path, - const LV2_Feature* const* features) -{ - Midigate* self = (Midigate*)calloc(1, sizeof(Midigate)); - if (!self) { - return NULL; - } - - // Scan host features for URID map - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - self->uris.midi_MidiEvent = - self->map->map(self->map->handle, LV2_MIDI__MidiEvent); - - return (LV2_Handle)self; -} - -static void -connect_port(LV2_Handle instance, uint32_t port, void* data) -{ - Midigate* self = (Midigate*)instance; - - switch ((PortIndex)port) { - case MIDIGATE_CONTROL: - self->control = (const LV2_Atom_Sequence*)data; - break; - case MIDIGATE_IN: - self->in = (const float*)data; - break; - case MIDIGATE_OUT: - self->out = (float*)data; - break; - } -} - -static void -activate(LV2_Handle instance) -{ - Midigate* self = (Midigate*)instance; - self->n_active_notes = 0; - self->program = 0; -} - -/** - A function to write a chunk of output, to be called from run(). If the gate - is high, then the input will be passed through for this chunk, otherwise - silence is written. -*/ -static void -write_output(Midigate* self, uint32_t offset, uint32_t len) -{ - const bool active = (self->program == 0) ? (self->n_active_notes > 0) - : (self->n_active_notes == 0); - if (active) { - memcpy(self->out + offset, self->in + offset, len * sizeof(float)); - } else { - memset(self->out + offset, 0, len * sizeof(float)); - } -} - -/** - This plugin works through the cycle in chunks starting at offset zero. The - +offset+ represents the current time within this this cycle, so - the output from 0 to +offset+ has already been written. - - MIDI events are read in a loop. In each iteration, the number of active - notes (on note on and note off) or the program (on program change) is - updated, then the output is written up until the current event time. Then - +offset+ is updated and the next event is processed. After the loop the - final chunk from the last event to the end of the cycle is emitted. - - There is currently no standard way to describe MIDI programs in LV2, so the - host has no way of knowing that these programs exist and should be presented - to the user. A future version of LV2 will address this shortcoming. - - This pattern of iterating over input events and writing output along the way - is a common idiom for writing sample accurate output based on event input. - - Note that this simple example simply writes input or zero for each sample - based on the gate. A serious implementation would need to envelope the - transition to avoid aliasing. -*/ -static void -run(LV2_Handle instance, uint32_t sample_count) -{ - Midigate* self = (Midigate*)instance; - uint32_t offset = 0; - - LV2_ATOM_SEQUENCE_FOREACH (self->control, ev) { - write_output(self, offset, (uint32_t)(ev->time.frames - offset)); - offset = (uint32_t)ev->time.frames; - - if (ev->body.type == self->uris.midi_MidiEvent) { - const uint8_t* const msg = (const uint8_t*)(ev + 1); - switch (lv2_midi_message_type(msg)) { - case LV2_MIDI_MSG_NOTE_ON: - ++self->n_active_notes; - break; - case LV2_MIDI_MSG_NOTE_OFF: - if (self->n_active_notes > 0) { - --self->n_active_notes; - } - break; - case LV2_MIDI_MSG_CONTROLLER: - if (msg[1] == LV2_MIDI_CTL_ALL_NOTES_OFF) { - self->n_active_notes = 0; - } - break; - case LV2_MIDI_MSG_PGM_CHANGE: - if (msg[1] == 0 || msg[1] == 1) { - self->program = msg[1]; - } - break; - default: - break; - } - } - } - - write_output(self, offset, sample_count - offset); -} - -/** - We have no resources to free on deactivation. - Note that the next call to activate will re-initialise the state, namely - self->n_active_notes, so there is no need to do so here. -*/ -static void -deactivate(LV2_Handle instance) -{} - -static void -cleanup(LV2_Handle instance) -{ - free(instance); -} - -/** - This plugin also has no extension data to return. -*/ -static const void* -extension_data(const char* uri) -{ - return NULL; -} - -static const LV2_Descriptor descriptor = {MIDIGATE_URI, - instantiate, - connect_port, - activate, - run, - deactivate, - cleanup, - extension_data}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* -lv2_descriptor(uint32_t index) -{ - return index == 0 ? &descriptor : NULL; -} diff --git a/plugins/eg-midigate.lv2/midigate.ttl b/plugins/eg-midigate.lv2/midigate.ttl deleted file mode 100644 index e14a329..0000000 --- a/plugins/eg-midigate.lv2/midigate.ttl +++ /dev/null @@ -1,56 +0,0 @@ -# The same set of namespace prefixes with two additions for LV2 extensions this -# plugin uses: atom and urid. - -@prefix atom: <http://lv2plug.in/ns/ext/atom#> . -@prefix doap: <http://usefulinc.com/ns/doap#> . -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix midi: <http://lv2plug.in/ns/ext/midi#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix urid: <http://lv2plug.in/ns/ext/urid#> . - -<http://lv2plug.in/plugins/eg-midigate> - a lv2:Plugin ; - doap:name "Example MIDI Gate" ; - doap:license <http://opensource.org/licenses/isc> ; - lv2:project <http://lv2plug.in/ns/lv2> ; - lv2:requiredFeature urid:map ; - lv2:optionalFeature lv2:hardRTCapable ; -# This plugin has three ports. There is an audio input and output as before, -# as well as a new AtomPort. An AtomPort buffer contains an Atom, which is a -# generic container for any type of data. In this case, we want to receive -# MIDI events, so the (mandatory) +atom:bufferType+ is atom:Sequence, which is -# a series of events with time stamps. -# -# Events themselves are also generic and can contain any type of data, but in -# this case we are only interested in MIDI events. The (optional) -# +atom:supports+ property describes which event types are supported. Though -# not required, this information should always be given so the host knows what -# types of event it can expect the plugin to understand. -# -# The (optional) +lv2:designation+ of this port is +lv2:control+, which -# indicates that this is the "main" control port where the host should send -# events it expects to configure the plugin, in this case changing the MIDI -# program. This is necessary since it is possible to have several MIDI input -# ports, though typically it is best to have one. - lv2:port [ - a lv2:InputPort , - atom:AtomPort ; - atom:bufferType atom:Sequence ; - atom:supports midi:MidiEvent ; - lv2:designation lv2:control ; - lv2:index 0 ; - lv2:symbol "control" ; - lv2:name "Control" - ] , [ - a lv2:AudioPort , - lv2:InputPort ; - lv2:index 1 ; - lv2:symbol "in" ; - lv2:name "In" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 2 ; - lv2:symbol "out" ; - lv2:name "Out" - ] . diff --git a/plugins/eg-params.lv2/README.txt b/plugins/eg-params.lv2/README.txt deleted file mode 100644 index acf90c1..0000000 --- a/plugins/eg-params.lv2/README.txt +++ /dev/null @@ -1,21 +0,0 @@ -== Params == - -The basic LV2 mechanism for controls is -http://lv2plug.in/ns/lv2core#ControlPort[lv2:ControlPort], inherited from -LADSPA. Control ports are problematic because they are not sample accurate, -support only one type (`float`), and require that plugins poll to know when a -control has changed. - -Parameters can be used instead to address these issues. Parameters can be -thought of as properties of a plugin instance; they are identified by URI and -have a value of any type. This deliberately meshes with the concept of plugin -state defined by the http://lv2plug.in/ns/ext/state[LV2 state extension]. -The state extension allows plugins to save and restore their parameters (along -with other internal state information, if necessary). - -Parameters are accessed and manipulated using messages sent via a sequence -port. The http://lv2plug.in/ns/ext/patch[LV2 patch extension] defines the -standard messages for working with parameters. Typically, only two are used -for simple plugins: http://lv2plug.in/ns/ext/patch#Set[patch:Set] sets a -parameter to some value, and http://lv2plug.in/ns/ext/patch#Get[patch:Get] -requests that the plugin send a description of its parameters. diff --git a/plugins/eg-params.lv2/manifest.ttl.in b/plugins/eg-params.lv2/manifest.ttl.in deleted file mode 100644 index 913de7c..0000000 --- a/plugins/eg-params.lv2/manifest.ttl.in +++ /dev/null @@ -1,7 +0,0 @@ -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . - -<http://lv2plug.in/plugins/eg-params> - a lv2:Plugin ; - lv2:binary <params@LIB_EXT@> ; - rdfs:seeAlso <params.ttl> . diff --git a/plugins/eg-params.lv2/meson.build b/plugins/eg-params.lv2/meson.build deleted file mode 100644 index 5b06709..0000000 --- a/plugins/eg-params.lv2/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -plugin_sources = files('params.c') -bundle_name = 'eg-params.lv2' -data_filenames = ['manifest.ttl.in', 'params.ttl'] - -module = shared_library( - 'params', - plugin_sources, - c_args: c_suppressions, - dependencies: [lv2_dep, m_dep], - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: lv2dir / bundle_name, - name_prefix: '', -) - -config = configuration_data( - { - 'LIB_EXT': '.' + module.full_path().split('.')[-1], - } -) - -foreach filename : data_filenames - if filename.endswith('.in') - configure_file( - configuration: config, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename.substring(0, -3), - ) - else - configure_file( - copy: true, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename, - ) - endif -endforeach diff --git a/plugins/eg-params.lv2/params.c b/plugins/eg-params.lv2/params.c deleted file mode 100644 index af96fc8..0000000 --- a/plugins/eg-params.lv2/params.c +++ /dev/null @@ -1,520 +0,0 @@ -// Copyright 2014-2016 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "state_map.h" - -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/patch/patch.h" -#include "lv2/state/state.h" -#include "lv2/urid/urid.h" - -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define MAX_STRING 1024 - -#define EG_PARAMS_URI "http://lv2plug.in/plugins/eg-params" - -#define N_PROPS 9 - -typedef struct { - LV2_URID plugin; - LV2_URID atom_Path; - LV2_URID atom_Sequence; - LV2_URID atom_URID; - LV2_URID atom_eventTransfer; - LV2_URID eg_spring; - LV2_URID midi_Event; - LV2_URID patch_Get; - LV2_URID patch_Set; - LV2_URID patch_Put; - LV2_URID patch_body; - LV2_URID patch_subject; - LV2_URID patch_property; - LV2_URID patch_value; -} URIs; - -typedef struct { - LV2_Atom_Int aint; - LV2_Atom_Long along; - LV2_Atom_Float afloat; - LV2_Atom_Double adouble; - LV2_Atom_Bool abool; - LV2_Atom astring; - char string[MAX_STRING]; - LV2_Atom apath; - char path[MAX_STRING]; - LV2_Atom_Float lfo; - LV2_Atom_Float spring; -} State; - -static inline void -map_uris(LV2_URID_Map* map, URIs* uris) -{ - uris->plugin = map->map(map->handle, EG_PARAMS_URI); - - uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); - uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); - uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); - uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->eg_spring = map->map(map->handle, EG_PARAMS_URI "#spring"); - uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); - uris->patch_Get = map->map(map->handle, LV2_PATCH__Get); - uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); - uris->patch_Put = map->map(map->handle, LV2_PATCH__Put); - uris->patch_body = map->map(map->handle, LV2_PATCH__body); - uris->patch_subject = map->map(map->handle, LV2_PATCH__subject); - uris->patch_property = map->map(map->handle, LV2_PATCH__property); - uris->patch_value = map->map(map->handle, LV2_PATCH__value); -} - -enum { PARAMS_IN = 0, PARAMS_OUT = 1 }; - -typedef struct { - // Features - LV2_URID_Map* map; - LV2_URID_Unmap* unmap; - LV2_Log_Logger log; - - // Forge for creating atoms - LV2_Atom_Forge forge; - - // Ports - const LV2_Atom_Sequence* in_port; - LV2_Atom_Sequence* out_port; - - // URIs - URIs uris; - - // Plugin state - StateMapItem props[N_PROPS]; - State state; - - // Buffer for making strings from URIDs if unmap is not provided - char urid_buf[12]; -} Params; - -static void -connect_port(LV2_Handle instance, uint32_t port, void* data) -{ - Params* self = (Params*)instance; - switch (port) { - case PARAMS_IN: - self->in_port = (const LV2_Atom_Sequence*)data; - break; - case PARAMS_OUT: - self->out_port = (LV2_Atom_Sequence*)data; - break; - default: - break; - } -} - -static LV2_Handle -instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* path, - const LV2_Feature* const* features) -{ - // Allocate instance - Params* self = (Params*)calloc(1, sizeof(Params)); - if (!self) { - return NULL; - } - - // Get host features - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->log.log, false, - LV2_URID__map, &self->map, true, - LV2_URID__unmap, &self->unmap, false, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->log, self->map); - if (missing) { - lv2_log_error(&self->log, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - // Map URIs and initialise forge - map_uris(self->map, &self->uris); - lv2_atom_forge_init(&self->forge, self->map); - - // Initialise state dictionary - // clang-format off - State* state = &self->state; - state_map_init( - self->props, self->map, self->map->handle, - EG_PARAMS_URI "#int", STATE_MAP_INIT(Int, &state->aint), - EG_PARAMS_URI "#long", STATE_MAP_INIT(Long, &state->along), - EG_PARAMS_URI "#float", STATE_MAP_INIT(Float, &state->afloat), - EG_PARAMS_URI "#double", STATE_MAP_INIT(Double, &state->adouble), - EG_PARAMS_URI "#bool", STATE_MAP_INIT(Bool, &state->abool), - EG_PARAMS_URI "#string", STATE_MAP_INIT(String, &state->astring), - EG_PARAMS_URI "#path", STATE_MAP_INIT(Path, &state->apath), - EG_PARAMS_URI "#lfo", STATE_MAP_INIT(Float, &state->lfo), - EG_PARAMS_URI "#spring", STATE_MAP_INIT(Float, &state->spring), - NULL); - // clang-format on - - return (LV2_Handle)self; -} - -static void -cleanup(LV2_Handle instance) -{ - free(instance); -} - -/** Helper function to unmap a URID if possible. */ -static const char* -unmap(Params* self, LV2_URID urid) -{ - if (self->unmap) { - return self->unmap->unmap(self->unmap->handle, urid); - } - - snprintf(self->urid_buf, sizeof(self->urid_buf), "%u", urid); - return self->urid_buf; -} - -static LV2_State_Status -check_type(Params* self, LV2_URID key, LV2_URID type, LV2_URID required_type) -{ - if (type != required_type) { - lv2_log_trace(&self->log, - "Bad type <%s> for <%s> (needs <%s>)\n", - unmap(self, type), - unmap(self, key), - unmap(self, required_type)); - return LV2_STATE_ERR_BAD_TYPE; - } - return LV2_STATE_SUCCESS; -} - -static LV2_State_Status -set_parameter(Params* self, - LV2_URID key, - uint32_t size, - LV2_URID type, - const void* body, - bool from_state) -{ - // Look up property in state dictionary - const StateMapItem* entry = state_map_find(self->props, N_PROPS, key); - if (!entry) { - lv2_log_trace(&self->log, "Unknown parameter <%s>\n", unmap(self, key)); - return LV2_STATE_ERR_NO_PROPERTY; - } - - // Ensure given type matches property's type - if (check_type(self, key, type, entry->value->type)) { - return LV2_STATE_ERR_BAD_TYPE; - } - - // Set property value in state dictionary - lv2_log_trace(&self->log, "Set <%s>\n", entry->uri); - memcpy(entry->value + 1, body, size); - entry->value->size = size; - return LV2_STATE_SUCCESS; -} - -static const LV2_Atom* -get_parameter(Params* self, LV2_URID key) -{ - const StateMapItem* entry = state_map_find(self->props, N_PROPS, key); - if (entry) { - lv2_log_trace(&self->log, "Get <%s>\n", entry->uri); - return entry->value; - } - - lv2_log_trace(&self->log, "Unknown parameter <%s>\n", unmap(self, key)); - return NULL; -} - -static LV2_State_Status -write_param_to_forge(LV2_State_Handle handle, - uint32_t key, - const void* value, - size_t size, - uint32_t type, - uint32_t flags) -{ - LV2_Atom_Forge* forge = (LV2_Atom_Forge*)handle; - - if (!lv2_atom_forge_key(forge, key) || - !lv2_atom_forge_atom(forge, size, type) || - !lv2_atom_forge_write(forge, value, size)) { - return LV2_STATE_ERR_UNKNOWN; - } - - return LV2_STATE_SUCCESS; -} - -static void -store_prop(Params* self, - LV2_State_Map_Path* map_path, - LV2_State_Status* save_status, - LV2_State_Store_Function store, - LV2_State_Handle handle, - LV2_URID key, - const LV2_Atom* value) -{ - LV2_State_Status st = LV2_STATE_SUCCESS; - if (map_path && value->type == self->uris.atom_Path) { - // Map path to abstract path for portable storage - const char* path = (const char*)(value + 1); - char* apath = map_path->abstract_path(map_path->handle, path); - st = store(handle, - key, - apath, - strlen(apath) + 1, - self->uris.atom_Path, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - free(apath); - } else { - // Store simple property - st = store(handle, - key, - value + 1, - value->size, - value->type, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - } - - if (save_status && !*save_status) { - *save_status = st; - } -} - -/** - State save method. - - This is used in the usual way when called by the host to save plugin state, - but also internally for writing messages in the audio thread by passing a - "store" function which actually writes the description to the forge. -*/ -static LV2_State_Status -save(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - Params* self = (Params*)instance; - LV2_State_Map_Path* map_path = - (LV2_State_Map_Path*)lv2_features_data(features, LV2_STATE__mapPath); - - LV2_State_Status st = LV2_STATE_SUCCESS; - for (unsigned i = 0; i < N_PROPS; ++i) { - StateMapItem* prop = &self->props[i]; - store_prop(self, map_path, &st, store, handle, prop->urid, prop->value); - } - - return st; -} - -static void -retrieve_prop(Params* self, - LV2_State_Status* restore_status, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - LV2_URID key) -{ - // Retrieve value from saved state - size_t vsize = 0; - uint32_t vtype = 0; - uint32_t vflags = 0; - const void* value = retrieve(handle, key, &vsize, &vtype, &vflags); - - // Set plugin instance state - const LV2_State_Status st = - value ? set_parameter(self, key, vsize, vtype, value, true) - : LV2_STATE_ERR_NO_PROPERTY; - - if (!*restore_status) { - *restore_status = st; // Set status if there has been no error yet - } -} - -/** State restore method. */ -static LV2_State_Status -restore(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - Params* self = (Params*)instance; - LV2_State_Status st = LV2_STATE_SUCCESS; - - for (unsigned i = 0; i < N_PROPS; ++i) { - retrieve_prop(self, &st, retrieve, handle, self->props[i].urid); - } - - return st; -} - -static inline bool -subject_is_plugin(Params* self, const LV2_Atom_URID* subject) -{ - // This simple plugin only supports one subject: itself - return (!subject || (subject->atom.type == self->uris.atom_URID && - subject->body == self->uris.plugin)); -} - -static void -run(LV2_Handle instance, uint32_t sample_count) -{ - Params* self = (Params*)instance; - URIs* uris = &self->uris; - - // Initially, self->out_port contains a Chunk with size set to capacity - // Set up forge to write directly to output port - const uint32_t out_capacity = self->out_port->atom.size; - lv2_atom_forge_set_buffer( - &self->forge, (uint8_t*)self->out_port, out_capacity); - - // Start a sequence in the output port - LV2_Atom_Forge_Frame out_frame; - lv2_atom_forge_sequence_head(&self->forge, &out_frame, 0); - - // Read incoming events - LV2_ATOM_SEQUENCE_FOREACH (self->in_port, ev) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == uris->patch_Set) { - // Get the property and value of the set message - const LV2_Atom_URID* subject = NULL; - const LV2_Atom_URID* property = NULL; - const LV2_Atom* value = NULL; - - // clang-format off - lv2_atom_object_get(obj, - uris->patch_subject, (const LV2_Atom**)&subject, - uris->patch_property, (const LV2_Atom**)&property, - uris->patch_value, &value, - 0); - // clang-format on - - if (!subject_is_plugin(self, subject)) { - lv2_log_error(&self->log, "Set for unknown subject\n"); - } else if (!property) { - lv2_log_error(&self->log, "Set with no property\n"); - } else if (property->atom.type != uris->atom_URID) { - lv2_log_error(&self->log, "Set property is not a URID\n"); - } else { - // Set property to the given value - const LV2_URID key = property->body; - set_parameter(self, key, value->size, value->type, value + 1, false); - } - } else if (obj->body.otype == uris->patch_Get) { - // Get the property of the get message - const LV2_Atom_URID* subject = NULL; - const LV2_Atom_URID* property = NULL; - - // clang-format off - lv2_atom_object_get(obj, - uris->patch_subject, (const LV2_Atom**)&subject, - uris->patch_property, (const LV2_Atom**)&property, - 0); - // clang-format on - - if (!subject_is_plugin(self, subject)) { - lv2_log_error(&self->log, "Get with unknown subject\n"); - } else if (!property) { - // Get with no property, emit complete state - lv2_atom_forge_frame_time(&self->forge, ev->time.frames); - LV2_Atom_Forge_Frame pframe; - lv2_atom_forge_object(&self->forge, &pframe, 0, uris->patch_Put); - lv2_atom_forge_key(&self->forge, uris->patch_body); - - LV2_Atom_Forge_Frame bframe; - lv2_atom_forge_object(&self->forge, &bframe, 0, 0); - save(self, write_param_to_forge, &self->forge, 0, NULL); - - lv2_atom_forge_pop(&self->forge, &bframe); - lv2_atom_forge_pop(&self->forge, &pframe); - } else if (property->atom.type != uris->atom_URID) { - lv2_log_error(&self->log, "Get property is not a URID\n"); - } else { - // Get for a specific property - const LV2_URID key = property->body; - const LV2_Atom* value = get_parameter(self, key); - if (value) { - lv2_atom_forge_frame_time(&self->forge, ev->time.frames); - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_object(&self->forge, &frame, 0, uris->patch_Set); - lv2_atom_forge_key(&self->forge, uris->patch_property); - lv2_atom_forge_urid(&self->forge, property->body); - store_prop(self, - NULL, - NULL, - write_param_to_forge, - &self->forge, - uris->patch_value, - value); - lv2_atom_forge_pop(&self->forge, &frame); - } - } - } else { - lv2_log_trace( - &self->log, "Unknown object type <%s>\n", unmap(self, obj->body.otype)); - } - } - - if (self->state.spring.body > 0.0f) { - const float spring = self->state.spring.body; - self->state.spring.body = (spring >= 0.001) ? spring - 0.001f : 0.0f; - lv2_atom_forge_frame_time(&self->forge, 0); - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_object(&self->forge, &frame, 0, uris->patch_Set); - - lv2_atom_forge_key(&self->forge, uris->patch_property); - lv2_atom_forge_urid(&self->forge, uris->eg_spring); - lv2_atom_forge_key(&self->forge, uris->patch_value); - lv2_atom_forge_float(&self->forge, self->state.spring.body); - - lv2_atom_forge_pop(&self->forge, &frame); - } - - lv2_atom_forge_pop(&self->forge, &out_frame); -} - -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 = {EG_PARAMS_URI, - instantiate, - connect_port, - NULL, // activate, - run, - NULL, // deactivate, - cleanup, - extension_data}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* -lv2_descriptor(uint32_t index) -{ - return (index == 0) ? &descriptor : NULL; -} diff --git a/plugins/eg-params.lv2/params.ttl b/plugins/eg-params.lv2/params.ttl deleted file mode 100644 index 931c826..0000000 --- a/plugins/eg-params.lv2/params.ttl +++ /dev/null @@ -1,126 +0,0 @@ -@prefix atom: <http://lv2plug.in/ns/ext/atom#> . -@prefix doap: <http://usefulinc.com/ns/doap#> . -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix param: <http://lv2plug.in/ns/ext/parameters#> . -@prefix patch: <http://lv2plug.in/ns/ext/patch#> . -@prefix plug: <http://lv2plug.in/plugins/eg-params#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix state: <http://lv2plug.in/ns/ext/state#> . -@prefix urid: <http://lv2plug.in/ns/ext/urid#> . -@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . - -# An existing parameter or RDF property can be used as a parameter. The LV2 -# parameters extension <http://lv2plug.in/ns/ext/parameters> defines many -# common audio parameters. Where possible, existing parameters should be used -# so hosts can intelligently control plugins. - -# If no suitable parameter exists, one can be defined for the plugin like so: - -plug:int - a lv2:Parameter ; - rdfs:label "int" ; - rdfs:range atom:Int . - -plug:long - a lv2:Parameter ; - rdfs:label "long" ; - rdfs:range atom:Long . - -plug:float - a lv2:Parameter ; - rdfs:label "float" ; - rdfs:range atom:Float . - -plug:double - a lv2:Parameter ; - rdfs:label "double" ; - rdfs:range atom:Double . - -plug:bool - a lv2:Parameter ; - rdfs:label "bool" ; - rdfs:range atom:Bool . - -plug:string - a lv2:Parameter ; - rdfs:label "string" ; - rdfs:range atom:String . - -plug:path - a lv2:Parameter ; - rdfs:label "path" ; - rdfs:range atom:Path . - -plug:lfo - a lv2:Parameter ; - rdfs:label "LFO" ; - rdfs:range atom:Float ; - lv2:minimum -1.0 ; - lv2:maximum 1.0 . - -plug:spring - a lv2:Parameter ; - rdfs:label "spring" ; - rdfs:range atom:Float . - -# Most of the plugin description is similar to the others we have seen, but -# this plugin has only two ports, for receiving and sending messages used to -# manipulate and access parameters. -<http://lv2plug.in/plugins/eg-params> - a lv2:Plugin , - lv2:UtilityPlugin ; - doap:name "Example Parameters" ; - doap:license <http://opensource.org/licenses/isc> ; - lv2:project <http://lv2plug.in/ns/lv2> ; - lv2:requiredFeature urid:map ; - lv2:optionalFeature lv2:hardRTCapable , - state:loadDefaultState ; - lv2:extensionData state:interface ; - lv2:port [ - a lv2:InputPort , - atom:AtomPort ; - atom:bufferType atom:Sequence ; - atom:supports patch:Message ; - lv2:designation lv2:control ; - lv2:index 0 ; - lv2:symbol "in" ; - lv2:name "In" - ] , [ - a lv2:OutputPort , - atom:AtomPort ; - atom:bufferType atom:Sequence ; - atom:supports patch:Message ; - lv2:designation lv2:control ; - lv2:index 1 ; - lv2:symbol "out" ; - lv2:name "Out" - ] ; -# The plugin must list all parameters that can be written (e.g. changed by the -# user) as patch:writable: - patch:writable plug:int , - plug:long , - plug:float , - plug:double , - plug:bool , - plug:string , - plug:path , - plug:spring ; -# Similarly, parameters that may change internally must be listed as patch:readable, -# meaning to host should watch for changes to the parameter's value: - patch:readable plug:lfo , - plug:spring ; -# Parameters map directly to properties of the plugin's state. So, we can -# specify initial parameter values with the state:state property. The -# state:loadDefaultState feature (required above) requires that the host loads -# the default state after instantiation but before running the plugin. - state:state [ - plug:int 0 ; - plug:long "0"^^xsd:long ; - plug:float "0.1234"^^xsd:float ; - plug:double "0e0"^^xsd:double ; - plug:bool false ; - plug:string "Hello, world" ; - plug:path <params.ttl> ; - plug:spring "0.0"^^xsd:float ; - plug:lfo "0.0"^^xsd:float - ] . diff --git a/plugins/eg-params.lv2/state_map.h b/plugins/eg-params.lv2/state_map.h deleted file mode 100644 index a223243..0000000 --- a/plugins/eg-params.lv2/state_map.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2016 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "lv2/atom/atom.h" -#include "lv2/urid/urid.h" - -#include <stdarg.h> -#include <stdint.h> -#include <stdlib.h> - -/** Entry in an array that serves as a dictionary of properties. */ -typedef struct { - const char* uri; - LV2_URID urid; - LV2_Atom* value; -} StateMapItem; - -/** Comparator for StateMapItems sorted by URID. */ -static int -state_map_cmp(const void* a, const void* b) -{ - const StateMapItem* ka = (const StateMapItem*)a; - const StateMapItem* kb = (const StateMapItem*)b; - if (ka->urid < kb->urid) { - return -1; - } - - if (kb->urid < ka->urid) { - return 1; - } - - return 0; -} - -/** Helper macro for terse state map initialisation. */ -#define STATE_MAP_INIT(type, ptr) \ - (LV2_ATOM__##type), (sizeof(*(ptr)) - sizeof(LV2_Atom)), (ptr) - -/** - Initialise a state map. - - The variable parameters list must be NULL terminated, and is a sequence of - const char* uri, const char* type, uint32_t size, LV2_Atom* value. The - value must point to a valid atom that resides elsewhere, the state map is - only an index and does not contain actual state values. The macro - STATE_MAP_INIT can be used to make simpler code when state is composed of - standard atom types, for example: - - struct Plugin { - LV2_URID_Map* map; - StateMapItem props[3]; - // ... - }; - - state_map_init( - self->props, self->map, self->map->handle, - PLUG_URI "#gain", STATE_MAP_INIT(Float, &state->gain), - PLUG_URI "#offset", STATE_MAP_INIT(Int, &state->offset), - PLUG_URI "#file", STATE_MAP_INIT(Path, &state->file), - NULL); -*/ -static void -state_map_init( - StateMapItem dict[], - LV2_URID_Map* map, - LV2_URID_Map_Handle handle, - /* const char* uri, const char* type, uint32_t size, LV2_Atom* value */...) -{ - // Set dict entries from parameters - unsigned i = 0; - va_list args; - va_start(args, handle); - for (const char* uri = NULL; (uri = va_arg(args, const char*)); ++i) { - const char* type = va_arg(args, const char*); - const uint32_t size = va_arg(args, uint32_t); - LV2_Atom* const value = va_arg(args, LV2_Atom*); - dict[i].uri = uri; - dict[i].urid = map->map(map->handle, uri); - dict[i].value = value; - dict[i].value->size = size; - dict[i].value->type = map->map(map->handle, type); - } - va_end(args); - - // Sort for fast lookup by URID by state_map_find() - qsort(dict, i, sizeof(StateMapItem), state_map_cmp); -} - -/** - Retrieve an item from a state map by URID. - - This takes O(lg(n)) time, and is useful for implementing generic property - access with little code, for example to respond to patch:Get messages for a - specific property. -*/ -static StateMapItem* -state_map_find(StateMapItem dict[], uint32_t n_entries, LV2_URID urid) -{ - const StateMapItem key = {NULL, urid, NULL}; - return (StateMapItem*)bsearch( - &key, dict, n_entries, sizeof(StateMapItem), state_map_cmp); -} diff --git a/plugins/eg-sampler.lv2/README.txt b/plugins/eg-sampler.lv2/README.txt deleted file mode 100644 index 8d136fa..0000000 --- a/plugins/eg-sampler.lv2/README.txt +++ /dev/null @@ -1,14 +0,0 @@ -== Sampler == - -This plugin loads a single sample from a .wav file and plays it back when a MIDI -note on is received. Any sample on the system can be loaded via another event. -A Gtk UI is included which does this, but the host can as well. - -This plugin illustrates: - -- UI <==> Plugin communication via events -- Use of the worker extension for non-realtime tasks (sample loading) -- Use of the log extension to print log messages via the host -- Saving plugin state via the state extension -- Dynamic plugin control via the same properties saved to state -- Network-transparent waveform display with incremental peak transmission diff --git a/plugins/eg-sampler.lv2/atom_sink.h b/plugins/eg-sampler.lv2/atom_sink.h deleted file mode 100644 index 8966eb2..0000000 --- a/plugins/eg-sampler.lv2/atom_sink.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" - -#include <stdint.h> -#include <string.h> - -/** - A forge sink that writes to an atom buffer. - - It is assumed that the handle points to an LV2_Atom large enough to store - the forge output. The forged result is in the body of the buffer atom. -*/ -static LV2_Atom_Forge_Ref -atom_sink(LV2_Atom_Forge_Sink_Handle handle, const void* buf, uint32_t size) -{ - LV2_Atom* atom = (LV2_Atom*)handle; - const uint32_t offset = lv2_atom_total_size(atom); - memcpy((char*)atom + offset, buf, size); - atom->size += size; - return offset; -} - -/** - Dereference counterpart to atom_sink(). -*/ -static LV2_Atom* -atom_sink_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref) -{ - return (LV2_Atom*)((char*)handle + ref); -} diff --git a/plugins/eg-sampler.lv2/click.wav b/plugins/eg-sampler.lv2/click.wav Binary files differdeleted file mode 100644 index 520a18c..0000000 --- a/plugins/eg-sampler.lv2/click.wav +++ /dev/null diff --git a/plugins/eg-sampler.lv2/manifest.ttl.in b/plugins/eg-sampler.lv2/manifest.ttl.in deleted file mode 100644 index e688256..0000000 --- a/plugins/eg-sampler.lv2/manifest.ttl.in +++ /dev/null @@ -1,19 +0,0 @@ -# Unlike the previous examples, this manifest lists more than one resource: the -# plugin as usual, and the UI. The descriptions are similar, but have -# different types, so the host can decide from this file alone whether or not -# it is interested, and avoid following the `rdfs:seeAlso` link if not (though -# in this case both are described in the same file). - -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . - -<http://lv2plug.in/plugins/eg-sampler> - a lv2:Plugin ; - lv2:binary <sampler@LIB_EXT@> ; - rdfs:seeAlso <sampler.ttl> . - -<http://lv2plug.in/plugins/eg-sampler#ui> - a ui:GtkUI ; - lv2:binary <sampler_ui@LIB_EXT@> ; - rdfs:seeAlso <sampler.ttl> . diff --git a/plugins/eg-sampler.lv2/meson.build b/plugins/eg-sampler.lv2/meson.build deleted file mode 100644 index 0916ff6..0000000 --- a/plugins/eg-sampler.lv2/meson.build +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -plugin_sources = files('sampler.c') -ui_sources = files('sampler_ui.c') -bundle_name = 'eg-sampler.lv2' -data_filenames = ['manifest.ttl.in', 'sampler.ttl', 'click.wav'] - -samplerate_dep = dependency('samplerate', - version: '>= 0.1.0', - required: get_option('plugins')) - -sndfile_dep = dependency('sndfile', - version: '>= 1.0.0', - required: get_option('plugins')) - -gtk2_dep = dependency('gtk+-2.0', - include_type: 'system', - required: get_option('plugins'), - version: '>= 2.18.0') - -if samplerate_dep.found() and sndfile_dep.found() - module = shared_library( - 'sampler', - plugin_sources, - c_args: c_suppressions, - dependencies: [lv2_dep, m_dep, samplerate_dep, sndfile_dep], - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: lv2dir / bundle_name, - name_prefix: '', - ) - - extension = '.' + module.full_path().split('.')[-1] - config = configuration_data({'LIB_EXT': extension}) - - foreach filename : data_filenames - if filename.endswith('.in') - configure_file( - configuration: config, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename.substring(0, -3), - ) - else - configure_file( - copy: true, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename, - ) - endif - endforeach - - if gtk2_dep.found() - ui_suppressions = c_suppressions - if cc.get_id() == 'gcc' - ui_suppressions += ['-Wno-strict-overflow'] - endif - - shared_library( - 'sampler_ui', - ui_sources, - c_args: ui_suppressions, - dependencies: [lv2_dep, gtk2_dep], - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: lv2dir / bundle_name, - name_prefix: '', - ) - endif -endif diff --git a/plugins/eg-sampler.lv2/peaks.h b/plugins/eg-sampler.lv2/peaks.h deleted file mode 100644 index 47d6616..0000000 --- a/plugins/eg-sampler.lv2/peaks.h +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2016 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef PEAKS_H_INCLUDED -#define PEAKS_H_INCLUDED - -/** - This file defines utilities for sending and receiving audio peaks for - waveform display. The functionality is divided into two objects: - PeaksSender, for sending peaks updates from the plugin, and PeaksReceiver, - for receiving such updates and caching the peaks. - - This allows peaks for a waveform of any size at any resolution to be - requested, with reasonably sized incremental updates sent over plugin ports. -*/ - -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/urid/urid.h" - -#include <math.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#define PEAKS_URI "http://lv2plug.in/ns/peaks#" -#define PEAKS__PeakUpdate PEAKS_URI "PeakUpdate" -#define PEAKS__magnitudes PEAKS_URI "magnitudes" -#define PEAKS__offset PEAKS_URI "offset" -#define PEAKS__total PEAKS_URI "total" - -#ifndef MIN -# define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif -#ifndef MAX -# define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -typedef struct { - LV2_URID atom_Float; - LV2_URID atom_Int; - LV2_URID atom_Vector; - LV2_URID peaks_PeakUpdate; - LV2_URID peaks_magnitudes; - LV2_URID peaks_offset; - LV2_URID peaks_total; -} PeaksURIs; - -typedef struct { - PeaksURIs uris; ///< URIDs used in protocol - const float* samples; ///< Sample data - uint32_t n_samples; ///< Total number of samples - uint32_t n_peaks; ///< Total number of peaks - uint32_t current_offset; ///< Current peak offset - bool sending; ///< True iff currently sending -} PeaksSender; - -typedef struct { - PeaksURIs uris; ///< URIDs used in protocol - float* peaks; ///< Received peaks, or zeroes - uint32_t n_peaks; ///< Total number of peaks -} PeaksReceiver; - -/** - Map URIs used in the peaks protocol. -*/ -static inline void -peaks_map_uris(PeaksURIs* uris, LV2_URID_Map* map) -{ - uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); - uris->atom_Int = map->map(map->handle, LV2_ATOM__Int); - uris->atom_Vector = map->map(map->handle, LV2_ATOM__Vector); - uris->peaks_PeakUpdate = map->map(map->handle, PEAKS__PeakUpdate); - uris->peaks_magnitudes = map->map(map->handle, PEAKS__magnitudes); - uris->peaks_offset = map->map(map->handle, PEAKS__offset); - uris->peaks_total = map->map(map->handle, PEAKS__total); -} - -/** - Initialise peaks sender. The new sender is inactive and will do nothing - when `peaks_sender_send()` is called, until a transmission is started with - `peaks_sender_start()`. -*/ -static inline PeaksSender* -peaks_sender_init(PeaksSender* sender, LV2_URID_Map* map) -{ - memset(sender, 0, sizeof(*sender)); - peaks_map_uris(&sender->uris, map); - return sender; -} - -/** - Prepare to start a new peaks transmission. After this is called, the peaks - can be sent with successive calls to `peaks_sender_send()`. -*/ -static inline void -peaks_sender_start(PeaksSender* sender, - const float* samples, - uint32_t n_samples, - uint32_t n_peaks) -{ - sender->samples = samples; - sender->n_samples = n_samples; - sender->n_peaks = n_peaks; - sender->current_offset = 0; - sender->sending = true; -} - -/** - Forge a message which sends a range of peaks. Writes a peaks:PeakUpdate - object to `forge`, like: - - [source,turtle] - ---- - [] - a peaks:PeakUpdate ; - peaks:offset 256 ; - peaks:total 1024 ; - peaks:magnitudes [ 0.2f, 0.3f, ... ] . - ---- -*/ -static inline bool -peaks_sender_send(PeaksSender* sender, - LV2_Atom_Forge* forge, - uint32_t n_frames, - uint32_t offset) -{ - const PeaksURIs* uris = &sender->uris; - if (!sender->sending || sender->current_offset >= sender->n_peaks) { - return sender->sending = false; - } - - // Start PeakUpdate object - lv2_atom_forge_frame_time(forge, offset); - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_object(forge, &frame, 0, uris->peaks_PeakUpdate); - - // eg:offset = OFFSET - lv2_atom_forge_key(forge, uris->peaks_offset); - lv2_atom_forge_int(forge, (int32_t)sender->current_offset); - - // eg:total = TOTAL - lv2_atom_forge_key(forge, uris->peaks_total); - lv2_atom_forge_int(forge, (int32_t)sender->n_peaks); - - // eg:magnitudes = Vector<Float>(PEAK, PEAK, ...) - lv2_atom_forge_key(forge, uris->peaks_magnitudes); - LV2_Atom_Forge_Frame vec_frame; - lv2_atom_forge_vector_head( - forge, &vec_frame, sizeof(float), uris->atom_Float); - - // Calculate how many peaks to send this update - const uint32_t chunk_size = MAX(1U, sender->n_samples / sender->n_peaks); - const uint32_t space = forge->size - forge->offset; - const uint32_t remaining = sender->n_peaks - sender->current_offset; - const uint32_t n_update = - MIN(remaining, MIN(n_frames / 4U, space / sizeof(float))); - - // Calculate peak (maximum magnitude) for each chunk - for (uint32_t i = 0; i < n_update; ++i) { - const uint32_t start = (sender->current_offset + i) * chunk_size; - float peak = 0.0f; - for (uint32_t j = 0; j < chunk_size; ++j) { - peak = fmaxf(peak, fabsf(sender->samples[start + j])); - } - lv2_atom_forge_float(forge, peak); - } - - // Finish message - lv2_atom_forge_pop(forge, &vec_frame); - lv2_atom_forge_pop(forge, &frame); - - sender->current_offset += n_update; - return true; -} - -/** - Initialise a peaks receiver. The receiver stores an array of all peaks, - which is updated incrementally with peaks_receiver_receive(). -*/ -static inline PeaksReceiver* -peaks_receiver_init(PeaksReceiver* receiver, LV2_URID_Map* map) -{ - memset(receiver, 0, sizeof(*receiver)); - peaks_map_uris(&receiver->uris, map); - return receiver; -} - -/** - Clear stored peaks and free all memory. This should be called when the - peaks are to be updated with a different audio source. -*/ -static inline void -peaks_receiver_clear(PeaksReceiver* receiver) -{ - free(receiver->peaks); - receiver->peaks = NULL; - receiver->n_peaks = 0; -} - -/** - Handle PeakUpdate message. - - The stored peaks array is updated with the slice of peaks in `update`, - resizing if necessary while preserving contents. - - Returns 0 if peaks have been updated, negative on error. -*/ -static inline int -peaks_receiver_receive(PeaksReceiver* receiver, const LV2_Atom_Object* update) -{ - const PeaksURIs* uris = &receiver->uris; - - // Get properties of interest from update - const LV2_Atom_Int* offset = NULL; - const LV2_Atom_Int* total = NULL; - const LV2_Atom_Vector* peaks = NULL; - - // clang-format off - lv2_atom_object_get_typed(update, - uris->peaks_offset, &offset, uris->atom_Int, - uris->peaks_total, &total, uris->atom_Int, - uris->peaks_magnitudes, &peaks, uris->atom_Vector, - 0); - // clang-format on - - if (!offset || !total || !peaks || - peaks->body.child_type != uris->atom_Float) { - return -1; // Invalid update - } - - const uint32_t n = (uint32_t)total->body; - if (receiver->n_peaks != n) { - // Update is for a different total number of peaks, resize - receiver->peaks = (float*)realloc(receiver->peaks, n * sizeof(float)); - if (receiver->n_peaks > 0 && receiver->n_peaks < n) { - /* The peaks array is being expanded. Copy the old peaks, - duplicating each as necessary to fill the new peaks buffer. - This preserves the current peaks so that the peaks array can be - reasonably drawn at any time, but the resolution will increase - as new updates arrive. */ - const int64_t n_per = n / receiver->n_peaks; - for (int64_t i = n - 1; i >= 0; --i) { - receiver->peaks[i] = receiver->peaks[i / n_per]; - } - } else if (receiver->n_peaks > 0) { - /* The peak array is being shrunk. Similar to the above. */ - const int64_t n_per = receiver->n_peaks / n; - for (int64_t i = n - 1; i >= 0; --i) { - receiver->peaks[i] = receiver->peaks[i * n_per]; - } - } - receiver->n_peaks = n; - } - - // Copy vector contents to corresponding range in peaks array - memcpy(receiver->peaks + offset->body, - peaks + 1, - peaks->atom.size - sizeof(LV2_Atom_Vector_Body)); - - return 0; -} - -#endif // PEAKS_H_INCLUDED diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c deleted file mode 100644 index d870a07..0000000 --- a/plugins/eg-sampler.lv2/sampler.c +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright 2011-2016 David Robillard <d@drobilla.net> -// Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org> -// Copyright 2011 James Morris <jwm.art.net@gmail.com> -// SPDX-License-Identifier: ISC - -#include "atom_sink.h" -#include "peaks.h" -#include "uris.h" - -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/state/state.h" -#include "lv2/urid/urid.h" -#include "lv2/worker/worker.h" - -#include <samplerate.h> -#include <sndfile.h> - -#include <math.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -enum { SAMPLER_CONTROL = 0, SAMPLER_NOTIFY = 1, SAMPLER_OUT = 2 }; - -typedef struct { - SF_INFO info; // Info about sample from sndfile - float* data; // Sample data in float - char* path; // Path of file - uint32_t path_len; // Length of path -} Sample; - -typedef struct { - // Features - LV2_URID_Map* map; - LV2_Worker_Schedule* schedule; - LV2_Log_Logger logger; - - // Ports - const LV2_Atom_Sequence* control_port; - LV2_Atom_Sequence* notify_port; - float* output_port; - - // Communication utilities - LV2_Atom_Forge_Frame notify_frame; ///< Cached for worker replies - LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread - PeaksSender psend; ///< Audio peaks sender - - // URIs - SamplerURIs uris; - - // Playback state - Sample* sample; - uint32_t frame_offset; - float gain; - float gain_dB; - sf_count_t frame; - bool play; - bool activated; - bool gain_changed; - bool sample_changed; - int sample_rate; -} Sampler; - -/** - An atom-like message used internally to apply/free samples. - - This is only used internally to communicate with the worker, it is never - sent to the outside world via a port since it is not POD. It is convenient - to use an Atom header so actual atoms can be easily sent through the same - ringbuffer. -*/ -typedef struct { - LV2_Atom atom; - Sample* sample; -} SampleMessage; - -/** - Convert an interleaved audio buffer to mono. - - This simply ignores the data on all channels but the first. -*/ -static sf_count_t -convert_to_mono(float* data, sf_count_t num_input_frames, uint32_t channels) -{ - sf_count_t num_output_frames = 0; - - for (sf_count_t i = 0; i < num_input_frames * channels; i += channels) { - data[num_output_frames++] = data[i]; - } - - return num_output_frames; -} - -/** - Load a new sample and return it. - - Since this is of course not a real-time safe action, this is called in the - worker thread only. The sample is loaded and returned only, plugin state is - not modified. -*/ -static Sample* -load_sample(LV2_Log_Logger* logger, const char* path, const int sample_rate) -{ - lv2_log_trace(logger, "Loading %s\n", path); - - const size_t path_len = strlen(path); - Sample* const sample = (Sample*)calloc(1, sizeof(Sample)); - SF_INFO* const info = &sample->info; - SNDFILE* const sndfile = sf_open(path, SFM_READ, info); - float* data = NULL; - bool error = true; - if (!sndfile || !info->frames) { - lv2_log_error(logger, "Failed to open %s\n", path); - } else if (!(data = (float*)malloc(sizeof(float) * info->frames * - info->channels))) { - lv2_log_error(logger, "Failed to allocate memory for sample\n"); - } else { - error = false; - } - - if (error) { - free(sample); - free(data); - sf_close(sndfile); - return NULL; - } - - sf_seek(sndfile, 0UL, SEEK_SET); - sf_read_float(sndfile, data, info->frames * info->channels); - sf_close(sndfile); - - if (info->channels != 1) { - info->frames = convert_to_mono(data, info->frames, info->channels); - info->channels = 1; - } - - if (info->samplerate != sample_rate) { - lv2_log_trace(logger, - "Converting from %d Hz to %d Hz\n", - info->samplerate, - sample_rate); - - const double src_ratio = (double)sample_rate / (double)info->samplerate; - const double output_length = ceil((double)info->frames * src_ratio); - const long output_frames = (long)output_length; - float* const output_buffer = (float*)malloc(sizeof(float) * output_frames); - - SRC_DATA src_data = { - data, - output_buffer, - info->frames, - output_frames, - 0, - 0, - 0, - src_ratio, - }; - - if (src_simple(&src_data, SRC_SINC_BEST_QUALITY, 1) != 0) { - lv2_log_error(logger, "Sample rate conversion failed\n"); - free(output_buffer); - } else { - // Replace original data with converted buffer - free(data); - data = output_buffer; - info->frames = src_data.output_frames_gen; - } - } else { - lv2_log_trace( - logger, "Sample matches the current rate of %d Hz\n", sample_rate); - } - - // Fill sample struct and return it - sample->data = data; - sample->path = (char*)malloc(path_len + 1); - sample->path_len = (uint32_t)path_len; - memcpy(sample->path, path, path_len + 1); - - return sample; -} - -static void -free_sample(Sampler* self, Sample* sample) -{ - if (sample) { - lv2_log_trace(&self->logger, "Freeing %s\n", sample->path); - free(sample->path); - free(sample->data); - free(sample); - } -} - -/** - Do work in a non-realtime thread. - - This is called for every piece of work scheduled in the audio thread using - self->schedule->schedule_work(). A reply can be sent back to the audio - thread using the provided `respond` function. -*/ -static LV2_Worker_Status -work(LV2_Handle instance, - LV2_Worker_Respond_Function respond, - LV2_Worker_Respond_Handle handle, - uint32_t size, - const void* data) -{ - Sampler* self = (Sampler*)instance; - const LV2_Atom* atom = (const LV2_Atom*)data; - if (atom->type == self->uris.eg_freeSample) { - // Free old sample - const SampleMessage* msg = (const SampleMessage*)data; - free_sample(self, msg->sample); - } else if (atom->type == self->forge.Object) { - // Handle set message (load sample). - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)data; - const char* path = read_set_file(&self->uris, obj); - if (!path) { - lv2_log_error(&self->logger, "Malformed set file request\n"); - return LV2_WORKER_ERR_UNKNOWN; - } - - // Load sample. - Sample* sample = load_sample(&self->logger, path, self->sample_rate); - if (sample) { - // Send new sample to run() to be applied - respond(handle, sizeof(Sample*), &sample); - } - } - - return LV2_WORKER_SUCCESS; -} - -/** - Handle a response from work() in the audio thread. - - When running normally, this will be called by the host after run(). When - freewheeling, this will be called immediately at the point the work was - scheduled. -*/ -static LV2_Worker_Status -work_response(LV2_Handle instance, uint32_t size, const void* data) -{ - Sampler* self = (Sampler*)instance; - Sample* old_sample = self->sample; - Sample* new_sample = *(Sample* const*)data; - - // Install the new sample - self->sample = *(Sample* const*)data; - - // Stop playing previous sample, which can be larger than new one - self->frame = 0; - self->play = false; - - // Schedule work to free the old sample - SampleMessage msg = {{sizeof(Sample*), self->uris.eg_freeSample}, old_sample}; - self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg); - - // Send a notification that we're using a new sample - lv2_atom_forge_frame_time(&self->forge, self->frame_offset); - write_set_file( - &self->forge, &self->uris, new_sample->path, new_sample->path_len); - - return LV2_WORKER_SUCCESS; -} - -static void -connect_port(LV2_Handle instance, uint32_t port, void* data) -{ - Sampler* self = (Sampler*)instance; - switch (port) { - case SAMPLER_CONTROL: - self->control_port = (const LV2_Atom_Sequence*)data; - break; - case SAMPLER_NOTIFY: - self->notify_port = (LV2_Atom_Sequence*)data; - break; - case SAMPLER_OUT: - self->output_port = (float*)data; - break; - default: - break; - } -} - -static LV2_Handle -instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* path, - const LV2_Feature* const* features) -{ - // Allocate and initialise instance structure. - Sampler* self = (Sampler*)calloc(1, sizeof(Sampler)); - if (!self) { - return NULL; - } - - // Get host features - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - LV2_WORKER__schedule, &self->schedule, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - // Map URIs and initialise forge - map_sampler_uris(self->map, &self->uris); - lv2_atom_forge_init(&self->forge, self->map); - peaks_sender_init(&self->psend, self->map); - - self->gain = 1.0f; - self->gain_dB = 0.0f; - self->sample_rate = (int)rate; - - return (LV2_Handle)self; -} - -static void -cleanup(LV2_Handle instance) -{ - Sampler* self = (Sampler*)instance; - free_sample(self, self->sample); - free(self); -} - -static void -activate(LV2_Handle instance) -{ - ((Sampler*)instance)->activated = true; -} - -static void -deactivate(LV2_Handle instance) -{ - ((Sampler*)instance)->activated = false; -} - -/** Define a macro for converting a gain in dB to a coefficient. */ -#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g)*0.05f) : 0.0f) - -/** - Handle an incoming event in the audio thread. - - This performs any actions triggered by an event, such as the start of sample - playback, a sample change, or responding to requests from the UI. -*/ -static void -handle_event(Sampler* self, LV2_Atom_Event* ev) -{ - SamplerURIs* uris = &self->uris; - PeaksURIs* peaks_uris = &self->psend.uris; - - if (ev->body.type == uris->midi_Event) { - const uint8_t* const msg = (const uint8_t*)(ev + 1); - switch (lv2_midi_message_type(msg)) { - case LV2_MIDI_MSG_NOTE_ON: - self->frame = 0; - self->play = true; - break; - default: - break; - } - } else if (lv2_atom_forge_is_object_type(&self->forge, ev->body.type)) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == uris->patch_Set) { - // Get the property and value of the set message - const LV2_Atom* property = NULL; - const LV2_Atom* value = NULL; - - // clang-format off - lv2_atom_object_get(obj, - uris->patch_property, &property, - uris->patch_value, &value, - 0); - // clang-format on - - if (!property) { - lv2_log_error(&self->logger, "Set message with no property\n"); - return; - } - - if (property->type != uris->atom_URID) { - lv2_log_error(&self->logger, "Set property is not a URID\n"); - return; - } - - const uint32_t key = ((const LV2_Atom_URID*)property)->body; - if (key == uris->eg_sample) { - // Sample change, send it to the worker. - lv2_log_trace(&self->logger, "Scheduling sample change\n"); - self->schedule->schedule_work( - self->schedule->handle, lv2_atom_total_size(&ev->body), &ev->body); - } else if (key == uris->param_gain) { - // Gain change - if (value->type == uris->atom_Float) { - self->gain_dB = ((LV2_Atom_Float*)value)->body; - self->gain = DB_CO(self->gain_dB); - } - } - } else if (obj->body.otype == uris->patch_Get && self->sample) { - const LV2_Atom_URID* accept = NULL; - const LV2_Atom_Int* n_peaks = NULL; - - // clang-format off - lv2_atom_object_get_typed( - obj, - uris->patch_accept, &accept, uris->atom_URID, - peaks_uris->peaks_total, &n_peaks, peaks_uris->atom_Int, - 0); - // clang-format on - - if (accept && accept->body == peaks_uris->peaks_PeakUpdate) { - // Received a request for peaks, prepare for transmission - peaks_sender_start(&self->psend, - self->sample->data, - self->sample->info.frames, - n_peaks->body); - } else { - // Received a get message, emit our state (probably to UI) - lv2_atom_forge_frame_time(&self->forge, self->frame_offset); - write_set_file(&self->forge, - &self->uris, - self->sample->path, - self->sample->path_len); - } - } else { - lv2_log_trace(&self->logger, "Unknown object type %u\n", obj->body.otype); - } - } else { - lv2_log_trace(&self->logger, "Unknown event type %u\n", ev->body.type); - } -} - -/** - Output audio for a slice of the current cycle. -*/ -static void -render(Sampler* self, uint32_t start, uint32_t end) -{ - float* output = self->output_port; - - if (self->play && self->sample) { - // Start/continue writing sample to output - for (; start < end; ++start) { - output[start] = self->sample->data[self->frame] * self->gain; - if (++self->frame == self->sample->info.frames) { - self->play = false; // Reached end of sample - break; - } - } - } - - // Write silence to remaining buffer - for (; start < end; ++start) { - output[start] = 0.0f; - } -} - -static void -run(LV2_Handle instance, uint32_t sample_count) -{ - Sampler* self = (Sampler*)instance; - - // Set up forge to write directly to notify output port. - const uint32_t notify_capacity = self->notify_port->atom.size; - lv2_atom_forge_set_buffer( - &self->forge, (uint8_t*)self->notify_port, notify_capacity); - - // Start a sequence in the notify output port. - lv2_atom_forge_sequence_head(&self->forge, &self->notify_frame, 0); - - // Send update to UI if gain has changed due to state restore - if (self->gain_changed) { - lv2_atom_forge_frame_time(&self->forge, 0); - write_set_gain(&self->forge, &self->uris, self->gain_dB); - self->gain_changed = false; - } - - // Send update to UI if sample has changed due to state restore - if (self->sample_changed) { - lv2_atom_forge_frame_time(&self->forge, 0); - write_set_file( - &self->forge, &self->uris, self->sample->path, self->sample->path_len); - self->sample_changed = false; - } - - // Iterate over incoming events, emitting audio along the way - self->frame_offset = 0; - LV2_ATOM_SEQUENCE_FOREACH (self->control_port, ev) { - // Render output up to the time of this event - render(self, self->frame_offset, ev->time.frames); - - /* Update current frame offset to this event's time. This is stored in - the instance because it is used for synchronous worker event - execution. This allows a sample load event to be executed with - sample accuracy when running in a non-realtime context (such as - exporting a session). */ - self->frame_offset = ev->time.frames; - - // Process this event - handle_event(self, ev); - } - - // Use available space after any emitted events to send peaks - peaks_sender_send( - &self->psend, &self->forge, sample_count, self->frame_offset); - - // Render output for the rest of the cycle past the last event - render(self, self->frame_offset, sample_count); -} - -static LV2_State_Status -save(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - Sampler* self = (Sampler*)instance; - if (!self->sample) { - return LV2_STATE_SUCCESS; - } - - LV2_State_Map_Path* map_path = - (LV2_State_Map_Path*)lv2_features_data(features, LV2_STATE__mapPath); - if (!map_path) { - return LV2_STATE_ERR_NO_FEATURE; - } - - // Map absolute sample path to an abstract state path - char* apath = map_path->abstract_path(map_path->handle, self->sample->path); - - // Store eg:sample = abstract path - store(handle, - self->uris.eg_sample, - apath, - strlen(apath) + 1, - self->uris.atom_Path, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - free(apath); - - // Store the gain value - store(handle, - self->uris.param_gain, - &self->gain_dB, - sizeof(self->gain_dB), - self->uris.atom_Float, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - return LV2_STATE_SUCCESS; -} - -static LV2_State_Status -restore(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - Sampler* self = (Sampler*)instance; - - // Get host features - LV2_Worker_Schedule* schedule = NULL; - LV2_State_Map_Path* paths = NULL; - - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_STATE__mapPath, &paths, true, - LV2_WORKER__schedule, &schedule, false, - NULL); - // clang-format on - - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - return LV2_STATE_ERR_NO_FEATURE; - } - - // Get eg:sample from state - size_t size = 0; - uint32_t type = 0; - uint32_t valflags = 0; - const void* value = - retrieve(handle, self->uris.eg_sample, &size, &type, &valflags); - - if (!value) { - lv2_log_error(&self->logger, "Missing eg:sample\n"); - return LV2_STATE_ERR_NO_PROPERTY; - } - - if (type != self->uris.atom_Path) { - lv2_log_error(&self->logger, "Non-path eg:sample\n"); - return LV2_STATE_ERR_BAD_TYPE; - } - - // Map abstract state path to absolute path - const char* apath = (const char*)value; - char* path = paths->absolute_path(paths->handle, apath); - - // Replace current sample with the new one - if (!self->activated || !schedule) { - // No scheduling available, load sample immediately - lv2_log_trace(&self->logger, "Synchronous restore\n"); - Sample* sample = load_sample(&self->logger, path, self->sample_rate); - if (sample) { - free_sample(self, self->sample); - self->sample = sample; - self->sample_changed = true; - } - } else { - // Schedule sample to be loaded by the provided worker - lv2_log_trace(&self->logger, "Scheduling restore\n"); - LV2_Atom_Forge forge; - LV2_Atom* buf = (LV2_Atom*)calloc(1, strlen(path) + 128); - lv2_atom_forge_init(&forge, self->map); - lv2_atom_forge_set_sink(&forge, atom_sink, atom_sink_deref, buf); - write_set_file(&forge, &self->uris, path, strlen(path)); - - const uint32_t msg_size = lv2_atom_pad_size(buf->size); - schedule->schedule_work(self->schedule->handle, msg_size, buf + 1); - free(buf); - } - - free(path); - - // Get param:gain from state - value = retrieve(handle, self->uris.param_gain, &size, &type, &valflags); - - if (!value) { - // Not an error, since older versions did not save this property - lv2_log_note(&self->logger, "Missing param:gain\n"); - return LV2_STATE_SUCCESS; - } - - if (type != self->uris.atom_Float) { - lv2_log_error(&self->logger, "Non-float param:gain\n"); - return LV2_STATE_ERR_BAD_TYPE; - } - - self->gain_dB = *(const float*)value; - self->gain = DB_CO(self->gain_dB); - self->gain_changed = true; - - return LV2_STATE_SUCCESS; -} - -static const void* -extension_data(const char* uri) -{ - static const LV2_State_Interface state = {save, restore}; - static const LV2_Worker_Interface worker = {work, work_response, NULL}; - - if (!strcmp(uri, LV2_STATE__interface)) { - return &state; - } - - if (!strcmp(uri, LV2_WORKER__interface)) { - return &worker; - } - - return NULL; -} - -static const LV2_Descriptor descriptor = {EG_SAMPLER_URI, - instantiate, - connect_port, - activate, - run, - deactivate, - cleanup, - extension_data}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* -lv2_descriptor(uint32_t index) -{ - return index == 0 ? &descriptor : NULL; -} diff --git a/plugins/eg-sampler.lv2/sampler.ttl b/plugins/eg-sampler.lv2/sampler.ttl deleted file mode 100644 index 4a3c24c..0000000 --- a/plugins/eg-sampler.lv2/sampler.ttl +++ /dev/null @@ -1,73 +0,0 @@ -@prefix atom: <http://lv2plug.in/ns/ext/atom#> . -@prefix doap: <http://usefulinc.com/ns/doap#> . -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix param: <http://lv2plug.in/ns/ext/parameters#> . -@prefix patch: <http://lv2plug.in/ns/ext/patch#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix state: <http://lv2plug.in/ns/ext/state#> . -@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . -@prefix urid: <http://lv2plug.in/ns/ext/urid#> . -@prefix work: <http://lv2plug.in/ns/ext/worker#> . -@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . - -<http://lv2plug.in/plugins/eg-sampler#sample> - a lv2:Parameter ; - rdfs:label "sample" ; - rdfs:range atom:Path . - -<http://lv2plug.in/plugins/eg-sampler> - a lv2:Plugin ; - doap:name "Exampler" ; - doap:license <http://opensource.org/licenses/isc> ; - lv2:project <http://lv2plug.in/ns/lv2> ; - lv2:requiredFeature state:loadDefaultState , - urid:map , - work:schedule ; - lv2:optionalFeature lv2:hardRTCapable , - state:threadSafeRestore ; - lv2:extensionData state:interface , - work:interface ; - ui:ui <http://lv2plug.in/plugins/eg-sampler#ui> ; - patch:writable <http://lv2plug.in/plugins/eg-sampler#sample> , - param:gain ; - lv2:port [ - a lv2:InputPort , - atom:AtomPort ; - atom:bufferType atom:Sequence ; - atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> , - patch:Message ; - lv2:designation lv2:control ; - lv2:index 0 ; - lv2:symbol "control" ; - lv2:name "Control" - ] , [ - a lv2:OutputPort , - atom:AtomPort ; - atom:bufferType atom:Sequence ; - atom:supports patch:Message ; - lv2:designation lv2:control ; - lv2:index 1 ; - lv2:symbol "notify" ; - lv2:name "Notify" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 2 ; - lv2:symbol "out" ; - lv2:name "Out" - ] ; - state:state [ - <http://lv2plug.in/plugins/eg-sampler#sample> <click.wav> ; - param:gain "0.0"^^xsd:float - ] . - -<http://lv2plug.in/plugins/eg-sampler#ui> - a ui:GtkUI ; - lv2:requiredFeature urid:map ; - lv2:optionalFeature ui:requestValue ; - lv2:extensionData ui:showInterface ; - ui:portNotification [ - ui:plugin <http://lv2plug.in/plugins/eg-sampler> ; - lv2:symbol "notify" ; - ui:notifyType atom:Blank - ] . diff --git a/plugins/eg-sampler.lv2/sampler_ui.c b/plugins/eg-sampler.lv2/sampler_ui.c deleted file mode 100644 index 16a8c71..0000000 --- a/plugins/eg-sampler.lv2/sampler_ui.c +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright 2011-2016 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "peaks.h" -#include "uris.h" - -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/ui/ui.h" -#include "lv2/urid/urid.h" - -#include <cairo.h> -#include <gdk/gdk.h> -#include <glib-object.h> -#include <glib.h> -#include <gobject/gclosure.h> -#include <gtk/gtk.h> - -#include <assert.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#define SAMPLER_UI_URI "http://lv2plug.in/plugins/eg-sampler#ui" - -#define MIN_CANVAS_W 128 -#define MIN_CANVAS_H 80 - -typedef struct { - LV2_Atom_Forge forge; - LV2_URID_Map* map; - LV2UI_Request_Value* request_value; - LV2_Log_Logger logger; - SamplerURIs uris; - PeaksReceiver precv; - - LV2UI_Write_Function write; - LV2UI_Controller controller; - - GtkWidget* box; - GtkWidget* play_button; - GtkWidget* file_button; - GtkWidget* request_file_button; - GtkWidget* button_box; - GtkWidget* canvas; - - uint32_t width; - uint32_t requested_n_peaks; - char* filename; - - uint8_t forge_buf[1024]; - - // Optional show/hide interface - GtkWidget* window; - bool did_init; -} SamplerUI; - -static void -on_file_set(GtkFileChooserButton* widget, void* handle) -{ - SamplerUI* ui = (SamplerUI*)handle; - - // Get the filename from the file chooser - char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)); - - // Write a set message to the plugin to load new file - lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); - LV2_Atom* msg = (LV2_Atom*)write_set_file( - &ui->forge, &ui->uris, filename, strlen(filename)); - - assert(msg); - - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); - - g_free(filename); -} - -static void -on_request_file(GtkButton* widget, void* handle) -{ - SamplerUI* ui = (SamplerUI*)handle; - - ui->request_value->request( - ui->request_value->handle, ui->uris.eg_sample, 0, NULL); -} - -static void -on_play_clicked(GtkFileChooserButton* widget, void* handle) -{ - SamplerUI* ui = (SamplerUI*)handle; - struct { - LV2_Atom atom; - uint8_t msg[3]; - } note_on; - - note_on.atom.type = ui->uris.midi_Event; - note_on.atom.size = 3; - note_on.msg[0] = LV2_MIDI_MSG_NOTE_ON; - note_on.msg[1] = 60; - note_on.msg[2] = 60; - ui->write(ui->controller, - 0, - sizeof(LV2_Atom) + 3, - ui->uris.atom_eventTransfer, - ¬e_on); -} - -static void -request_peaks(SamplerUI* ui, uint32_t n_peaks) -{ - if (n_peaks == ui->requested_n_peaks) { - return; - } - - lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); - - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.patch_Get); - lv2_atom_forge_key(&ui->forge, ui->uris.patch_accept); - lv2_atom_forge_urid(&ui->forge, ui->precv.uris.peaks_PeakUpdate); - lv2_atom_forge_key(&ui->forge, ui->precv.uris.peaks_total); - lv2_atom_forge_int(&ui->forge, n_peaks); - lv2_atom_forge_pop(&ui->forge, &frame); - - LV2_Atom* msg = lv2_atom_forge_deref(&ui->forge, frame.ref); - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); - - ui->requested_n_peaks = n_peaks; -} - -/** Set Cairo color to a GDK color (to follow Gtk theme). */ -static void -cairo_set_source_gdk(cairo_t* cr, const GdkColor* color) -{ - cairo_set_source_rgb( - cr, color->red / 65535.0, color->green / 65535.0, color->blue / 65535.0); -} - -static gboolean -on_canvas_expose(GtkWidget* widget, GdkEventExpose* event, gpointer data) -{ - SamplerUI* ui = (SamplerUI*)data; - - GtkAllocation size; - gtk_widget_get_allocation(widget, &size); - - ui->width = size.width; - if (ui->width > 2 * ui->requested_n_peaks) { - request_peaks(ui, 2 * ui->requested_n_peaks); - } - - cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); - - cairo_set_line_width(cr, 1.0); - cairo_translate(cr, 0.5, 0.5); - - const double mid_y = size.height / 2.0; - - const float* const peaks = ui->precv.peaks; - const int32_t n_peaks = ui->precv.n_peaks; - if (peaks) { - // Draw waveform - const double scale = size.width / ((double)n_peaks - 1.0f); - - // Start at left origin - cairo_move_to(cr, 0, mid_y); - - // Draw line through top peaks - for (int i = 0; i < n_peaks; ++i) { - const float peak = peaks[i]; - cairo_line_to(cr, i * scale, mid_y + (peak / 2.0f) * size.height); - } - - // Continue through bottom peaks - for (int i = n_peaks - 1; i >= 0; --i) { - const float peak = peaks[i]; - cairo_line_to(cr, i * scale, mid_y - (peak / 2.0f) * size.height); - } - - // Close shape - cairo_line_to(cr, 0, mid_y); - - cairo_set_source_gdk(cr, widget->style->mid); - cairo_fill_preserve(cr); - - cairo_set_source_gdk(cr, widget->style->fg); - cairo_stroke(cr); - } - - cairo_destroy(cr); - return TRUE; -} - -static void -destroy_window(SamplerUI* ui) -{ - if (ui->window) { - gtk_container_remove(GTK_CONTAINER(ui->window), ui->box); - gtk_widget_destroy(ui->window); - ui->window = NULL; - } -} - -static gboolean -on_window_closed(GtkWidget* widget, GdkEvent* event, gpointer data) -{ - SamplerUI* ui = (SamplerUI*)data; - - // Remove widget so Gtk doesn't delete it when the window is closed - gtk_container_remove(GTK_CONTAINER(ui->window), ui->box); - ui->window = NULL; - - return FALSE; -} - -static LV2UI_Handle -instantiate(const LV2UI_Descriptor* descriptor, - const char* plugin_uri, - const char* bundle_path, - LV2UI_Write_Function write_function, - LV2UI_Controller controller, - LV2UI_Widget* widget, - const LV2_Feature* const* features) -{ - SamplerUI* ui = (SamplerUI*)calloc(1, sizeof(SamplerUI)); - if (!ui) { - return NULL; - } - - ui->write = write_function; - ui->controller = controller; - ui->width = MIN_CANVAS_W; - *widget = NULL; - ui->window = NULL; - ui->did_init = false; - - // Get host features - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &ui->logger.log, false, - LV2_URID__map, &ui->map, true, - LV2_UI__requestValue, &ui->request_value, false, - NULL); - // clang-format on - - lv2_log_logger_set_map(&ui->logger, ui->map); - if (missing) { - lv2_log_error(&ui->logger, "Missing feature <%s>\n", missing); - free(ui); - return NULL; - } - - // Map URIs and initialise forge - map_sampler_uris(ui->map, &ui->uris); - lv2_atom_forge_init(&ui->forge, ui->map); - peaks_receiver_init(&ui->precv, ui->map); - - // Construct Gtk UI - ui->box = gtk_vbox_new(FALSE, 4); - ui->play_button = gtk_button_new_with_label("▶"); - ui->canvas = gtk_drawing_area_new(); - ui->button_box = gtk_hbox_new(FALSE, 4); - ui->file_button = - gtk_file_chooser_button_new("Load Sample", GTK_FILE_CHOOSER_ACTION_OPEN); - ui->request_file_button = gtk_button_new_with_label("Request Sample"); - gtk_widget_set_size_request(ui->canvas, MIN_CANVAS_W, MIN_CANVAS_H); - gtk_container_set_border_width(GTK_CONTAINER(ui->box), 4); - gtk_box_pack_start(GTK_BOX(ui->box), ui->canvas, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(ui->box), ui->button_box, FALSE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(ui->button_box), ui->play_button, FALSE, FALSE, 0); - gtk_box_pack_start( - GTK_BOX(ui->button_box), ui->request_file_button, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(ui->button_box), ui->file_button, TRUE, TRUE, 0); - - g_signal_connect(ui->file_button, "file-set", G_CALLBACK(on_file_set), ui); - - g_signal_connect( - ui->request_file_button, "clicked", G_CALLBACK(on_request_file), ui); - - g_signal_connect(ui->play_button, "clicked", G_CALLBACK(on_play_clicked), ui); - - g_signal_connect( - G_OBJECT(ui->canvas), "expose_event", G_CALLBACK(on_canvas_expose), ui); - - // Request state (filename) from plugin - lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = - (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.patch_Get); - - assert(msg); - - lv2_atom_forge_pop(&ui->forge, &frame); - - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); - - *widget = ui->box; - - return ui; -} - -static void -cleanup(LV2UI_Handle handle) -{ - SamplerUI* ui = (SamplerUI*)handle; - - if (ui->window) { - destroy_window(ui); - } - - gtk_widget_destroy(ui->canvas); - gtk_widget_destroy(ui->play_button); - gtk_widget_destroy(ui->file_button); - gtk_widget_destroy(ui->request_file_button); - gtk_widget_destroy(ui->button_box); - gtk_widget_destroy(ui->box); - free(ui); -} - -static void -port_event(LV2UI_Handle handle, - uint32_t port_index, - uint32_t buffer_size, - uint32_t format, - const void* buffer) -{ - SamplerUI* ui = (SamplerUI*)handle; - if (format == ui->uris.atom_eventTransfer) { - const LV2_Atom* atom = (const LV2_Atom*)buffer; - if (lv2_atom_forge_is_object_type(&ui->forge, atom->type)) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom; - if (obj->body.otype == ui->uris.patch_Set) { - const char* path = read_set_file(&ui->uris, obj); - if (path && (!ui->filename || !!strcmp(path, ui->filename))) { - g_free(ui->filename); - ui->filename = g_strdup(path); - gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(ui->file_button), - path); - peaks_receiver_clear(&ui->precv); - ui->requested_n_peaks = 0; - request_peaks(ui, ui->width / 2 * 2); - } else if (!path) { - lv2_log_warning(&ui->logger, "Set message has no path\n"); - } - } else if (obj->body.otype == ui->precv.uris.peaks_PeakUpdate) { - if (!peaks_receiver_receive(&ui->precv, obj)) { - gtk_widget_queue_draw(ui->canvas); - } - } - } else { - lv2_log_error(&ui->logger, "Unknown message type\n"); - } - } else { - lv2_log_warning(&ui->logger, "Unknown port event format\n"); - } -} - -/* Optional non-embedded UI show interface. */ -static int -ui_show(LV2UI_Handle handle) -{ - SamplerUI* ui = (SamplerUI*)handle; - - if (ui->window) { - return 0; - } - - if (!ui->did_init) { - int argc = 0; - gtk_init_check(&argc, NULL); - g_object_ref(ui->box); - ui->did_init = true; - } - - ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_container_add(GTK_CONTAINER(ui->window), ui->box); - - g_signal_connect( - G_OBJECT(ui->window), "delete-event", G_CALLBACK(on_window_closed), handle); - - gtk_widget_show_all(ui->window); - gtk_window_present(GTK_WINDOW(ui->window)); - - return 0; -} - -/* Optional non-embedded UI hide interface. */ -static int -ui_hide(LV2UI_Handle handle) -{ - SamplerUI* ui = (SamplerUI*)handle; - - if (ui->window) { - destroy_window(ui); - } - - return 0; -} - -/* Idle interface for optional non-embedded UI. */ -static int -ui_idle(LV2UI_Handle handle) -{ - SamplerUI* ui = (SamplerUI*)handle; - if (ui->window) { - gtk_main_iteration_do(false); - } - return 0; -} - -static const void* -extension_data(const char* uri) -{ - static const LV2UI_Show_Interface show = {ui_show, ui_hide}; - static const LV2UI_Idle_Interface idle = {ui_idle}; - - if (!strcmp(uri, LV2_UI__showInterface)) { - return &show; - } - - if (!strcmp(uri, LV2_UI__idleInterface)) { - return &idle; - } - - return NULL; -} - -static const LV2UI_Descriptor descriptor = {SAMPLER_UI_URI, - instantiate, - cleanup, - port_event, - extension_data}; - -LV2_SYMBOL_EXPORT -const LV2UI_Descriptor* -lv2ui_descriptor(uint32_t index) -{ - return index == 0 ? &descriptor : NULL; -} diff --git a/plugins/eg-sampler.lv2/uris.h b/plugins/eg-sampler.lv2/uris.h deleted file mode 100644 index f934390..0000000 --- a/plugins/eg-sampler.lv2/uris.h +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2011-2016 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef SAMPLER_URIS_H -#define SAMPLER_URIS_H - -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/midi/midi.h" -#include "lv2/parameters/parameters.h" -#include "lv2/patch/patch.h" -#include "lv2/urid/urid.h" - -#include <stdint.h> -#include <stdio.h> - -#define EG_SAMPLER_URI "http://lv2plug.in/plugins/eg-sampler" -#define EG_SAMPLER__applySample EG_SAMPLER_URI "#applySample" -#define EG_SAMPLER__freeSample EG_SAMPLER_URI "#freeSample" -#define EG_SAMPLER__sample EG_SAMPLER_URI "#sample" - -typedef struct { - LV2_URID atom_Float; - LV2_URID atom_Path; - LV2_URID atom_Resource; - LV2_URID atom_Sequence; - LV2_URID atom_URID; - LV2_URID atom_eventTransfer; - LV2_URID eg_applySample; - LV2_URID eg_freeSample; - LV2_URID eg_sample; - LV2_URID midi_Event; - LV2_URID param_gain; - LV2_URID patch_Get; - LV2_URID patch_Set; - LV2_URID patch_accept; - LV2_URID patch_property; - LV2_URID patch_value; -} SamplerURIs; - -static inline void -map_sampler_uris(LV2_URID_Map* map, SamplerURIs* uris) -{ - uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); - uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); - uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); - uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); - uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); - uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->eg_applySample = map->map(map->handle, EG_SAMPLER__applySample); - uris->eg_freeSample = map->map(map->handle, EG_SAMPLER__freeSample); - uris->eg_sample = map->map(map->handle, EG_SAMPLER__sample); - uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); - uris->param_gain = map->map(map->handle, LV2_PARAMETERS__gain); - uris->patch_Get = map->map(map->handle, LV2_PATCH__Get); - uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); - uris->patch_accept = map->map(map->handle, LV2_PATCH__accept); - uris->patch_property = map->map(map->handle, LV2_PATCH__property); - uris->patch_value = map->map(map->handle, LV2_PATCH__value); -} - -/** - Write a message like the following to `forge`: - [source,turtle] - ---- - [] - a patch:Set ; - patch:property param:gain ; - patch:value 0.0f . - ---- -*/ -static inline LV2_Atom_Forge_Ref -write_set_gain(LV2_Atom_Forge* forge, const SamplerURIs* uris, const float gain) -{ - LV2_Atom_Forge_Frame frame; - LV2_Atom_Forge_Ref set = - lv2_atom_forge_object(forge, &frame, 0, uris->patch_Set); - - lv2_atom_forge_key(forge, uris->patch_property); - lv2_atom_forge_urid(forge, uris->param_gain); - lv2_atom_forge_key(forge, uris->patch_value); - lv2_atom_forge_float(forge, gain); - - lv2_atom_forge_pop(forge, &frame); - return set; -} - -/** - Write a message like the following to `forge`: - [source,turtle] - ---- - [] - a patch:Set ; - patch:property eg:sample ; - patch:value </home/me/foo.wav> . - ---- -*/ -static inline LV2_Atom_Forge_Ref -write_set_file(LV2_Atom_Forge* forge, - const SamplerURIs* uris, - const char* filename, - const uint32_t filename_len) -{ - LV2_Atom_Forge_Frame frame; - LV2_Atom_Forge_Ref set = - lv2_atom_forge_object(forge, &frame, 0, uris->patch_Set); - - lv2_atom_forge_key(forge, uris->patch_property); - lv2_atom_forge_urid(forge, uris->eg_sample); - lv2_atom_forge_key(forge, uris->patch_value); - lv2_atom_forge_path(forge, filename, filename_len); - - lv2_atom_forge_pop(forge, &frame); - return set; -} - -/** - Get the file path from `obj` which is a message like: - [source,turtle] - ---- - [] - a patch:Set ; - patch:property eg:sample ; - patch:value </home/me/foo.wav> . - ---- -*/ -static inline const char* -read_set_file(const SamplerURIs* uris, const LV2_Atom_Object* obj) -{ - if (obj->body.otype != uris->patch_Set) { - fprintf(stderr, "Ignoring unknown message type %u\n", obj->body.otype); - return NULL; - } - - /* Get property URI. */ - const LV2_Atom* property = NULL; - lv2_atom_object_get(obj, uris->patch_property, &property, 0); - if (!property) { - fprintf(stderr, "Malformed set message has no body.\n"); - return NULL; - } - - if (property->type != uris->atom_URID) { - fprintf(stderr, "Malformed set message has non-URID property.\n"); - return NULL; - } - - if (((const LV2_Atom_URID*)property)->body != uris->eg_sample) { - fprintf(stderr, "Set message for unknown property.\n"); - return NULL; - } - - /* Get value. */ - const LV2_Atom* value = NULL; - lv2_atom_object_get(obj, uris->patch_value, &value, 0); - if (!value) { - fprintf(stderr, "Malformed set message has no value.\n"); - return NULL; - } - - if (value->type != uris->atom_Path) { - fprintf(stderr, "Set message value is not a Path.\n"); - return NULL; - } - - return (const char*)LV2_ATOM_BODY_CONST(value); -} - -#endif /* SAMPLER_URIS_H */ diff --git a/plugins/eg-scope.lv2/README.txt b/plugins/eg-scope.lv2/README.txt deleted file mode 100644 index 122794c..0000000 --- a/plugins/eg-scope.lv2/README.txt +++ /dev/null @@ -1,32 +0,0 @@ -== Simple Oscilloscope == - -This plugin displays the waveform of an incoming audio signal using a simple -GTK+Cairo GUI. - -This plugin illustrates: - -- UI <==> Plugin communication via http://lv2plug.in/ns/ext/atom/[LV2 Atom] events -- Atom vector usage and resize-port extension -- Save/Restore UI state by communicating state to backend -- Saving simple key/value state via the http://lv2plug.in/ns/ext/state/[LV2 State] extension -- Cairo drawing and partial exposure - -This plugin intends to outline the basics for building visualization plugins -that rely on atom communication. The UI looks like an oscilloscope, but is not -a real oscilloscope implementation: - -- There is no display synchronisation, results will depend on LV2 host. -- It displays raw audio samples, which a proper scope must not do. -- The display itself just connects min/max line segments. -- No triggering or synchronization. -- No labels, no scale, no calibration, no markers, no numeric readout, etc. - -Addressing these issues is beyond the scope of this example. - -Please see http://lac.linuxaudio.org/2013/papers/36.pdf for scope design, -https://wiki.xiph.org/Videos/Digital_Show_and_Tell for background information, -and http://lists.lv2plug.in/pipermail/devel-lv2plug.in/2013-November/000545.html -for general LV2 related conceptual criticism regarding real-time visualizations. - -A proper oscilloscope based on this example can be found at -https://github.com/x42/sisco.lv2 diff --git a/plugins/eg-scope.lv2/examploscope.c b/plugins/eg-scope.lv2/examploscope.c deleted file mode 100644 index e3bb53b..0000000 --- a/plugins/eg-scope.lv2/examploscope.c +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright 2016 David Robillard <d@drobilla.net> -// Copyright 2013 Robin Gareus <robin@gareus.org> -// SPDX-License-Identifier: ISC - -#include "./uris.h" - -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/state/state.h" -#include "lv2/urid/urid.h" - -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -/** - ==== Private Plugin Instance Structure ==== - - In addition to the usual port buffers and features, this plugin stores the - state of the UI here, so it can be opened and closed without losing the - current settings. The UI state is communicated between the plugin and the - UI using atom messages via a sequence port, similarly to MIDI I/O. -*/ -typedef struct { - // Port buffers - float* input[2]; - float* output[2]; - const LV2_Atom_Sequence* control; - LV2_Atom_Sequence* notify; - - // Atom forge and URI mapping - LV2_URID_Map* map; - ScoLV2URIs uris; - LV2_Atom_Forge forge; - LV2_Atom_Forge_Frame frame; - - // Log feature and convenience API - LV2_Log_Logger logger; - - // Instantiation settings - uint32_t n_channels; - double rate; - - // UI state - bool ui_active; - bool send_settings_to_ui; - float ui_amp; - uint32_t ui_spp; -} EgScope; - -/** ==== Port Indices ==== */ -typedef enum { - SCO_CONTROL = 0, // Event input - SCO_NOTIFY = 1, // Event output - SCO_INPUT0 = 2, // Audio input 0 - SCO_OUTPUT0 = 3, // Audio output 0 - SCO_INPUT1 = 4, // Audio input 1 (stereo variant) - SCO_OUTPUT1 = 5, // Audio input 2 (stereo variant) -} PortIndex; - -/** ==== Instantiate Method ==== */ -static LV2_Handle -instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* bundle_path, - const LV2_Feature* const* features) -{ - (void)descriptor; // Unused variable - (void)bundle_path; // Unused variable - - // Allocate and initialise instance structure. - EgScope* self = (EgScope*)calloc(1, sizeof(EgScope)); - if (!self) { - return NULL; - } - - // Get host features - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - // Decide which variant to use depending on the plugin URI - if (!strcmp(descriptor->URI, SCO_URI "#Stereo")) { - self->n_channels = 2; - } else if (!strcmp(descriptor->URI, SCO_URI "#Mono")) { - self->n_channels = 1; - } else { - free(self); - return NULL; - } - - // Initialise local variables - self->ui_active = false; - self->send_settings_to_ui = false; - self->rate = rate; - - // Set default UI settings - self->ui_spp = 50; - self->ui_amp = 1.0f; - - // Map URIs and initialise forge/logger - map_sco_uris(self->map, &self->uris); - lv2_atom_forge_init(&self->forge, self->map); - - return (LV2_Handle)self; -} - -/** ==== Connect Port Method ==== */ -static void -connect_port(LV2_Handle handle, uint32_t port, void* data) -{ - EgScope* self = (EgScope*)handle; - - switch ((PortIndex)port) { - case SCO_CONTROL: - self->control = (const LV2_Atom_Sequence*)data; - break; - case SCO_NOTIFY: - self->notify = (LV2_Atom_Sequence*)data; - break; - case SCO_INPUT0: - self->input[0] = (float*)data; - break; - case SCO_OUTPUT0: - self->output[0] = (float*)data; - break; - case SCO_INPUT1: - self->input[1] = (float*)data; - break; - case SCO_OUTPUT1: - self->output[1] = (float*)data; - break; - } -} - -/** - ==== Utility Function: `tx_rawaudio` ==== - - This function forges a message for sending a vector of raw data. The object - is a http://lv2plug.in/ns/ext/atom#Blank[Blank] with a few properties, like: - [source,turtle] - -------- - [] - a sco:RawAudio ; - sco:channelID 0 ; - sco:audioData [ 0.0, 0.0, ... ] . - -------- - - where the value of the `sco:audioData` property, `[ 0.0, 0.0, ... ]`, is a - http://lv2plug.in/ns/ext/atom#Vector[Vector] of - http://lv2plug.in/ns/ext/atom#Float[Float]. -*/ -static void -tx_rawaudio(LV2_Atom_Forge* forge, - ScoLV2URIs* uris, - const int32_t channel, - const size_t n_samples, - const float* data) -{ - LV2_Atom_Forge_Frame frame; - - // Forge container object of type 'RawAudio' - lv2_atom_forge_frame_time(forge, 0); - lv2_atom_forge_object(forge, &frame, 0, uris->RawAudio); - - // Add integer 'channelID' property - lv2_atom_forge_key(forge, uris->channelID); - lv2_atom_forge_int(forge, channel); - - // Add vector of floats 'audioData' property - lv2_atom_forge_key(forge, uris->audioData); - lv2_atom_forge_vector( - forge, sizeof(float), uris->atom_Float, n_samples, data); - - // Close off object - lv2_atom_forge_pop(forge, &frame); -} - -/** ==== Run Method ==== */ -static void -run(LV2_Handle handle, uint32_t n_samples) -{ - EgScope* self = (EgScope*)handle; - - /* Ensure notify port buffer is large enough to hold all audio-samples and - configuration settings. A minimum size was requested in the .ttl file, - but check here just to be sure. - - TODO: Explain these magic numbers. - */ - const size_t size = (sizeof(float) * n_samples + 64) * self->n_channels; - const uint32_t space = self->notify->atom.size; - if (space < size + 128) { - /* Insufficient space, report error and do nothing. Note that a - real-time production plugin mustn't call log functions in run(), but - this can be useful for debugging and example purposes. - */ - lv2_log_error(&self->logger, "Buffer size is insufficient\n"); - return; - } - - // Prepare forge buffer and initialize atom-sequence - lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->notify, space); - lv2_atom_forge_sequence_head(&self->forge, &self->frame, 0); - - /* Send settings to UI - - The plugin can continue to run while the UI is closed and re-opened. - The state and settings of the UI are kept here and transmitted to the UI - every time it asks for them or if the user initializes a 'load preset'. - */ - if (self->send_settings_to_ui && self->ui_active) { - self->send_settings_to_ui = false; - // Forge container object of type 'ui_state' - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_frame_time(&self->forge, 0); - lv2_atom_forge_object(&self->forge, &frame, 0, self->uris.ui_State); - - // Add UI state as properties - lv2_atom_forge_key(&self->forge, self->uris.ui_spp); - lv2_atom_forge_int(&self->forge, (int32_t)self->ui_spp); - lv2_atom_forge_key(&self->forge, self->uris.ui_amp); - lv2_atom_forge_float(&self->forge, self->ui_amp); - lv2_atom_forge_key(&self->forge, self->uris.param_sampleRate); - lv2_atom_forge_float(&self->forge, (float)self->rate); - lv2_atom_forge_pop(&self->forge, &frame); - } - - // Process incoming events from GUI - if (self->control) { - const LV2_Atom_Event* ev = lv2_atom_sequence_begin(&(self->control)->body); - // For each incoming message... - while (!lv2_atom_sequence_is_end( - &self->control->body, self->control->atom.size, ev)) { - // If the event is an atom:Blank object - if (lv2_atom_forge_is_object_type(&self->forge, ev->body.type)) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == self->uris.ui_On) { - // If the object is a ui-on, the UI was activated - self->ui_active = true; - self->send_settings_to_ui = true; - } else if (obj->body.otype == self->uris.ui_Off) { - // If the object is a ui-off, the UI was closed - self->ui_active = false; - } else if (obj->body.otype == self->uris.ui_State) { - // If the object is a ui-state, it's the current UI settings - const LV2_Atom* spp = NULL; - const LV2_Atom* amp = NULL; - lv2_atom_object_get( - obj, self->uris.ui_spp, &spp, self->uris.ui_amp, &, 0); - if (spp) { - self->ui_spp = ((const LV2_Atom_Int*)spp)->body; - } - if (amp) { - self->ui_amp = ((const LV2_Atom_Float*)amp)->body; - } - } - } - ev = lv2_atom_sequence_next(ev); - } - } - - // Process audio data - for (uint32_t c = 0; c < self->n_channels; ++c) { - if (self->ui_active) { - // If UI is active, send raw audio data to UI - tx_rawaudio( - &self->forge, &self->uris, (int32_t)c, n_samples, self->input[c]); - } - // If not processing audio in-place, forward audio - if (self->input[c] != self->output[c]) { - memcpy(self->output[c], self->input[c], sizeof(float) * n_samples); - } - } - - // Close off sequence - lv2_atom_forge_pop(&self->forge, &self->frame); -} - -static void -cleanup(LV2_Handle handle) -{ - free(handle); -} - -/** - ==== State Methods ==== - - This plugin's state consists of two basic properties: one `int` and one - `float`. No files are used. Note these values are POD, but not portable, - since different machines may have a different integer endianness or floating - point format. However, since standard Atom types are used, a good host will - be able to save them portably as text anyway. -*/ -static LV2_State_Status -state_save(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - EgScope* self = (EgScope*)instance; - if (!self) { - return LV2_STATE_SUCCESS; - } - - store(handle, - self->uris.ui_spp, - (void*)&self->ui_spp, - sizeof(uint32_t), - self->uris.atom_Int, - LV2_STATE_IS_POD); - - store(handle, - self->uris.ui_amp, - (void*)&self->ui_amp, - sizeof(float), - self->uris.atom_Float, - LV2_STATE_IS_POD); - - return LV2_STATE_SUCCESS; -} - -static LV2_State_Status -state_restore(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - EgScope* self = (EgScope*)instance; - - size_t size = 0; - uint32_t type = 0; - uint32_t valflags = 0; - - const void* spp = - retrieve(handle, self->uris.ui_spp, &size, &type, &valflags); - if (spp && size == sizeof(uint32_t) && type == self->uris.atom_Int) { - self->ui_spp = *((const uint32_t*)spp); - self->send_settings_to_ui = true; - } - - const void* amp = - retrieve(handle, self->uris.ui_amp, &size, &type, &valflags); - if (amp && size == sizeof(float) && type == self->uris.atom_Float) { - self->ui_amp = *((const float*)amp); - self->send_settings_to_ui = true; - } - - return LV2_STATE_SUCCESS; -} - -static const void* -extension_data(const char* uri) -{ - static const LV2_State_Interface state = {state_save, state_restore}; - if (!strcmp(uri, LV2_STATE__interface)) { - return &state; - } - return NULL; -} - -/** ==== Plugin Descriptors ==== */ -static const LV2_Descriptor descriptor_mono = {SCO_URI "#Mono", - instantiate, - connect_port, - NULL, - run, - NULL, - cleanup, - extension_data}; - -static const LV2_Descriptor descriptor_stereo = {SCO_URI "#Stereo", - instantiate, - connect_port, - NULL, - run, - NULL, - cleanup, - extension_data}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* -lv2_descriptor(uint32_t index) -{ - switch (index) { - case 0: - return &descriptor_mono; - case 1: - return &descriptor_stereo; - default: - return NULL; - } -} diff --git a/plugins/eg-scope.lv2/examploscope.ttl.in b/plugins/eg-scope.lv2/examploscope.ttl.in deleted file mode 100644 index 0b76962..0000000 --- a/plugins/eg-scope.lv2/examploscope.ttl.in +++ /dev/null @@ -1,130 +0,0 @@ -@prefix atom: <http://lv2plug.in/ns/ext/atom#> . -@prefix bufsz: <http://lv2plug.in/ns/ext/buf-size#> . -@prefix doap: <http://usefulinc.com/ns/doap#> . -@prefix foaf: <http://xmlns.com/foaf/0.1/> . -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . -@prefix urid: <http://lv2plug.in/ns/ext/urid#> . -@prefix rsz: <http://lv2plug.in/ns/ext/resize-port#> . -@prefix state: <http://lv2plug.in/ns/ext/state#> . -@prefix egscope: <http://lv2plug.in/plugins/eg-scope#> . - -<http://gareus.org/rgareus#me> - a foaf:Person ; - foaf:name "Robin Gareus" ; - foaf:mbox <mailto:robin@gareus.org> ; - foaf:homepage <http://gareus.org/> . - -<http://lv2plug.in/plugins/eg-scope> - a doap:Project ; - doap:maintainer <http://gareus.org/rgareus#me> ; - doap:name "Example Scope" . - -egscope:Mono - a lv2:Plugin, lv2:AnalyserPlugin ; - doap:name "Example Scope (Mono)" ; - lv2:project <http://lv2plug.in/plugins/eg-scope> ; - doap:license <http://usefulinc.com/doap/licenses/gpl> ; - lv2:requiredFeature urid:map ; - lv2:optionalFeature lv2:hardRTCapable ; - lv2:extensionData state:interface ; - ui:ui egscope:ui ; - lv2:port [ - a atom:AtomPort , - lv2:InputPort ; - atom:bufferType atom:Sequence ; - lv2:designation lv2:control ; - lv2:index 0 ; - lv2:symbol "control" ; - lv2:name "Control" - ] , [ - a atom:AtomPort , - lv2:OutputPort ; - atom:bufferType atom:Sequence ; - lv2:designation lv2:control ; - lv2:index 1 ; - lv2:symbol "notify" ; - lv2:name "Notify" ; - # 8192 * sizeof(float) + LV2-Atoms - rsz:minimumSize 32832; - ] , [ - a lv2:AudioPort , - lv2:InputPort ; - lv2:index 2 ; - lv2:symbol "in" ; - lv2:name "In" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 3 ; - lv2:symbol "out" ; - lv2:name "Out" - ] . - - -egscope:Stereo - a lv2:Plugin, lv2:AnalyserPlugin ; - doap:name "Example Scope (Stereo)" ; - lv2:project <http://lv2plug.in/plugins/eg-scope> ; - doap:license <http://usefulinc.com/doap/licenses/gpl> ; - lv2:requiredFeature urid:map ; - lv2:optionalFeature lv2:hardRTCapable ; - lv2:extensionData state:interface ; - ui:ui egscope:ui ; - lv2:port [ - a atom:AtomPort , - lv2:InputPort ; - atom:bufferType atom:Sequence ; - lv2:designation lv2:control ; - lv2:index 0 ; - lv2:symbol "control" ; - lv2:name "Control" - ] , [ - a atom:AtomPort , - lv2:OutputPort ; - atom:bufferType atom:Sequence ; - lv2:designation lv2:control ; - lv2:index 1 ; - lv2:symbol "notify" ; - lv2:name "Notify" ; - rsz:minimumSize 65664; - ] , [ - a lv2:AudioPort , - lv2:InputPort ; - lv2:index 2 ; - lv2:symbol "in0" ; - lv2:name "InL" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 3 ; - lv2:symbol "out0" ; - lv2:name "OutL" - ] , [ - a lv2:AudioPort , - lv2:InputPort ; - lv2:index 4 ; - lv2:symbol "in1" ; - lv2:name "InR" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 5 ; - lv2:symbol "out1" ; - lv2:name "OutR" - ] . - - -egscope:ui - a ui:GtkUI ; - lv2:requiredFeature urid:map ; - ui:portNotification [ - ui:plugin egscope:Mono ; - lv2:symbol "notify" ; - ui:notifyType atom:Blank - ] , [ - ui:plugin egscope:Stereo ; - lv2:symbol "notify" ; - ui:notifyType atom:Blank - ] . diff --git a/plugins/eg-scope.lv2/examploscope_ui.c b/plugins/eg-scope.lv2/examploscope_ui.c deleted file mode 100644 index 1c82d44..0000000 --- a/plugins/eg-scope.lv2/examploscope_ui.c +++ /dev/null @@ -1,663 +0,0 @@ -// Copyright 2013 Robin Gareus <robin@gareus.org> -// SPDX-License-Identifier: ISC - -#include "./uris.h" - -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/ui/ui.h" -#include "lv2/urid/urid.h" - -#include <cairo.h> -#include <gdk/gdk.h> -#include <glib-object.h> -#include <glib.h> -#include <gobject/gclosure.h> -#include <gtk/gtk.h> - -#include <assert.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -// Drawing area size -#define DAWIDTH (640) -#define DAHEIGHT (200) - -/** - Max continuous points on path. Many short-path segments are - expensive|inefficient long paths are not supported by all surfaces (usually - its a miter - not point - limit, depending on used cairo backend) -*/ -#define MAX_CAIRO_PATH (128) - -/** - Representation of the raw audio-data for display (min | max) values for a - given 'index' position. -*/ -typedef struct { - float data_min[DAWIDTH]; - float data_max[DAWIDTH]; - - uint32_t idx; - uint32_t sub; -} ScoChan; - -typedef struct { - LV2_Atom_Forge forge; - LV2_URID_Map* map; - ScoLV2URIs uris; - - LV2UI_Write_Function write; - LV2UI_Controller controller; - - GtkWidget* hbox; - GtkWidget* vbox; - GtkWidget* sep[2]; - GtkWidget* darea; - GtkWidget* btn_pause; - GtkWidget* lbl_speed; - GtkWidget* lbl_amp; - GtkWidget* spb_speed; - GtkWidget* spb_amp; - GtkAdjustment* spb_speed_adj; - GtkAdjustment* spb_amp_adj; - - ScoChan chn[2]; - uint32_t stride; - uint32_t n_channels; - bool paused; - float rate; - bool updating; -} EgScopeUI; - -/** Send current UI settings to backend. */ -static void -send_ui_state(LV2UI_Handle handle) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp)); - - // Use local buffer on the stack to build atom - uint8_t obj_buf[1024]; - lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - - // Start a ui:State object - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = - (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.ui_State); - - assert(msg); - - // msg[samples-per-pixel] = integer - lv2_atom_forge_key(&ui->forge, ui->uris.ui_spp); - lv2_atom_forge_int(&ui->forge, ui->stride); - - // msg[amplitude] = float - lv2_atom_forge_key(&ui->forge, ui->uris.ui_amp); - lv2_atom_forge_float(&ui->forge, gain); - - // Finish ui:State object - lv2_atom_forge_pop(&ui->forge, &frame); - - // Send message to plugin port '0' - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); -} - -/** Notify backend that UI is closed. */ -static void -send_ui_disable(LV2UI_Handle handle) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - send_ui_state(handle); - - uint8_t obj_buf[64]; - lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = - (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.ui_Off); - - assert(msg); - - lv2_atom_forge_pop(&ui->forge, &frame); - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); -} - -/** - Notify backend that UI is active. - - The plugin should send state and enable data transmission. -*/ -static void -send_ui_enable(LV2UI_Handle handle) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - - uint8_t obj_buf[64]; - lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = - (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.ui_On); - - assert(msg); - - lv2_atom_forge_pop(&ui->forge, &frame); - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); -} - -/** Gtk widget callback. */ -static gboolean -on_cfg_changed(GtkWidget* widget, gpointer data) -{ - EgScopeUI* ui = (EgScopeUI*)data; - if (!ui->updating) { - // Only send UI state if the change is from user interaction - send_ui_state(data); - } - return TRUE; -} - -/** - Gdk drawing area draw callback. - - Called in Gtk's main thread and uses Cairo to draw the data. -*/ -static gboolean -on_expose_event(GtkWidget* widget, GdkEventExpose* ev, gpointer data) -{ - EgScopeUI* ui = (EgScopeUI*)data; - const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp)); - - // Get cairo type for the gtk window - cairo_t* cr = gdk_cairo_create(ui->darea->window); - - // Limit cairo-drawing to exposed area - cairo_rectangle(cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height); - cairo_clip(cr); - - // Clear background - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); - cairo_rectangle(cr, 0, 0, DAWIDTH, DAHEIGHT * ui->n_channels); - cairo_fill(cr); - - cairo_set_line_width(cr, 1.0); - - const uint32_t start = ev->area.x; - const uint32_t end = ev->area.x + ev->area.width; - - assert(start < DAWIDTH); - assert(end <= DAWIDTH); - assert(start < end); - - for (uint32_t c = 0; c < ui->n_channels; ++c) { - ScoChan* chn = &ui->chn[c]; - - /* Drawing area Y-position of given sample-value. - * Note: cairo-pixel at 0 spans -0.5 .. +0.5, hence (DAHEIGHT / 2.0 -0.5) - * also the cairo Y-axis points upwards (hence 'minus value') - * - * == ( DAHEIGHT * (CHN) // channel offset - * + (DAHEIGHT / 2) - 0.5 // vertical center -- '0' - * - (DAHEIGHT / 2) * (VAL) * (GAIN) - * ) - */ - const float chn_y_offset = DAHEIGHT * c + DAHEIGHT * 0.5f - 0.5f; - const float chn_y_scale = DAHEIGHT * 0.5f * gain; - -#define CYPOS(VAL) (chn_y_offset - (VAL)*chn_y_scale) - - cairo_save(cr); - - /* Restrict drawing to current channel area, don't bleed drawing into - neighboring channels. */ - cairo_rectangle(cr, 0, DAHEIGHT * c, DAWIDTH, DAHEIGHT); - cairo_clip(cr); - - // Set color of wave-form - cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0); - - /* This is a somewhat 'smart' mechanism to plot audio data using - alternating up/down line-directions. It works well for both cases: - 1 pixel <= 1 sample and 1 pixel represents more than 1 sample, but - is not ideal for either. */ - if (start == chn->idx) { - cairo_move_to(cr, start - 0.5, CYPOS(0)); - } else { - cairo_move_to(cr, start - 0.5, CYPOS(chn->data_max[start])); - } - - uint32_t pathlength = 0; - for (uint32_t i = start; i < end; ++i) { - if (i == chn->idx) { - continue; - } - - if (i % 2) { - cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); - cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); - ++pathlength; - } else { - cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); - cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); - ++pathlength; - } - - /** Limit the max cairo path length. This is an optimization trade - off: too short path: high load CPU/GPU load. too-long path: - bad anti-aliasing, or possibly lost points */ - if (pathlength > MAX_CAIRO_PATH) { - pathlength = 0; - cairo_stroke(cr); - if (i % 2) { - cairo_move_to(cr, i - .5, CYPOS(chn->data_max[i])); - } else { - cairo_move_to(cr, i - .5, CYPOS(chn->data_min[i])); - } - } - } - - if (pathlength > 0) { - cairo_stroke(cr); - } - - // Draw current position vertical line if display is slow - if (ui->stride >= ui->rate / 4800.0f || ui->paused) { - cairo_set_source_rgba(cr, .9, .2, .2, .6); - cairo_move_to(cr, chn->idx - .5, DAHEIGHT * c); - cairo_line_to(cr, chn->idx - .5, DAHEIGHT * (c + 1)); - cairo_stroke(cr); - } - - // Undo the 'clipping' restriction - cairo_restore(cr); - - // Channel separator - if (c > 0) { - cairo_set_source_rgba(cr, .5, .5, .5, 1.0); - cairo_move_to(cr, 0, DAHEIGHT * c - .5); - cairo_line_to(cr, DAWIDTH, DAHEIGHT * c - .5); - cairo_stroke(cr); - } - - // Zero scale line - cairo_set_source_rgba(cr, .3, .3, .7, .5); - cairo_move_to(cr, 0, DAHEIGHT * (c + .5) - .5); - cairo_line_to(cr, DAWIDTH, DAHEIGHT * (c + .5) - .5); - cairo_stroke(cr); - } - - cairo_destroy(cr); - return TRUE; -} - -/** - Parse raw audio data and prepare for later drawing. - - Note this is a toy example, which is really a waveform display, not an - oscilloscope. A serious scope would not display samples as is. - - Signals above ~ 1/10 of the sampling-rate will not yield a useful visual - display and result in a rather unintuitive representation of the actual - waveform. - - Ideally the audio-data would be buffered and upsampled here and after that - written in a display buffer for later use. - - For more information, see - https://wiki.xiph.org/Videos/Digital_Show_and_Tell - http://lac.linuxaudio.org/2013/papers/36.pdf - and https://github.com/x42/sisco.lv2 -*/ -static int -process_channel(EgScopeUI* ui, - ScoChan* chn, - const size_t n_elem, - float const* data, - uint32_t* idx_start, - uint32_t* idx_end) -{ - int overflow = 0; - *idx_start = chn->idx; - for (size_t i = 0; i < n_elem; ++i) { - if (data[i] < chn->data_min[chn->idx]) { - chn->data_min[chn->idx] = data[i]; - } - if (data[i] > chn->data_max[chn->idx]) { - chn->data_max[chn->idx] = data[i]; - } - if (++chn->sub >= ui->stride) { - chn->sub = 0; - chn->idx = (chn->idx + 1) % DAWIDTH; - if (chn->idx == 0) { - ++overflow; - } - chn->data_min[chn->idx] = 1.0f; - chn->data_max[chn->idx] = -1.0f; - } - } - *idx_end = chn->idx; - return overflow; -} - -/** - Called via port_event() which is called by the host, typically at a rate of - around 25 FPS. -*/ -static void -update_scope(EgScopeUI* ui, - const int32_t channel, - const size_t n_elem, - float const* data) -{ - // Never trust input data which could lead to application failure. - if (channel < 0 || (uint32_t)channel > ui->n_channels) { - return; - } - - // Update state in sync with 1st channel - if (channel == 0) { - ui->stride = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_speed)); - const bool paused = - gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ui->btn_pause)); - - if (paused != ui->paused) { - ui->paused = paused; - gtk_widget_queue_draw(ui->darea); - } - } - if (ui->paused) { - return; - } - - uint32_t idx_start = 0; // Display pixel start - uint32_t idx_end = 0; // Display pixel end - int overflow = 0; // Received more audio-data than display-pixel - - // Process this channel's audio-data for display - ScoChan* chn = &ui->chn[channel]; - overflow = process_channel(ui, chn, n_elem, data, &idx_start, &idx_end); - - // Signal gtk's main thread to redraw the widget after the last channel - if ((uint32_t)channel + 1 == ui->n_channels) { - if (overflow > 1) { - // Redraw complete widget - gtk_widget_queue_draw(ui->darea); - } else if (idx_end > idx_start) { - // Redraw area between start -> end pixel - gtk_widget_queue_draw_area(ui->darea, - idx_start - 2, - 0, - 3 + idx_end - idx_start, - DAHEIGHT * ui->n_channels); - } else if (idx_end < idx_start) { - // Wrap-around: redraw area between 0->start AND end->right-end - gtk_widget_queue_draw_area(ui->darea, - idx_start - 2, - 0, - 3 + DAWIDTH - idx_start, - DAHEIGHT * ui->n_channels); - gtk_widget_queue_draw_area( - ui->darea, 0, 0, idx_end + 1, DAHEIGHT * ui->n_channels); - } - } -} - -static LV2UI_Handle -instantiate(const LV2UI_Descriptor* descriptor, - const char* plugin_uri, - const char* bundle_path, - LV2UI_Write_Function write_function, - LV2UI_Controller controller, - LV2UI_Widget* widget, - const LV2_Feature* const* features) -{ - EgScopeUI* ui = (EgScopeUI*)calloc(1, sizeof(EgScopeUI)); - - if (!ui) { - fprintf(stderr, "EgScope.lv2 UI: out of memory\n"); - return NULL; - } - - ui->map = NULL; - *widget = NULL; - - if (!strcmp(plugin_uri, SCO_URI "#Mono")) { - ui->n_channels = 1; - } else if (!strcmp(plugin_uri, SCO_URI "#Stereo")) { - ui->n_channels = 2; - } else { - free(ui); - return NULL; - } - - for (int i = 0; features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { - ui->map = (LV2_URID_Map*)features[i]->data; - } - } - - if (!ui->map) { - fprintf(stderr, "EgScope.lv2 UI: Host does not support urid:map\n"); - free(ui); - return NULL; - } - - // Initialize private data structure - ui->write = write_function; - ui->controller = controller; - - ui->vbox = NULL; - ui->hbox = NULL; - ui->darea = NULL; - ui->stride = 25; - ui->paused = false; - ui->rate = 48000; - - ui->chn[0].idx = 0; - ui->chn[0].sub = 0; - ui->chn[1].idx = 0; - ui->chn[1].sub = 0; - memset(ui->chn[0].data_min, 0, sizeof(float) * DAWIDTH); - memset(ui->chn[0].data_max, 0, sizeof(float) * DAWIDTH); - memset(ui->chn[1].data_min, 0, sizeof(float) * DAWIDTH); - memset(ui->chn[1].data_max, 0, sizeof(float) * DAWIDTH); - - map_sco_uris(ui->map, &ui->uris); - lv2_atom_forge_init(&ui->forge, ui->map); - - // Setup UI - ui->hbox = gtk_hbox_new(FALSE, 0); - ui->vbox = gtk_vbox_new(FALSE, 0); - - ui->darea = gtk_drawing_area_new(); - gtk_widget_set_size_request(ui->darea, DAWIDTH, DAHEIGHT * ui->n_channels); - - ui->lbl_speed = gtk_label_new("Samples/Pixel"); - ui->lbl_amp = gtk_label_new("Amplitude"); - - ui->sep[0] = gtk_hseparator_new(); - ui->sep[1] = gtk_label_new(""); - ui->btn_pause = gtk_toggle_button_new_with_label("Pause"); - - ui->spb_speed_adj = - (GtkAdjustment*)gtk_adjustment_new(25.0, 1.0, 1000.0, 1.0, 5.0, 0.0); - ui->spb_speed = gtk_spin_button_new(ui->spb_speed_adj, 1.0, 0); - - ui->spb_amp_adj = - (GtkAdjustment*)gtk_adjustment_new(1.0, 0.1, 6.0, 0.1, 1.0, 0.0); - ui->spb_amp = gtk_spin_button_new(ui->spb_amp_adj, 0.1, 1); - - gtk_box_pack_start(GTK_BOX(ui->hbox), ui->darea, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(ui->hbox), ui->vbox, FALSE, FALSE, 4); - - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_speed, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->spb_speed, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->sep[0], FALSE, FALSE, 8); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_amp, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->spb_amp, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->sep[1], TRUE, FALSE, 8); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_pause, FALSE, FALSE, 2); - - g_signal_connect( - G_OBJECT(ui->darea), "expose_event", G_CALLBACK(on_expose_event), ui); - g_signal_connect( - G_OBJECT(ui->spb_amp), "value-changed", G_CALLBACK(on_cfg_changed), ui); - g_signal_connect( - G_OBJECT(ui->spb_speed), "value-changed", G_CALLBACK(on_cfg_changed), ui); - - *widget = ui->hbox; - - /* Send UIOn message to plugin, which will request state and enable message - transmission. */ - send_ui_enable(ui); - - return ui; -} - -static void -cleanup(LV2UI_Handle handle) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - /* Send UIOff message to plugin, which will save state and disable message - * transmission. */ - send_ui_disable(ui); - gtk_widget_destroy(ui->darea); - free(ui); -} - -static int -recv_raw_audio(EgScopeUI* ui, const LV2_Atom_Object* obj) -{ - const LV2_Atom* chan_val = NULL; - const LV2_Atom* data_val = NULL; - const int n_props = lv2_atom_object_get( - obj, ui->uris.channelID, &chan_val, ui->uris.audioData, &data_val, NULL); - - if (n_props != 2 || chan_val->type != ui->uris.atom_Int || - data_val->type != ui->uris.atom_Vector) { - // Object does not have the required properties with correct types - fprintf(stderr, "eg-scope.lv2 UI error: Corrupt audio message\n"); - return 1; - } - - // Get the values we need from the body of the property value atoms - const int32_t chn = ((const LV2_Atom_Int*)chan_val)->body; - const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*)data_val; - if (vec->body.child_type != ui->uris.atom_Float) { - return 1; // Vector has incorrect element type - } - - // Number of elements = (total size - header size) / element size - const size_t n_elem = - ((data_val->size - sizeof(LV2_Atom_Vector_Body)) / sizeof(float)); - - // Float elements immediately follow the vector body header - const float* data = (const float*)(&vec->body + 1); - - // Update display - update_scope(ui, chn, n_elem, data); - return 0; -} - -static int -recv_ui_state(EgScopeUI* ui, const LV2_Atom_Object* obj) -{ - const LV2_Atom* spp_val = NULL; - const LV2_Atom* amp_val = NULL; - const LV2_Atom* rate_val = NULL; - - const int n_props = lv2_atom_object_get(obj, - ui->uris.ui_spp, - &spp_val, - ui->uris.ui_amp, - &_val, - ui->uris.param_sampleRate, - &rate_val, - NULL); - - if (n_props != 3 || spp_val->type != ui->uris.atom_Int || - amp_val->type != ui->uris.atom_Float || - rate_val->type != ui->uris.atom_Float) { - // Object does not have the required properties with correct types - fprintf(stderr, "eg-scope.lv2 UI error: Corrupt state message\n"); - return 1; - } - - // Get the values we need from the body of the property value atoms - const int32_t spp = ((const LV2_Atom_Int*)spp_val)->body; - const float amp = ((const LV2_Atom_Float*)amp_val)->body; - const float rate = ((const LV2_Atom_Float*)rate_val)->body; - - // Disable transmission and update UI - ui->updating = true; - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ui->spb_speed), spp); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ui->spb_amp), amp); - ui->updating = false; - ui->rate = rate; - - return 0; -} - -/** - Receive data from the DSP-backend. - - This is called by the host, typically at a rate of around 25 FPS. - - Ideally this happens regularly and with relatively low latency, but there - are no hard guarantees about message delivery. -*/ -static void -port_event(LV2UI_Handle handle, - uint32_t port_index, - uint32_t buffer_size, - uint32_t format, - const void* buffer) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - const LV2_Atom* atom = (const LV2_Atom*)buffer; - - /* Check type of data received - * - format == 0: Control port event (float) - * - format > 0: Message (atom) - */ - if (format == ui->uris.atom_eventTransfer && - lv2_atom_forge_is_object_type(&ui->forge, atom->type)) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom; - if (obj->body.otype == ui->uris.RawAudio) { - recv_raw_audio(ui, obj); - } else if (obj->body.otype == ui->uris.ui_State) { - recv_ui_state(ui, obj); - } - } -} - -static const LV2UI_Descriptor descriptor = {SCO_URI "#ui", - instantiate, - cleanup, - port_event, - NULL}; - -LV2_SYMBOL_EXPORT -const LV2UI_Descriptor* -lv2ui_descriptor(uint32_t index) -{ - return index == 0 ? &descriptor : NULL; -} diff --git a/plugins/eg-scope.lv2/manifest.ttl.in b/plugins/eg-scope.lv2/manifest.ttl.in deleted file mode 100644 index 66c3c9d..0000000 --- a/plugins/eg-scope.lv2/manifest.ttl.in +++ /dev/null @@ -1,21 +0,0 @@ -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . - -# ==== Mono plugin variant ==== -<http://lv2plug.in/plugins/eg-scope#Mono> - a lv2:Plugin ; - lv2:binary <examploscope@LIB_EXT@> ; - rdfs:seeAlso <examploscope.ttl> . - -# ==== Stereo plugin variant ==== -<http://lv2plug.in/plugins/eg-scope#Stereo> - a lv2:Plugin ; - lv2:binary <examploscope@LIB_EXT@> ; - rdfs:seeAlso <examploscope.ttl> . - -# ==== Gtk 2.0 UI ==== -<http://lv2plug.in/plugins/eg-scope#ui> - a ui:GtkUI ; - lv2:binary <examploscope_ui@LIB_EXT@> ; - rdfs:seeAlso <examploscope.ttl> . diff --git a/plugins/eg-scope.lv2/meson.build b/plugins/eg-scope.lv2/meson.build deleted file mode 100644 index 773a3d0..0000000 --- a/plugins/eg-scope.lv2/meson.build +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -plugin_sources = files('examploscope.c') -ui_sources = files('examploscope_ui.c') -bundle_name = 'eg-scope.lv2' -data_filenames = ['manifest.ttl.in', 'examploscope.ttl.in'] - -gtk2_dep = dependency('gtk+-2.0', - include_type: 'system', - required: get_option('plugins'), - version: '>= 2.18.0') - -module = shared_library( - 'examploscope', - plugin_sources, - c_args: c_suppressions, - dependencies: [lv2_dep, m_dep], - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: lv2dir / bundle_name, - name_prefix: '', -) - -config = configuration_data( - { - 'LIB_EXT': '.' + module.full_path().split('.')[-1], - } -) - -foreach filename : data_filenames - if filename.endswith('.in') - configure_file( - configuration: config, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename.substring(0, -3), - ) - else - configure_file( - copy: true, - input: files(filename), - install_dir: lv2dir / bundle_name, - output: filename, - ) - endif -endforeach - -if gtk2_dep.found() - shared_library( - 'examploscope_ui', - ui_sources, - c_args: c_suppressions, - dependencies: [lv2_dep, gtk2_dep], - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: lv2dir / bundle_name, - name_prefix: '', - ) -endif diff --git a/plugins/eg-scope.lv2/uris.h b/plugins/eg-scope.lv2/uris.h deleted file mode 100644 index e2becae..0000000 --- a/plugins/eg-scope.lv2/uris.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 Robin Gareus <robin@gareus.org> -// SPDX-License-Identifier: ISC - -#ifndef SCO_URIS_H -#define SCO_URIS_H - -#include "lv2/atom/atom.h" -#include "lv2/parameters/parameters.h" -#include "lv2/urid/urid.h" - -#define SCO_URI "http://lv2plug.in/plugins/eg-scope" - -typedef struct { - // URIs defined in LV2 specifications - LV2_URID atom_Vector; - LV2_URID atom_Float; - LV2_URID atom_Int; - LV2_URID atom_eventTransfer; - LV2_URID param_sampleRate; - - /* URIs defined for this plugin. It is best to re-use existing URIs as - much as possible, but plugins may need more vocabulary specific to their - needs. These are used as types and properties for plugin:UI - communication, as well as for saving state. */ - LV2_URID RawAudio; - LV2_URID channelID; - LV2_URID audioData; - LV2_URID ui_On; - LV2_URID ui_Off; - LV2_URID ui_State; - LV2_URID ui_spp; - LV2_URID ui_amp; -} ScoLV2URIs; - -static inline void -map_sco_uris(LV2_URID_Map* map, ScoLV2URIs* uris) -{ - uris->atom_Vector = map->map(map->handle, LV2_ATOM__Vector); - uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); - uris->atom_Int = map->map(map->handle, LV2_ATOM__Int); - uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->param_sampleRate = map->map(map->handle, LV2_PARAMETERS__sampleRate); - - /* Note the convention that URIs for types are capitalized, and URIs for - everything else (mainly properties) are not, just as in LV2 - specifications. */ - uris->RawAudio = map->map(map->handle, SCO_URI "#RawAudio"); - uris->audioData = map->map(map->handle, SCO_URI "#audioData"); - uris->channelID = map->map(map->handle, SCO_URI "#channelID"); - uris->ui_On = map->map(map->handle, SCO_URI "#UIOn"); - uris->ui_Off = map->map(map->handle, SCO_URI "#UIOff"); - uris->ui_State = map->map(map->handle, SCO_URI "#UIState"); - uris->ui_spp = map->map(map->handle, SCO_URI "#ui-spp"); - uris->ui_amp = map->map(map->handle, SCO_URI "#ui-amp"); -} - -#endif /* SCO_URIS_H */ diff --git a/plugins/literasc.py b/plugins/literasc.py deleted file mode 100755 index 74b13a7..0000000 --- a/plugins/literasc.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2012-2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: ISC - -""" -A simple literate programming tool for C, C++, and Turtle. - -Unlike many LP tools, this tool uses normal source code as input, there is no -tangle/weave and no special file format. The literate parts of the program are -written in comments, which are emitted as paragraphs of regular text -interleaved with code. Asciidoc is both the comment and output syntax. -""" - -import os -import re -import sys - - -def format_text(text): - "Format a text (comment) fragment and return it as a marked up string." - return "\n\n" + re.sub("\n *", "\n", text.strip()) + "\n\n" - - -def format_code(lang, code): - "Format a block of code and return it as a marked up string." - - if code.strip() == "": - return code - - head = f"[source,{lang}]" - code = code.strip("\n") - sep = "-" * len(head) - return "\n".join([head, sep, code, sep]) + "\n" - - -def format_c_source(filename, in_file): - "Format an annotated C source file as a marked up string." - - output = f"=== {os.path.basename(filename)} ===\n" - chunk = "" - prev_c = 0 - in_comment = False - in_comment_start = False - n_stars = 0 - code = "".join(in_file) - - # Skip initial license comment - if code[0:2] == "/*": - end = code.find("*/") + 2 - code = code[end:] - - def last_chunk(chunk): - length = len(chunk) - 1 - return chunk[0:length] - - for char in code: - if prev_c == "/" and char == "*": - in_comment_start = True - n_stars = 1 - elif in_comment_start: - if char == "*": - n_stars += 1 - else: - if n_stars > 1: - output += format_code("c", last_chunk(chunk)) - chunk = "" - in_comment = True - else: - chunk += "*" + char - in_comment_start = False - elif in_comment and prev_c == "*" and char == "/": - if n_stars > 1: - output += format_text(last_chunk(chunk)) - else: - output += format_code("c", "/* " + last_chunk(chunk) + "*/") - in_comment = False - in_comment_start = False - chunk = "" - else: - chunk += char - - prev_c = char - - return output + format_code("c", chunk) - - -def format_ttl_source(filename, in_file): - "Format an annotated Turtle source file as a marked up string." - - output = f"=== {os.path.basename(filename)} ===\n" - - in_comment = False - chunk = "" - for line in in_file: - is_comment = line.strip().startswith("#") - if in_comment: - if is_comment: - chunk += line.strip().lstrip("# ") + " \n" - else: - output += format_text(chunk) - in_comment = False - chunk = line - else: - if is_comment: - output += format_code("turtle", chunk) - in_comment = True - chunk = line.strip().lstrip("# ") + " \n" - else: - chunk += line - - if in_comment: - return output + format_text(chunk) - - return output + format_code("turtle", chunk) - - -def gen(out, filenames): - "Write markup generated from filenames to an output file." - - for filename in filenames: - with open(filename, "r", encoding="utf-8") as in_file: - if filename.endswith(".c") or filename.endswith(".h"): - out.write(format_c_source(filename, in_file)) - elif filename.endswith(".ttl") or filename.endswith(".ttl.in"): - out.write(format_ttl_source(filename, in_file)) - elif filename.endswith(".txt"): - for line in in_file: - out.write(line) - out.write("\n") - else: - sys.stderr.write( - f"Unknown source format `{filename.splitext()[1]}`\n" - ) - - -if __name__ == "__main__": - if len(sys.argv) < 2: - sys.stderr.write(f"Usage: {sys.argv[0]} OUT_FILE IN_FILE...\n") - sys.exit(1) - - with open(sys.argv[1], "w", encoding="utf-8") as out_file: - gen(out_file, sys.argv[2:]) diff --git a/plugins/meson.build b/plugins/meson.build deleted file mode 100644 index 098f585..0000000 --- a/plugins/meson.build +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -if not get_option('plugins').disabled() - m_dep = cc.find_library('m', required: false) - - subdir('eg-amp.lv2') - subdir('eg-fifths.lv2') - subdir('eg-metro.lv2') - subdir('eg-midigate.lv2') - subdir('eg-params.lv2') - subdir('eg-sampler.lv2') - subdir('eg-scope.lv2') -endif - -if not get_option('docs').disabled() - literasc_py = files('literasc.py') - asciidoc = find_program('asciidoc', required: get_option('docs')) - - if asciidoc.found() - book_inputs = files( - 'README.txt', - 'eg-amp.lv2/README.txt', - 'eg-amp.lv2/amp.c', - 'eg-amp.lv2/amp.ttl', - 'eg-fifths.lv2/README.txt', - 'eg-fifths.lv2/fifths.c', - 'eg-fifths.lv2/fifths.ttl', - 'eg-fifths.lv2/uris.h', - 'eg-metro.lv2/README.txt', - 'eg-metro.lv2/metro.c', - 'eg-metro.lv2/metro.ttl', - 'eg-midigate.lv2/README.txt', - 'eg-midigate.lv2/midigate.c', - 'eg-midigate.lv2/midigate.ttl', - 'eg-params.lv2/README.txt', - 'eg-params.lv2/params.c', - 'eg-params.lv2/params.ttl', - 'eg-params.lv2/state_map.h', - 'eg-sampler.lv2/README.txt', - 'eg-sampler.lv2/atom_sink.h', - 'eg-sampler.lv2/peaks.h', - 'eg-sampler.lv2/sampler.c', - 'eg-sampler.lv2/sampler.ttl', - 'eg-sampler.lv2/sampler_ui.c', - 'eg-sampler.lv2/uris.h', - 'eg-scope.lv2/README.txt', - 'eg-scope.lv2/examploscope.c', - 'eg-scope.lv2/examploscope_ui.c', - 'eg-scope.lv2/uris.h', - ) - - # Compile book sources into book.txt asciidoc source - book_txt = custom_target( - 'book.txt', - command: [ - literasc_py, - '@OUTPUT@', - '@INPUT@', - ], - input: book_inputs, - output: 'book.txt', - ) - - # Run asciidoc to generate book.html - book_html = custom_target( - 'book.html', - build_by_default: true, - command: [ - asciidoc, - '-a', 'stylesdir=' + lv2_source_root / 'doc' / 'style', - '-a', 'source-highlighter=pygments', - '-a', 'pygments-style=' + lv2_source_root / 'doc' / 'style' / 'style.css', - '-b', 'html', - '-o', '@OUTPUT@', - '@INPUT@', - ], - input: book_txt, - output: 'book.html', - ) - endif -endif diff --git a/schemas.lv2/dcterms.ttl b/schemas.lv2/dcterms.ttl index f6a5d06..d8fd196 100644 --- a/schemas.lv2/dcterms.ttl +++ b/schemas.lv2/dcterms.ttl @@ -337,4 +337,3 @@ dcterms:title rdfs:isDefinedBy dcterms: ; rdfs:label "title"@en-us ; rdfs:range rdfs:Literal . - diff --git a/schemas.lv2/doap.ttl b/schemas.lv2/doap.ttl index b806d31..160cda5 100644 --- a/schemas.lv2/doap.ttl +++ b/schemas.lv2/doap.ttl @@ -706,4 +706,3 @@ doap:wiki "wiki"@en , "wiki"@es , "wiki"@fr . - diff --git a/schemas.lv2/foaf.ttl b/schemas.lv2/foaf.ttl index 17dcc13..5aec41e 100644 --- a/schemas.lv2/foaf.ttl +++ b/schemas.lv2/foaf.ttl @@ -609,4 +609,3 @@ foaf:yahooChatID rdfs:label "Yahoo chat ID" ; rdfs:range rdfs:Literal ; rdfs:subPropertyOf foaf:nick . - diff --git a/schemas.lv2/manifest.ttl b/schemas.lv2/manifest.ttl index dd41f58..813d254 100644 --- a/schemas.lv2/manifest.ttl +++ b/schemas.lv2/manifest.ttl @@ -28,4 +28,3 @@ rdfs: <http://www.w3.org/2001/XMLSchema#> a owl:Ontology ; rdfs:seeAlso <xsd.ttl> . - diff --git a/schemas.lv2/meson.build b/schemas.lv2/meson.build index 8a0d36b..e8c8179 100644 --- a/schemas.lv2/meson.build +++ b/schemas.lv2/meson.build @@ -12,4 +12,6 @@ schema_data = files( 'xsd.ttl', ) -install_data(schema_data, install_dir: lv2dir / 'schemas.lv2') +if get_option('bundles') + install_data(schema_data, install_dir: lv2dir / 'schemas.lv2') +endif diff --git a/schemas.lv2/owl.ttl b/schemas.lv2/owl.ttl index 26bd0e8..e5f4fa2 100644 --- a/schemas.lv2/owl.ttl +++ b/schemas.lv2/owl.ttl @@ -618,4 +618,3 @@ owl:withRestrictions rdfs:isDefinedBy owl: ; rdfs:label "with restrictions" ; rdfs:range rdf:List . - diff --git a/schemas.lv2/rdf.ttl b/schemas.lv2/rdf.ttl index cb758cb..0fdeed0 100644 --- a/schemas.lv2/rdf.ttl +++ b/schemas.lv2/rdf.ttl @@ -126,4 +126,3 @@ rdf:value rdfs:isDefinedBy rdf: ; rdfs:label "value" ; rdfs:range rdfs:Resource . - diff --git a/schemas.lv2/rdfs.ttl b/schemas.lv2/rdfs.ttl index 10cfbb7..09ba907 100644 --- a/schemas.lv2/rdfs.ttl +++ b/schemas.lv2/rdfs.ttl @@ -121,4 +121,3 @@ rdfs:subPropertyOf rdfs:isDefinedBy rdfs: ; rdfs:label "sub-property of" ; rdfs:range rdf:Property . - diff --git a/schemas.lv2/xsd.ttl b/schemas.lv2/xsd.ttl index cb98363..38bae58 100644 --- a/schemas.lv2/xsd.ttl +++ b/schemas.lv2/xsd.ttl @@ -45,7 +45,8 @@ xsd:byte owl:withRestrictions ( [ xsd:maxInclusive "127"^^xsd:byte - ] [ + ] + [ xsd:minInclusive "-128"^^xsd:byte ] ) . @@ -99,7 +100,8 @@ xsd:duration owl:withRestrictions ( [ xsd:pattern "-?P([0-9]+Y)?([0-9]+M)?([0-9]+D)?(T([0-9]+H)?([0-9]+M)?([0-9]+(\\.[0-9]+)?S)?)?" - ] [ + ] + [ xsd:whiteSpace "collapse" ] ) . @@ -112,7 +114,8 @@ xsd:float owl:withRestrictions ( [ xsd:pattern "-?INF|NaN|[+-]?(([0-9]+[.]?[0-9]*)|([0-9]*[.]?[0-9]+))([eE][-+]?[0-9]+)?" - ] [ + ] + [ xsd:whiteSpace "collapse" ] ) . @@ -142,7 +145,8 @@ xsd:int owl:withRestrictions ( [ xsd:maxInclusive "2147483647"^^xsd:int - ] [ + ] + [ xsd:minInclusive "-2147483648"^^xsd:int ] ) . @@ -154,7 +158,8 @@ xsd:integer owl:withRestrictions ( [ xsd:pattern "[-+]?[0-9]+" - ] [ + ] + [ xsd:fractionDigits 0 ] ) . @@ -176,7 +181,8 @@ xsd:long owl:withRestrictions ( [ xsd:maxInclusive "9223372036854775807"^^xsd:long - ] [ + ] + [ xsd:minInclusive "-9223372036854775808"^^xsd:long ] ) . @@ -222,7 +228,8 @@ xsd:nonNegativeInteger owl:withRestrictions ( [ xsd:pattern "[+]?[0-9]+" - ] [ + ] + [ xsd:minInclusive 0 ] ) . @@ -234,7 +241,8 @@ xsd:nonPositiveInteger owl:withRestrictions ( [ xsd:pattern "(0|-[0-9]+)" - ] [ + ] + [ xsd:maxInclusive 0 ] ) . @@ -258,7 +266,8 @@ xsd:positiveInteger owl:withRestrictions ( [ xsd:pattern "[+]?[0-9]*[1-9]+[0-9]*" - ] [ + ] + [ xsd:minInclusive 1 ] ) . @@ -270,7 +279,8 @@ xsd:short owl:withRestrictions ( [ xsd:maxInclusive "32767"^^xsd:short - ] [ + ] + [ xsd:minInclusive "-32768"^^xsd:short ] ) . @@ -351,4 +361,3 @@ xsd:whiteSpace ] ) ] . - diff --git a/scripts/lv2_check_specification.py b/scripts/lv2_check_specification.py index 0cd296e..41611ef 100755 --- a/scripts/lv2_check_specification.py +++ b/scripts/lv2_check_specification.py @@ -150,8 +150,8 @@ def _check_specification(checker, spec_dir, is_stable=False): # Get all subjects that have an explicit rdf:type typed_subjects = set() - for typing in model.triples([None, rdf.type, None]): - typed_subjects.add(typing[0]) + for subject in model.subjects(rdf.type, None): + typed_subjects.add(subject) # Check that all named and typed resources have labels and comments for subject in typed_subjects: diff --git a/test/.clang-tidy b/test/.clang-tidy index ca868be..a622e62 100644 --- a/test/.clang-tidy +++ b/test/.clang-tidy @@ -1,26 +1,9 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> +# Copyright 2020-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC Checks: > - *, -*-else-after-return, - -*-magic-numbers, - -altera-*, - -bugprone-easily-swappable-parameters, - -bugprone-macro-parentheses, -bugprone-suspicious-include, - -llvm-header-guard, - -llvmlibc-implementation-in-namespace, - -llvmlibc-restrict-system-libc-headers, -modernize-use-trailing-return-type, - -performance-no-int-to-ptr, -readability-function-cognitive-complexity, - -readability-identifier-length, -WarningsAsErrors: '*' -HeaderFilterRegex: '.*' -FormatStyle: file -CheckOptions: - - key: hicpp-uppercase-literal-suffix.NewSuffixes - value: L;U;f - - key: readability-uppercase-literal-suffix.NewSuffixes - value: L;U;f +InheritParentConfig: true diff --git a/test/atom_test_utils.c b/test/atom_test_utils.c index e7d45d0..5c313a5 100644 --- a/test/atom_test_utils.c +++ b/test/atom_test_utils.c @@ -1,12 +1,15 @@ // Copyright 2012-2018 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/log/log.h" -#include "lv2/urid/urid.h" +#undef NDEBUG +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/atom/util.h> +#include <lv2/log/log.h> +#include <lv2/urid/urid.h> + +#include <assert.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -32,8 +35,11 @@ urid_map(LV2_URID_Map_Handle handle, const char* uri) } } - uris = (char**)realloc(uris, ++n_uris * sizeof(char*)); - uris[n_uris - 1] = copy_string(uri); + char** const new_uris = (char**)realloc(uris, (n_uris + 1) * sizeof(char*)); + assert(new_uris); + + uris = new_uris; + uris[n_uris++] = copy_string(uri); return n_uris; } diff --git a/test/cpp/.clang-tidy b/test/cpp/.clang-tidy new file mode 100644 index 0000000..b7981cc --- /dev/null +++ b/test/cpp/.clang-tidy @@ -0,0 +1,23 @@ +# Copyright 2020-2025 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +Checks: > + -*-no-malloc, + -*-use-auto, + -*-use-nullptr, + -bugprone-reserved-identifier, + -cert-dcl37-c, + -cert-dcl50-cpp, + -cert-dcl51-cpp, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-cstyle-cast, + -cppcoreguidelines-pro-type-vararg, + -hicpp-no-array-decay, + -hicpp-vararg, + -modernize-use-using, + -performance-enum-size, + -readability-implicit-bool-conversion, +InheritParentConfig: true diff --git a/test/cpp/test_build.cpp b/test/cpp/test_build.cpp new file mode 100644 index 0000000..73868a4 --- /dev/null +++ b/test/cpp/test_build.cpp @@ -0,0 +1,54 @@ +// Copyright 2022 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#if defined(__clang__) +_Pragma("clang diagnostic push") +_Pragma("clang diagnostic ignored \"-Wold-style-cast\"") +_Pragma("clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"") +#elif defined(__GNUC__) +_Pragma("GCC diagnostic push") +_Pragma("GCC diagnostic ignored \"-Wsuggest-attribute=const\"") +#endif + +#include <lv2/atom/atom.h> // IWYU pragma: keep +#include <lv2/atom/forge.h> // IWYU pragma: keep +#include <lv2/atom/util.h> // IWYU pragma: keep +#include <lv2/buf-size/buf-size.h> // IWYU pragma: keep +#include <lv2/core/attributes.h> // IWYU pragma: keep +#include <lv2/core/lv2.h> // IWYU pragma: keep +#include <lv2/core/lv2_util.h> // IWYU pragma: keep +#include <lv2/data-access/data-access.h> // IWYU pragma: keep +#include <lv2/dynmanifest/dynmanifest.h> // IWYU pragma: keep +#include <lv2/event/event-helpers.h> // IWYU pragma: keep +#include <lv2/event/event.h> // IWYU pragma: keep +#include <lv2/instance-access/instance-access.h> // IWYU pragma: keep +#include <lv2/log/log.h> // IWYU pragma: keep +#include <lv2/log/logger.h> // IWYU pragma: keep +#include <lv2/midi/midi.h> // IWYU pragma: keep +#include <lv2/morph/morph.h> // IWYU pragma: keep +#include <lv2/options/options.h> // IWYU pragma: keep +#include <lv2/parameters/parameters.h> // IWYU pragma: keep +#include <lv2/patch/patch.h> // IWYU pragma: keep +#include <lv2/port-groups/port-groups.h> // IWYU pragma: keep +#include <lv2/port-props/port-props.h> // IWYU pragma: keep +#include <lv2/presets/presets.h> // IWYU pragma: keep +#include <lv2/resize-port/resize-port.h> // IWYU pragma: keep +#include <lv2/state/state.h> // IWYU pragma: keep +#include <lv2/time/time.h> // IWYU pragma: keep +#include <lv2/ui/ui.h> // IWYU pragma: keep +#include <lv2/units/units.h> // IWYU pragma: keep +#include <lv2/uri-map/uri-map.h> // IWYU pragma: keep +#include <lv2/urid/urid.h> // IWYU pragma: keep +#include <lv2/worker/worker.h> // IWYU pragma: keep + +int +main() +{ + return 0; +} + +#if defined(__clang__) +_Pragma("clang diagnostic pop") +#elif defined(__GNUC__) +_Pragma("GCC diagnostic pop") +#endif diff --git a/plugins/.clang-tidy b/test/headers/.clang-tidy index bff0bfd..60c4447 100644 --- a/plugins/.clang-tidy +++ b/test/headers/.clang-tidy @@ -1,26 +1,20 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> +# Copyright 2020-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC Checks: > *, + -*-macro-to-enum, -*-magic-numbers, - -*-narrowing-conversions, -altera-*, -bugprone-easily-swappable-parameters, -bugprone-macro-parentheses, - -cert-err33-c, - -hicpp-signed-bitwise, - -llvm-header-guard, -llvmlibc-*, - -misc-unused-parameters, - -performance-no-int-to-ptr, - -readability-function-cognitive-complexity, -readability-identifier-length, -WarningsAsErrors: '*' -HeaderFilterRegex: '.*' -FormatStyle: file CheckOptions: - key: hicpp-uppercase-literal-suffix.NewSuffixes value: L;U;f - key: readability-uppercase-literal-suffix.NewSuffixes value: L;U;f +FormatStyle: file +HeaderFilterRegex: 'lv2/.*\.h' +WarningsAsErrors: '*' diff --git a/test/headers/meson.build b/test/headers/meson.build new file mode 100644 index 0000000..89ee4b8 --- /dev/null +++ b/test/headers/meson.build @@ -0,0 +1,69 @@ +# Copyright 2020-2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +header_c_suppressions = [] + +if get_option('warning_level') == 'everything' + if cc.get_id() == 'clang' + header_c_suppressions += [ + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-declaration-after-statement', + '-Wno-padded', + '-Wno-unsafe-buffer-usage', + ] + + if not meson.is_cross_build() + header_c_suppressions += [ + '-Wno-poison-system-directories', + ] + endif + + if host_machine.system() == 'windows' + header_c_suppressions += [ + '-Wno-format-nonliteral', + ] + endif + + elif cc.get_id() == 'gcc' + header_c_suppressions += [ + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-padded', + '-Wno-sign-conversion', + '-Wno-suggest-attribute=format', + '-Wno-unused-const-variable', + ] + + if host_machine.system() == 'windows' + header_c_suppressions += [ + '-Wno-sign-conversion', + ] + endif + + elif cc.get_id() == 'msvc' + header_c_suppressions += [ + '/wd4820', # padding added after construct + ] + endif +endif + +if cc.get_id() == 'clang' + header_c_suppressions += [ + '-Wno-nullability-extension', + ] +endif + +header_c_suppressions = cc.get_supported_arguments(header_c_suppressions) + +test( + 'headers', + executable( + 'test_headers', + files('test_headers.c'), + c_args: header_c_suppressions, + dependencies: [lv2_dep], + implicit_include_directories: false, + ), + suite: 'unit', +) diff --git a/test/headers/test_headers.c b/test/headers/test_headers.c new file mode 100644 index 0000000..d62b000 --- /dev/null +++ b/test/headers/test_headers.c @@ -0,0 +1,42 @@ +// Copyright 2022 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include <lv2/atom/atom.h> // IWYU pragma: keep +#include <lv2/atom/forge.h> // IWYU pragma: keep +#include <lv2/atom/util.h> // IWYU pragma: keep +#include <lv2/buf-size/buf-size.h> // IWYU pragma: keep +#include <lv2/core/attributes.h> // IWYU pragma: keep +#include <lv2/core/lv2.h> // IWYU pragma: keep +#include <lv2/core/lv2_util.h> // IWYU pragma: keep +#include <lv2/data-access/data-access.h> // IWYU pragma: keep +#include <lv2/dynmanifest/dynmanifest.h> // IWYU pragma: keep +#include <lv2/event/event-helpers.h> // IWYU pragma: keep +#include <lv2/event/event.h> // IWYU pragma: keep +#include <lv2/instance-access/instance-access.h> // IWYU pragma: keep +#include <lv2/log/log.h> // IWYU pragma: keep +#include <lv2/log/logger.h> // IWYU pragma: keep +#include <lv2/midi/midi.h> // IWYU pragma: keep +#include <lv2/morph/morph.h> // IWYU pragma: keep +#include <lv2/options/options.h> // IWYU pragma: keep +#include <lv2/parameters/parameters.h> // IWYU pragma: keep +#include <lv2/patch/patch.h> // IWYU pragma: keep +#include <lv2/port-groups/port-groups.h> // IWYU pragma: keep +#include <lv2/port-props/port-props.h> // IWYU pragma: keep +#include <lv2/presets/presets.h> // IWYU pragma: keep +#include <lv2/resize-port/resize-port.h> // IWYU pragma: keep +#include <lv2/state/state.h> // IWYU pragma: keep +#include <lv2/time/time.h> // IWYU pragma: keep +#include <lv2/ui/ui.h> // IWYU pragma: keep +#include <lv2/units/units.h> // IWYU pragma: keep +#include <lv2/uri-map/uri-map.h> // IWYU pragma: keep +#include <lv2/urid/urid.h> // IWYU pragma: keep +#include <lv2/worker/worker.h> // IWYU pragma: keep + +#ifdef __GNUC__ +__attribute__((const)) +#endif +int +main(void) +{ + return 0; +} diff --git a/test/meson.build b/test/meson.build index 092499a..7144349 100644 --- a/test/meson.build +++ b/test/meson.build @@ -5,51 +5,224 @@ # Data # ######## -# Check for spelling errors -codespell = find_program('codespell', required: get_option('tests')) -if codespell.found() - ignore = [ - lv2_source_root / 'doc' / 'style' / 'pygments.css', - lv2_source_root / 'lv2specgen' / 'DTD', - lv2_source_root / 'schemas.lv2' / 'doap.ttl', - ] +if get_option('lint') + # Check licensing metadata + reuse = find_program('reuse', required: false) + if reuse.found() + reuse_args = ['--root', lv2_source_root, 'lint'] + test('REUSE', reuse, args: reuse_args, suite: 'data') + endif - test( - 'codespell', - codespell, - args: [ - '-d', - '-q', '3', - '-S', ','.join(ignore), - lv2_source_root / 'doc', - lv2_source_root / 'lv2', - lv2_source_root / 'lv2specgen', - lv2_source_root / 'plugins', - lv2_source_root / 'schemas.lv2', - ], - suite: 'data', + # Check for spelling errors + codespell = find_program('codespell', required: get_option('tests')) + if codespell.found() + ignore = [ + lv2_source_root / 'doc' / 'style' / 'pygments.css', + lv2_source_root / 'lv2specgen' / 'DTD', + lv2_source_root / 'schemas.lv2' / 'doap.ttl', + ] + + test( + 'codespell', + codespell, + args: [ + '-d', + ['-q', '3'], + ['-S', ','.join(ignore)], + lv2_source_root / 'doc', + lv2_source_root / 'lv2', + lv2_source_root / 'lv2specgen', + lv2_source_root / 'schemas.lv2', + ], + suite: 'data', + ) + endif + + # Check that specification data is strictly formatted + serdi = find_program( + 'serdi', + required: get_option('tests'), + version: '>= 0.32.0', ) + native_build = ( + not meson.is_cross_build() + and host_machine.system() != 'windows' + ) + if serdi.found() and native_build + lv2_check_syntax = files( + lv2_source_root / 'scripts' / 'lv2_check_syntax.py', + ) + + test( + 'syntax', + lv2_check_syntax, + args: ['--serdi', serdi.full_path()] + spec_files + schema_data, + suite: 'data', + ) + endif + + # Check that specification data validates + sord_validate = find_program('sord_validate', required: get_option('tests')) + if sord_validate.found() + test('valid', sord_validate, args: spec_files + schema_data, suite: 'data') + endif endif -# Check that specification data is strictly formatted -serdi = find_program('serdi', required: get_option('tests')) -native_build = not meson.is_cross_build() and host_machine.system() != 'windows' -if serdi.found() and native_build - lv2_check_syntax = files(lv2_source_root / 'scripts' / 'lv2_check_syntax.py') +############################# +# Compilers and Build Tools # +############################# + +cc = meson.get_compiler('c') + +if add_languages(['cpp'], native: false, required: get_option('tests')) + cpp = meson.get_compiler('cpp') +endif + +######################## +# Warning Suppressions # +######################## + +warning_level = get_option('warning_level') + +# C +test_c_suppressions = [] +if cc.get_id() in ['clang', 'emscripten'] + if warning_level == 'everything' + test_c_suppressions += [ + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-declaration-after-statement', + '-Wno-documentation-unknown-command', + '-Wno-double-promotion', + '-Wno-float-equal', + '-Wno-padded', + '-Wno-reserved-id-macro', + '-Wno-unsafe-buffer-usage', + ] + + if not meson.is_cross_build() + test_c_suppressions += ['-Wno-poison-system-directories'] + endif + + if host_machine.system() == 'windows' + test_c_suppressions += ['-Wno-format-nonliteral'] + endif + endif + + if warning_level in ['everything', '3', '2'] + test_c_suppressions += ['-Wno-unused-parameter'] + endif + +elif cc.get_id() == 'gcc' + if warning_level == 'everything' + test_c_suppressions += [ + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-double-promotion', + '-Wno-float-equal', + '-Wno-inline', + '-Wno-padded', + '-Wno-suggest-attribute=const', + '-Wno-suggest-attribute=malloc', + '-Wno-suggest-attribute=pure', + '-Wno-unsuffixed-float-constants', + '-Wno-unused-const-variable', + ] + + if target_machine.system() == 'windows' + test_c_suppressions += [ + '-Wno-sign-conversion', + '-Wno-suggest-attribute=format', + ] + endif + endif + + if warning_level in ['everything', '3', '2'] + test_c_suppressions += ['-Wno-unused-parameter'] + endif + +elif cc.get_id() == 'msvc' + if warning_level == 'everything' + test_c_suppressions += [ + '/wd4061', # enumerator in switch is not explicitly handled + '/wd4244', # conversion with possible loss of data + '/wd4365', # signed/unsigned mismatch + '/wd4514', # unreferenced inline function has been removed + '/wd4710', # function not inlined + '/wd4711', # function selected for automatic inline expansion + '/wd4820', # padding added after construct + '/wd5045', # will insert Spectre mitigation for memory load + ] + endif + + if warning_level in ['everything', '3'] + test_c_suppressions += [ + '/wd4100', # unreferenced formal parameter + ] + endif - test('syntax', - lv2_check_syntax, - args: ['--serdi', serdi.full_path()] + spec_files + schema_data, - suite: 'data') + if warning_level in ['everything', '3', '2'] + test_c_suppressions += [ + '/wd4267', # conversion from size_t to a smaller type + ] + endif endif -# Check that specification data validates -sord_validate = find_program('sord_validate', required: get_option('tests')) -if sord_validate.found() - test('valid', - sord_validate, - args: spec_files + schema_data, - suite: 'data') +test_c_suppressions = cc.get_supported_arguments(test_c_suppressions) + +# C++ +if is_variable('cpp') + test_cpp_suppressions = [] + + if warning_level == 'everything' + if cpp.get_id() in ['clang', 'emscripten'] + test_cpp_suppressions += [ + '-Wno-c++98-compat', + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-documentation-unknown-command', + '-Wno-nullability-extension', + '-Wno-padded', + '-Wno-reserved-id-macro', + '-Wno-unsafe-buffer-usage', + ] + + if not meson.is_cross_build() + test_cpp_suppressions += ['-Wno-poison-system-directories'] + endif + + if host_machine.system() == 'windows' + test_cpp_suppressions += ['-Wno-format-nonliteral'] + endif + + elif cpp.get_id() == 'gcc' + test_cpp_suppressions += [ + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-inline', + '-Wno-padded', + '-Wno-unused-const-variable', + '-Wno-useless-cast', + ] + + if target_machine.system() == 'windows' + test_cpp_suppressions += ['-Wno-suggest-attribute=format'] + endif + + elif cpp.get_id() == 'msvc' + test_cpp_suppressions += [ + '/wd4514', # unreferenced inline function has been removed + '/wd4706', # assignment within conditional expression + '/wd4710', # function not inlined + '/wd4711', # function selected for automatic inline expansion + '/wd4820', # padding added after data member + '/wd5045', # will insert Spectre mitigation + '/wd5264', # const variable is not used + ] + endif + endif + + test_cpp_suppressions = cpp.get_supported_arguments(test_cpp_suppressions) endif ######## @@ -57,61 +230,82 @@ endif ######## # Check that all the headers compile cleanly in C -test('c', - executable( - 'test_build_c', - files('test_build.c'), - c_args: c_suppressions, - dependencies: lv2_dep, - ), - suite: 'build') +test( + 'c', + executable( + 'test_build_c', + files('test_build.c'), + c_args: test_c_suppressions, + dependencies: [lv2_dep], + implicit_include_directories: false, + ), + suite: 'build', +) # Check that all the headers compile cleanly in C++ if is_variable('cpp') - test('cpp', - executable( - 'test_build_cpp', - files('test_build.cpp'), - cpp_args: cpp_suppressions, - dependencies: lv2_dep, - ), - suite: 'build') + test( + 'cpp', + executable( + 'test_build_cpp', + files('cpp/test_build.cpp'), + cpp_args: test_cpp_suppressions, + dependencies: [lv2_dep], + implicit_include_directories: false, + ), + suite: 'build', + ) endif ########## # Python # ########## -if get_option('strict') - flake8 = find_program('flake8', required: get_option('tests')) - pylint = find_program('pylint', required: get_option('tests')) - black = find_program('black', required: get_option('tests')) - - # Scripts that don't pass with pylint - lax_python_scripts = files( +if get_option('lint') + python_scripts = lv2_scripts + files( '../lv2specgen/lv2docgen.py', '../lv2specgen/lv2specgen.py', ) - # Scripts that pass with everything including pylint - strict_python_scripts = lv2_scripts + files('../plugins/literasc.py') - - all_python_scripts = lax_python_scripts + strict_python_scripts - - if is_variable('black') and black.found() + # Check script formatting + black = find_program('black', required: get_option('tests')) + if black.found() black_opts = ['-l', '79', '-q', '--check'] - test('black', black, args: black_opts + all_python_scripts, suite: 'scripts') + test( + 'black', + black, + args: black_opts + python_scripts, + suite: 'scripts', + ) endif - if is_variable('flake8') and flake8.found() - test('flake8', flake8, args: all_python_scripts, suite: 'scripts') + # Check scripts for errors with flake8 + flake8 = find_program('flake8', required: get_option('tests')) + if flake8.found() + test('flake8', flake8, args: python_scripts, suite: 'scripts') endif - if is_variable('pylint') and pylint.found() - test('pylint', pylint, args: strict_python_scripts, suite: 'scripts') + # Check scripts for errors with pylint + pylint = find_program('pylint', required: get_option('tests')) + if pylint.found() + lint_py = pymod.find_installation( + 'python3', + modules: ['pygments', 'rdflib'], + required: false, + ) + + if lint_py.found() + test('pylint', pylint, args: python_scripts, suite: 'scripts') + endif endif endif +################### +# Header Warnings # +################### + +subdir('headers') + ############## # Unit Tests # ############## @@ -121,18 +315,22 @@ test_names = [ 'forge_overflow', ] +atom_test_suppressions = [] +if cc.get_id() == 'gcc' + atom_test_suppressions += ['-Wno-stringop-overflow'] +endif + # Build and run tests -if not get_option('tests').disabled() - foreach test_name : test_names - test( +foreach test_name : test_names + test( + test_name, + executable( test_name, - executable( - test_name, - files('test_@0@.c'.format(test_name)), - c_args: c_suppressions, - dependencies: lv2_dep, - ), - suite: 'unit', - ) - endforeach -endif + files('test_@0@.c'.format(test_name)), + c_args: test_c_suppressions + atom_test_suppressions, + dependencies: [lv2_dep], + implicit_include_directories: false, + ), + suite: 'unit', + ) +endforeach diff --git a/test/test_atom.c b/test/test_atom.c index beac05c..f88c4c7 100644 --- a/test/test_atom.c +++ b/test/test_atom.c @@ -3,10 +3,10 @@ #include "atom_test_utils.c" -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/urid/urid.h" +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/atom/util.h> +#include <lv2/urid/urid.h> #include <stdbool.h> #include <stdint.h> @@ -101,7 +101,7 @@ main(void) lv2_atom_forge_key(&forge, eg_path); LV2_Atom_String* path = (LV2_Atom_String*)lv2_atom_forge_deref( &forge, lv2_atom_forge_uri(&forge, pstr, pstr_len)); - char* pbody = (char*)LV2_ATOM_BODY(path); + char* pbody = (char*)&path[1]; if (!!strcmp(pbody, pstr)) { return test_fail("%s != \"%s\"\n", pbody, pstr); } @@ -112,7 +112,7 @@ main(void) lv2_atom_forge_key(&forge, eg_uri); LV2_Atom_String* uri = (LV2_Atom_String*)lv2_atom_forge_deref( &forge, lv2_atom_forge_uri(&forge, ustr, ustr_len)); - char* ubody = (char*)LV2_ATOM_BODY(uri); + char* ubody = (char*)&uri[1]; if (!!strcmp(ubody, ustr)) { return test_fail("%s != \"%s\"\n", ubody, ustr); } @@ -130,7 +130,7 @@ main(void) lv2_atom_forge_key(&forge, eg_string); LV2_Atom_String* string = (LV2_Atom_String*)lv2_atom_forge_deref( &forge, lv2_atom_forge_string(&forge, "hello", strlen("hello"))); - char* sbody = (char*)LV2_ATOM_BODY(string); + char* sbody = (char*)&string[1]; if (!!strcmp(sbody, "hello")) { return test_fail("%s != \"hello\"\n", sbody); } @@ -144,7 +144,7 @@ main(void) strlen("bonjour"), 0, urid_map(NULL, "http://lexvo.org/id/term/fr"))); - char* lbody = (char*)LV2_ATOM_CONTENTS(LV2_Atom_Literal, literal); + char* lbody = (char*)&literal[1]; if (!!strcmp(lbody, "bonjour")) { return test_fail("%s != \"bonjour\"\n", lbody); } @@ -186,7 +186,7 @@ main(void) LV2_Atom_Vector* vector = (LV2_Atom_Vector*)lv2_atom_forge_deref( &forge, lv2_atom_forge_vector(&forge, sizeof(int32_t), forge.Int, 4, elems)); - void* vec_body = LV2_ATOM_CONTENTS(LV2_Atom_Vector, vector); + const void* vec_body = LV2_ATOM_CONTENTS_CONST(LV2_Atom_Vector, vector); if (!!memcmp(elems, vec_body, sizeof(elems))) { return test_fail("Corrupt vector\n"); } diff --git a/test/test_build.c b/test/test_build.c index bdc47f6..83a69df 100644 --- a/test/test_build.c +++ b/test/test_build.c @@ -1,36 +1,36 @@ // Copyright 2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "lv2/atom/atom.h" // IWYU pragma: keep -#include "lv2/atom/forge.h" // IWYU pragma: keep -#include "lv2/atom/util.h" // IWYU pragma: keep -#include "lv2/buf-size/buf-size.h" // IWYU pragma: keep -#include "lv2/core/attributes.h" // IWYU pragma: keep -#include "lv2/core/lv2.h" // IWYU pragma: keep -#include "lv2/core/lv2_util.h" // IWYU pragma: keep -#include "lv2/data-access/data-access.h" // IWYU pragma: keep -#include "lv2/dynmanifest/dynmanifest.h" // IWYU pragma: keep -#include "lv2/event/event-helpers.h" // IWYU pragma: keep -#include "lv2/event/event.h" // IWYU pragma: keep -#include "lv2/instance-access/instance-access.h" // IWYU pragma: keep -#include "lv2/log/log.h" // IWYU pragma: keep -#include "lv2/log/logger.h" // IWYU pragma: keep -#include "lv2/midi/midi.h" // IWYU pragma: keep -#include "lv2/morph/morph.h" // IWYU pragma: keep -#include "lv2/options/options.h" // IWYU pragma: keep -#include "lv2/parameters/parameters.h" // IWYU pragma: keep -#include "lv2/patch/patch.h" // IWYU pragma: keep -#include "lv2/port-groups/port-groups.h" // IWYU pragma: keep -#include "lv2/port-props/port-props.h" // IWYU pragma: keep -#include "lv2/presets/presets.h" // IWYU pragma: keep -#include "lv2/resize-port/resize-port.h" // IWYU pragma: keep -#include "lv2/state/state.h" // IWYU pragma: keep -#include "lv2/time/time.h" // IWYU pragma: keep -#include "lv2/ui/ui.h" // IWYU pragma: keep -#include "lv2/units/units.h" // IWYU pragma: keep -#include "lv2/uri-map/uri-map.h" // IWYU pragma: keep -#include "lv2/urid/urid.h" // IWYU pragma: keep -#include "lv2/worker/worker.h" // IWYU pragma: keep +#include <lv2/atom/atom.h> // IWYU pragma: keep +#include <lv2/atom/forge.h> // IWYU pragma: keep +#include <lv2/atom/util.h> // IWYU pragma: keep +#include <lv2/buf-size/buf-size.h> // IWYU pragma: keep +#include <lv2/core/attributes.h> // IWYU pragma: keep +#include <lv2/core/lv2.h> // IWYU pragma: keep +#include <lv2/core/lv2_util.h> // IWYU pragma: keep +#include <lv2/data-access/data-access.h> // IWYU pragma: keep +#include <lv2/dynmanifest/dynmanifest.h> // IWYU pragma: keep +#include <lv2/event/event-helpers.h> // IWYU pragma: keep +#include <lv2/event/event.h> // IWYU pragma: keep +#include <lv2/instance-access/instance-access.h> // IWYU pragma: keep +#include <lv2/log/log.h> // IWYU pragma: keep +#include <lv2/log/logger.h> // IWYU pragma: keep +#include <lv2/midi/midi.h> // IWYU pragma: keep +#include <lv2/morph/morph.h> // IWYU pragma: keep +#include <lv2/options/options.h> // IWYU pragma: keep +#include <lv2/parameters/parameters.h> // IWYU pragma: keep +#include <lv2/patch/patch.h> // IWYU pragma: keep +#include <lv2/port-groups/port-groups.h> // IWYU pragma: keep +#include <lv2/port-props/port-props.h> // IWYU pragma: keep +#include <lv2/presets/presets.h> // IWYU pragma: keep +#include <lv2/resize-port/resize-port.h> // IWYU pragma: keep +#include <lv2/state/state.h> // IWYU pragma: keep +#include <lv2/time/time.h> // IWYU pragma: keep +#include <lv2/ui/ui.h> // IWYU pragma: keep +#include <lv2/units/units.h> // IWYU pragma: keep +#include <lv2/uri-map/uri-map.h> // IWYU pragma: keep +#include <lv2/urid/urid.h> // IWYU pragma: keep +#include <lv2/worker/worker.h> // IWYU pragma: keep int main(void) diff --git a/test/test_build.cpp b/test/test_build.cpp deleted file mode 100644 index c8b2ca0..0000000 --- a/test/test_build.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2022 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#if defined(__clang__) -_Pragma("clang diagnostic push") -_Pragma("clang diagnostic ignored \"-Wold-style-cast\"") -_Pragma("clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"") -#elif defined(__GNUC__) -_Pragma("GCC diagnostic push") -_Pragma("GCC diagnostic ignored \"-Wsuggest-attribute=const\"") -#endif - -#include "lv2/atom/atom.h" // IWYU pragma: keep -#include "lv2/atom/forge.h" // IWYU pragma: keep -#include "lv2/atom/util.h" // IWYU pragma: keep -#include "lv2/buf-size/buf-size.h" // IWYU pragma: keep -#include "lv2/core/attributes.h" // IWYU pragma: keep -#include "lv2/core/lv2.h" // IWYU pragma: keep -#include "lv2/core/lv2_util.h" // IWYU pragma: keep -#include "lv2/data-access/data-access.h" // IWYU pragma: keep -#include "lv2/dynmanifest/dynmanifest.h" // IWYU pragma: keep -#include "lv2/event/event-helpers.h" // IWYU pragma: keep -#include "lv2/event/event.h" // IWYU pragma: keep -#include "lv2/instance-access/instance-access.h" // IWYU pragma: keep -#include "lv2/log/log.h" // IWYU pragma: keep -#include "lv2/log/logger.h" // IWYU pragma: keep -#include "lv2/midi/midi.h" // IWYU pragma: keep -#include "lv2/morph/morph.h" // IWYU pragma: keep -#include "lv2/options/options.h" // IWYU pragma: keep -#include "lv2/parameters/parameters.h" // IWYU pragma: keep -#include "lv2/patch/patch.h" // IWYU pragma: keep -#include "lv2/port-groups/port-groups.h" // IWYU pragma: keep -#include "lv2/port-props/port-props.h" // IWYU pragma: keep -#include "lv2/presets/presets.h" // IWYU pragma: keep -#include "lv2/resize-port/resize-port.h" // IWYU pragma: keep -#include "lv2/state/state.h" // IWYU pragma: keep -#include "lv2/time/time.h" // IWYU pragma: keep -#include "lv2/ui/ui.h" // IWYU pragma: keep -#include "lv2/units/units.h" // IWYU pragma: keep -#include "lv2/uri-map/uri-map.h" // IWYU pragma: keep -#include "lv2/urid/urid.h" // IWYU pragma: keep -#include "lv2/worker/worker.h" // IWYU pragma: keep - -int -main() -{ - return 0; -} - -#if defined(__clang__) -_Pragma("clang diagnostic pop") -#elif defined(__GNUC__) -_Pragma("GCC diagnostic pop") -#endif diff --git a/test/test_forge_overflow.c b/test/test_forge_overflow.c index 95e9b87..7e97ae4 100644 --- a/test/test_forge_overflow.c +++ b/test/test_forge_overflow.c @@ -3,9 +3,9 @@ #include "atom_test_utils.c" -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/urid/urid.h" +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/urid/urid.h> #include <assert.h> #include <stdint.h> @@ -90,7 +90,7 @@ test_literal_overflow(void) static int test_sequence_overflow(void) { - static const size_t size = sizeof(LV2_Atom_Sequence) + 6 * sizeof(LV2_Atom); + static const size_t size = sizeof(LV2_Atom_Sequence) + (6 * sizeof(LV2_Atom)); LV2_URID_Map map = {NULL, urid_map}; // Test over a range that fails in the sequence header and event components @@ -121,7 +121,7 @@ test_sequence_overflow(void) static int test_vector_head_overflow(void) { - static const size_t size = sizeof(LV2_Atom_Vector) + 3 * sizeof(LV2_Atom); + static const size_t size = sizeof(LV2_Atom_Vector) + (3 * sizeof(LV2_Atom)); LV2_URID_Map map = {NULL, urid_map}; // Test over a range that fails in the vector header and elements @@ -154,7 +154,7 @@ test_vector_head_overflow(void) static int test_vector_overflow(void) { - static const size_t size = sizeof(LV2_Atom_Vector) + 3 * sizeof(LV2_Atom); + static const size_t size = sizeof(LV2_Atom_Vector) + (3 * sizeof(LV2_Atom)); static const int32_t vec[] = {1, 2, 3}; LV2_URID_Map map = {NULL, urid_map}; @@ -181,7 +181,7 @@ test_vector_overflow(void) static int test_tuple_overflow(void) { - static const size_t size = sizeof(LV2_Atom_Tuple) + 3 * sizeof(LV2_Atom); + static const size_t size = sizeof(LV2_Atom_Tuple) + (3 * sizeof(LV2_Atom)); LV2_URID_Map map = {NULL, urid_map}; // Test over a range that fails in the tuple header and elements diff --git a/util/lv2_validate.in b/util/lv2_validate.in index c26bb30..5b9d8ec 100755 --- a/util/lv2_validate.in +++ b/util/lv2_validate.in @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright 2017-2020 David Robillard <d@drobilla.net> +# Copyright 2017-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: ISC LV2DIR="@LV2DIR@" @@ -14,86 +14,61 @@ if [ "$#" -eq "0" ]; then fi sord_validate \ - "$LV2DIR/patch.lv2/manifest.ttl" \ - "$LV2DIR/patch.lv2/patch.meta.ttl" \ - "$LV2DIR/patch.lv2/patch.ttl" \ - "$LV2DIR/port-props.lv2/manifest.ttl" \ - "$LV2DIR/port-props.lv2/port-props.meta.ttl" \ - "$LV2DIR/port-props.lv2/port-props.ttl" \ - "$LV2DIR/worker.lv2/worker.meta.ttl" \ - "$LV2DIR/worker.lv2/manifest.ttl" \ - "$LV2DIR/worker.lv2/worker.ttl" \ - "$LV2DIR/buf-size.lv2/manifest.ttl" \ - "$LV2DIR/buf-size.lv2/buf-size.meta.ttl" \ + "$LV2DIR/atom.lv2/atom.ttl" \ + "$LV2DIR/atom.lv2/manifest.ttl" \ "$LV2DIR/buf-size.lv2/buf-size.ttl" \ - "$LV2DIR/midi.lv2/midi.meta.ttl" \ + "$LV2DIR/buf-size.lv2/manifest.ttl" \ + "$LV2DIR/core.lv2/lv2core.ttl" \ + "$LV2DIR/core.lv2/manifest.ttl" \ + "$LV2DIR/core.lv2/people.ttl" \ + "$LV2DIR/data-access.lv2/data-access.ttl" \ + "$LV2DIR/data-access.lv2/manifest.ttl" \ + "$LV2DIR/dynmanifest.lv2/dynmanifest.ttl" \ + "$LV2DIR/dynmanifest.lv2/manifest.ttl" \ + "$LV2DIR/event.lv2/event.ttl" \ + "$LV2DIR/event.lv2/manifest.ttl" \ + "$LV2DIR/instance-access.lv2/instance-access.ttl" \ + "$LV2DIR/instance-access.lv2/manifest.ttl" \ + "$LV2DIR/log.lv2/log.ttl" \ + "$LV2DIR/log.lv2/manifest.ttl" \ "$LV2DIR/midi.lv2/manifest.ttl" \ "$LV2DIR/midi.lv2/midi.ttl" \ - "$LV2DIR/atom.lv2/manifest.ttl" \ - "$LV2DIR/atom.lv2/atom.ttl" \ - "$LV2DIR/atom.lv2/atom.meta.ttl" \ - "$LV2DIR/dynmanifest.lv2/dynmanifest.meta.ttl" \ - "$LV2DIR/dynmanifest.lv2/manifest.ttl" \ - "$LV2DIR/dynmanifest.lv2/dynmanifest.ttl" \ + "$LV2DIR/morph.lv2/manifest.ttl" \ + "$LV2DIR/morph.lv2/morph.ttl" \ "$LV2DIR/options.lv2/manifest.ttl" \ - "$LV2DIR/options.lv2/options.meta.ttl" \ "$LV2DIR/options.lv2/options.ttl" \ "$LV2DIR/parameters.lv2/manifest.ttl" \ "$LV2DIR/parameters.lv2/parameters.ttl" \ - "$LV2DIR/parameters.lv2/parameters.meta.ttl" \ - "$LV2DIR/instance-access.lv2/instance-access.ttl" \ - "$LV2DIR/instance-access.lv2/manifest.ttl" \ - "$LV2DIR/instance-access.lv2/instance-access.meta.ttl" \ - "$LV2DIR/state.lv2/manifest.ttl" \ - "$LV2DIR/state.lv2/state.meta.ttl" \ - "$LV2DIR/state.lv2/state.ttl" \ + "$LV2DIR/patch.lv2/manifest.ttl" \ + "$LV2DIR/patch.lv2/patch.ttl" \ "$LV2DIR/port-groups.lv2/manifest.ttl" \ "$LV2DIR/port-groups.lv2/port-groups.ttl" \ - "$LV2DIR/port-groups.lv2/port-groups.meta.ttl" \ - "$LV2DIR/ui.lv2/manifest.ttl" \ - "$LV2DIR/ui.lv2/ui.ttl" \ - "$LV2DIR/ui.lv2/ui.meta.ttl" \ - "$LV2DIR/morph.lv2/manifest.ttl" \ - "$LV2DIR/morph.lv2/morph.ttl" \ - "$LV2DIR/morph.lv2/morph.meta.ttl" \ - "$LV2DIR/event.lv2/manifest.ttl" \ - "$LV2DIR/event.lv2/event.meta.ttl" \ - "$LV2DIR/event.lv2/event.ttl" \ - "$LV2DIR/resize-port.lv2/manifest.ttl" \ - "$LV2DIR/resize-port.lv2/resize-port.ttl" \ - "$LV2DIR/resize-port.lv2/resize-port.meta.ttl" \ - "$LV2DIR/log.lv2/log.ttl" \ - "$LV2DIR/log.lv2/manifest.ttl" \ - "$LV2DIR/log.lv2/log.meta.ttl" \ - "$LV2DIR/core.lv2/manifest.ttl" \ - "$LV2DIR/core.lv2/lv2core.ttl" \ - "$LV2DIR/core.lv2/lv2core.doap.ttl" \ - "$LV2DIR/core.lv2/meta.ttl" \ - "$LV2DIR/core.lv2/people.ttl" \ + "$LV2DIR/port-props.lv2/manifest.ttl" \ + "$LV2DIR/port-props.lv2/port-props.ttl" \ "$LV2DIR/presets.lv2/manifest.ttl" \ "$LV2DIR/presets.lv2/presets.ttl" \ - "$LV2DIR/presets.lv2/presets.meta.ttl" \ - "$LV2DIR/urid.lv2/manifest.ttl" \ - "$LV2DIR/urid.lv2/urid.ttl" \ - "$LV2DIR/urid.lv2/urid.meta.ttl" \ - "$LV2DIR/time.lv2/time.meta.ttl" \ - "$LV2DIR/time.lv2/manifest.ttl" \ - "$LV2DIR/time.lv2/time.ttl" \ - "$LV2DIR/data-access.lv2/manifest.ttl" \ - "$LV2DIR/data-access.lv2/data-access.meta.ttl" \ - "$LV2DIR/data-access.lv2/data-access.ttl" \ - "$LV2DIR/units.lv2/manifest.ttl" \ - "$LV2DIR/units.lv2/units.ttl" \ - "$LV2DIR/units.lv2/units.meta.ttl" \ - "$LV2DIR/schemas.lv2/xsd.ttl" \ - "$LV2DIR/schemas.lv2/manifest.ttl" \ - "$LV2DIR/schemas.lv2/rdf.ttl" \ + "$LV2DIR/resize-port.lv2/manifest.ttl" \ + "$LV2DIR/resize-port.lv2/resize-port.ttl" \ "$LV2DIR/schemas.lv2/dcterms.ttl" \ "$LV2DIR/schemas.lv2/doap.ttl" \ - "$LV2DIR/schemas.lv2/rdfs.ttl" \ "$LV2DIR/schemas.lv2/foaf.ttl" \ + "$LV2DIR/schemas.lv2/manifest.ttl" \ "$LV2DIR/schemas.lv2/owl.ttl" \ + "$LV2DIR/schemas.lv2/rdf.ttl" \ + "$LV2DIR/schemas.lv2/rdfs.ttl" \ + "$LV2DIR/schemas.lv2/xsd.ttl" \ + "$LV2DIR/state.lv2/manifest.ttl" \ + "$LV2DIR/state.lv2/state.ttl" \ + "$LV2DIR/time.lv2/manifest.ttl" \ + "$LV2DIR/time.lv2/time.ttl" \ + "$LV2DIR/ui.lv2/manifest.ttl" \ + "$LV2DIR/ui.lv2/ui.ttl" \ + "$LV2DIR/units.lv2/manifest.ttl" \ + "$LV2DIR/units.lv2/units.ttl" \ "$LV2DIR/uri-map.lv2/manifest.ttl" \ "$LV2DIR/uri-map.lv2/uri-map.ttl" \ - "$LV2DIR/uri-map.lv2/uri-map.meta.ttl" \ - $@ + "$LV2DIR/urid.lv2/manifest.ttl" \ + "$LV2DIR/urid.lv2/urid.ttl" \ + "$LV2DIR/worker.lv2/manifest.ttl" \ + "$LV2DIR/worker.lv2/worker.ttl" \ + "$@" diff --git a/util/meson.build b/util/meson.build index 58cf5e4..759a993 100644 --- a/util/meson.build +++ b/util/meson.build @@ -1,4 +1,4 @@ -# Copyright 2022 David Robillard <d@drobilla.net> +# Copyright 2022-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC config = configuration_data({'LV2DIR': lv2dir}) |